From 94f904544b4372d38270b23c9a3087313da58651 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Wed, 9 Jul 2014 17:51:10 +0000 Subject: [PATCH] Creating branches/14.1 --- egw-pear/HTTP/WebDAV/Server.php | 2921 ++++++++ egw-pear/HTTP/WebDAV/Server/Filesystem.php | 921 +++ .../HTTP/WebDAV/Tools/_parse_lockinfo.php | 251 + .../HTTP/WebDAV/Tools/_parse_propfind.php | 285 + .../HTTP/WebDAV/Tools/_parse_proppatch.php | 246 + egw-pear/setup/setup.inc.php | 44 + emailadmin/doc/dbmail.schema | 71 + emailadmin/doc/dovecot_checkpassword_ads.php | 330 + emailadmin/doc/main.cf | 754 ++ emailadmin/doc/postfix_tcp_map_ads.php | 408 ++ emailadmin/doc/qmail.new.schema | 271 + emailadmin/doc/qmailuser.schema | 103 + emailadmin/doc/smartsieve-NOTICE | 19 + emailadmin/doc/tcpmaps.conf | 16 + emailadmin/inc/class.dbmaildbmailuser.inc.php | 185 + emailadmin/inc/class.dbmailqmailuser.inc.php | 164 + emailadmin/inc/class.defaultimap.inc.php | 160 + emailadmin/inc/class.defaultsmtp.inc.php | 20 + .../inc/class.emailadmin_account.inc.php | 1477 ++++ emailadmin/inc/class.emailadmin_base.inc.php | 77 + emailadmin/inc/class.emailadmin_bo.inc.php | 242 + .../inc/class.emailadmin_credentials.inc.php | 460 ++ emailadmin/inc/class.emailadmin_hooks.inc.php | 167 + .../inc/class.emailadmin_horde_cache.inc.php | 91 + emailadmin/inc/class.emailadmin_imap.inc.php | 1188 +++ .../inc/class.emailadmin_imap_cyrus.inc.php | 190 + .../inc/class.emailadmin_imap_dovecot.inc.php | 257 + .../inc/class.emailadmin_imapbase.inc.php | 6528 +++++++++++++++++ .../class.emailadmin_notifications.inc.php | 174 + .../inc/class.emailadmin_script.inc.php | 603 ++ emailadmin/inc/class.emailadmin_sieve.inc.php | 480 ++ emailadmin/inc/class.emailadmin_smtp.inc.php | 250 + .../inc/class.emailadmin_smtp_ads.inc.php | 166 + .../inc/class.emailadmin_smtp_ldap.inc.php | 783 ++ .../class.emailadmin_smtp_mandriva.inc.php | 84 + .../inc/class.emailadmin_smtp_qmail.inc.php | 76 + .../inc/class.emailadmin_smtp_sql.inc.php | 376 + .../inc/class.emailadmin_smtp_suse.inc.php | 72 + .../inc/class.emailadmin_wizard.inc.php | 1398 ++++ .../inc/class.postfixdbmailuser.inc.php | 64 + .../inc/class.postfixinetorgperson.inc.php | 16 + emailadmin/inc/class.postfixldap.inc.php | 67 + emailadmin/index.php | 13 + emailadmin/js/app.js | 318 + emailadmin/lang/egw_bg.lang | 42 + emailadmin/lang/egw_ca.lang | 75 + emailadmin/lang/egw_cs.lang | 149 + emailadmin/lang/egw_da.lang | 72 + emailadmin/lang/egw_de.lang | 208 + emailadmin/lang/egw_el.lang | 51 + emailadmin/lang/egw_en.lang | 208 + emailadmin/lang/egw_es-es.lang | 149 + emailadmin/lang/egw_et.lang | 50 + emailadmin/lang/egw_eu.lang | 31 + emailadmin/lang/egw_fa.lang | 71 + emailadmin/lang/egw_fi.lang | 157 + emailadmin/lang/egw_fr.lang | 157 + emailadmin/lang/egw_hr.lang | 73 + emailadmin/lang/egw_hu.lang | 149 + emailadmin/lang/egw_id.lang | 93 + emailadmin/lang/egw_it.lang | 156 + emailadmin/lang/egw_iw.lang | 74 + emailadmin/lang/egw_ja.lang | 7 + emailadmin/lang/egw_ko.lang | 7 + emailadmin/lang/egw_lo.lang | 24 + emailadmin/lang/egw_lt.lang | 0 emailadmin/lang/egw_lv.lang | 68 + emailadmin/lang/egw_nl.lang | 145 + emailadmin/lang/egw_no.lang | 77 + emailadmin/lang/egw_pl.lang | 150 + emailadmin/lang/egw_pt-br.lang | 144 + emailadmin/lang/egw_pt.lang | 90 + emailadmin/lang/egw_ru.lang | 159 + emailadmin/lang/egw_rw.lang | 1 + emailadmin/lang/egw_sk.lang | 151 + emailadmin/lang/egw_sl.lang | 145 + emailadmin/lang/egw_sv.lang | 100 + emailadmin/lang/egw_tr.lang | 5 + emailadmin/lang/egw_uk.lang | 4 + emailadmin/lang/egw_vi.lang | 1 + emailadmin/lang/egw_zh-tw.lang | 127 + emailadmin/lang/egw_zh.lang | 128 + emailadmin/setup/etemplates.inc.php | 100 + emailadmin/setup/setup.inc.php | 89 + emailadmin/setup/tables_current.inc.php | 166 + emailadmin/setup/tables_update.inc.php | 870 +++ emailadmin/templates/default/account.xet | 336 + emailadmin/templates/default/app.css | 57 + emailadmin/templates/default/config.tpl | 42 + .../templates/default/images/navbar.png | Bin 0 -> 2051 bytes .../templates/default/images/progress.gif | Bin 0 -> 3951 bytes .../templates/default/wizard.folder.xet | 58 + emailadmin/templates/default/wizard.sieve.xet | 52 + emailadmin/templates/default/wizard.smtp.xet | 54 + emailadmin/templates/default/wizard.xet | 58 + .../templates/jerryr/images/navbar-over.png | Bin 0 -> 2051 bytes emailadmin/templates/jerryr/images/navbar.png | Bin 0 -> 1110 bytes emailadmin/test.php | 167 + 98 files changed, 28333 insertions(+) create mode 100644 egw-pear/HTTP/WebDAV/Server.php create mode 100644 egw-pear/HTTP/WebDAV/Server/Filesystem.php create mode 100644 egw-pear/HTTP/WebDAV/Tools/_parse_lockinfo.php create mode 100644 egw-pear/HTTP/WebDAV/Tools/_parse_propfind.php create mode 100644 egw-pear/HTTP/WebDAV/Tools/_parse_proppatch.php create mode 100644 egw-pear/setup/setup.inc.php create mode 100644 emailadmin/doc/dbmail.schema create mode 100755 emailadmin/doc/dovecot_checkpassword_ads.php create mode 100644 emailadmin/doc/main.cf create mode 100755 emailadmin/doc/postfix_tcp_map_ads.php create mode 100644 emailadmin/doc/qmail.new.schema create mode 100644 emailadmin/doc/qmailuser.schema create mode 100644 emailadmin/doc/smartsieve-NOTICE create mode 100644 emailadmin/doc/tcpmaps.conf create mode 100755 emailadmin/inc/class.dbmaildbmailuser.inc.php create mode 100644 emailadmin/inc/class.dbmailqmailuser.inc.php create mode 100644 emailadmin/inc/class.defaultimap.inc.php create mode 100644 emailadmin/inc/class.defaultsmtp.inc.php create mode 100644 emailadmin/inc/class.emailadmin_account.inc.php create mode 100644 emailadmin/inc/class.emailadmin_base.inc.php create mode 100644 emailadmin/inc/class.emailadmin_bo.inc.php create mode 100644 emailadmin/inc/class.emailadmin_credentials.inc.php create mode 100644 emailadmin/inc/class.emailadmin_hooks.inc.php create mode 100644 emailadmin/inc/class.emailadmin_horde_cache.inc.php create mode 100644 emailadmin/inc/class.emailadmin_imap.inc.php create mode 100644 emailadmin/inc/class.emailadmin_imap_cyrus.inc.php create mode 100644 emailadmin/inc/class.emailadmin_imap_dovecot.inc.php create mode 100644 emailadmin/inc/class.emailadmin_imapbase.inc.php create mode 100644 emailadmin/inc/class.emailadmin_notifications.inc.php create mode 100644 emailadmin/inc/class.emailadmin_script.inc.php create mode 100644 emailadmin/inc/class.emailadmin_sieve.inc.php create mode 100644 emailadmin/inc/class.emailadmin_smtp.inc.php create mode 100644 emailadmin/inc/class.emailadmin_smtp_ads.inc.php create mode 100644 emailadmin/inc/class.emailadmin_smtp_ldap.inc.php create mode 100644 emailadmin/inc/class.emailadmin_smtp_mandriva.inc.php create mode 100644 emailadmin/inc/class.emailadmin_smtp_qmail.inc.php create mode 100644 emailadmin/inc/class.emailadmin_smtp_sql.inc.php create mode 100644 emailadmin/inc/class.emailadmin_smtp_suse.inc.php create mode 100644 emailadmin/inc/class.emailadmin_wizard.inc.php create mode 100755 emailadmin/inc/class.postfixdbmailuser.inc.php create mode 100644 emailadmin/inc/class.postfixinetorgperson.inc.php create mode 100644 emailadmin/inc/class.postfixldap.inc.php create mode 100644 emailadmin/index.php create mode 100644 emailadmin/js/app.js create mode 100644 emailadmin/lang/egw_bg.lang create mode 100644 emailadmin/lang/egw_ca.lang create mode 100644 emailadmin/lang/egw_cs.lang create mode 100644 emailadmin/lang/egw_da.lang create mode 100644 emailadmin/lang/egw_de.lang create mode 100644 emailadmin/lang/egw_el.lang create mode 100755 emailadmin/lang/egw_en.lang create mode 100644 emailadmin/lang/egw_es-es.lang create mode 100755 emailadmin/lang/egw_et.lang create mode 100644 emailadmin/lang/egw_eu.lang create mode 100644 emailadmin/lang/egw_fa.lang create mode 100644 emailadmin/lang/egw_fi.lang create mode 100644 emailadmin/lang/egw_fr.lang create mode 100755 emailadmin/lang/egw_hr.lang create mode 100644 emailadmin/lang/egw_hu.lang create mode 100644 emailadmin/lang/egw_id.lang create mode 100644 emailadmin/lang/egw_it.lang create mode 100755 emailadmin/lang/egw_iw.lang create mode 100644 emailadmin/lang/egw_ja.lang create mode 100644 emailadmin/lang/egw_ko.lang create mode 100644 emailadmin/lang/egw_lo.lang create mode 100644 emailadmin/lang/egw_lt.lang create mode 100644 emailadmin/lang/egw_lv.lang create mode 100644 emailadmin/lang/egw_nl.lang create mode 100644 emailadmin/lang/egw_no.lang create mode 100644 emailadmin/lang/egw_pl.lang create mode 100644 emailadmin/lang/egw_pt-br.lang create mode 100644 emailadmin/lang/egw_pt.lang create mode 100644 emailadmin/lang/egw_ru.lang create mode 100644 emailadmin/lang/egw_rw.lang create mode 100644 emailadmin/lang/egw_sk.lang create mode 100644 emailadmin/lang/egw_sl.lang create mode 100644 emailadmin/lang/egw_sv.lang create mode 100644 emailadmin/lang/egw_tr.lang create mode 100644 emailadmin/lang/egw_uk.lang create mode 100644 emailadmin/lang/egw_vi.lang create mode 100644 emailadmin/lang/egw_zh-tw.lang create mode 100644 emailadmin/lang/egw_zh.lang create mode 100644 emailadmin/setup/etemplates.inc.php create mode 100644 emailadmin/setup/setup.inc.php create mode 100644 emailadmin/setup/tables_current.inc.php create mode 100644 emailadmin/setup/tables_update.inc.php create mode 100644 emailadmin/templates/default/account.xet create mode 100644 emailadmin/templates/default/app.css create mode 100644 emailadmin/templates/default/config.tpl create mode 100644 emailadmin/templates/default/images/navbar.png create mode 100644 emailadmin/templates/default/images/progress.gif create mode 100644 emailadmin/templates/default/wizard.folder.xet create mode 100644 emailadmin/templates/default/wizard.sieve.xet create mode 100644 emailadmin/templates/default/wizard.smtp.xet create mode 100644 emailadmin/templates/default/wizard.xet create mode 100644 emailadmin/templates/jerryr/images/navbar-over.png create mode 100644 emailadmin/templates/jerryr/images/navbar.png create mode 100644 emailadmin/test.php diff --git a/egw-pear/HTTP/WebDAV/Server.php b/egw-pear/HTTP/WebDAV/Server.php new file mode 100644 index 0000000000..a9f6eefe04 --- /dev/null +++ b/egw-pear/HTTP/WebDAV/Server.php @@ -0,0 +1,2921 @@ + + * @version @package_version@ + */ +class HTTP_WebDAV_Server +{ + // {{{ Member Variables + + /** + * complete URI for this request + * + * @var string + */ + var $uri; + + /** + * base URI for this request + * + * @var string + */ + var $base_uri; + + /** + * Set if client requires to be a url (true) or a path (false). + * RFC 4918 allows both: http://www.webdav.org/specs/rfc4918.html#ELEMENT_href + * But some clients can NOT deal with one or the other! + * + * @var boolean + */ + var $client_require_href_as_url; + + /** + * Set if client requires or does not allow namespace redundacy. + * The XML Namespace specification does allow both + * But some clients can NOT deal with one or the other! + * + * $this->crrnd === false: + * + * + * /egroupware/webdav.php/home/ralf/ + * + * + * + * + * HTTP/1.1 200 OK + * + * + * + * + * $this->crrnd === true: + * + * + * /egroupware/webdav.php/home/ralf/ + * + * + * + * + * HTTP/1.1 200 OK + * + * + * + * + * @var boolean (client_refuses_redundand_namespace_declarations) + */ + var $crrnd = false; + + /** + + + /** + * URI path for this request + * + * @var string + */ + var $path; + + /** + * Realm string to be used in authentification popups + * + * @var string + */ + var $http_auth_realm = "PHP WebDAV"; + + /** + * String to be used in "X-Dav-Powered-By" header + * + * @var string + */ + var $dav_powered_by = ""; + + /** + * Remember parsed If: (RFC2518/9.4) header conditions + * + * @var array + */ + var $_if_header_uris = array(); + + /** + * HTTP response status/message + * + * @var string + */ + var $_http_status = "200 OK"; + + /** + * encoding of property values passed in + * + * @var string + */ + var $_prop_encoding = "utf-8"; + + /** + * Copy of $_SERVER superglobal array + * + * Derived classes may extend the constructor to + * modify its contents + * + * @var array + */ + var $_SERVER; + + // }}} + + // {{{ Constructor + + /** + * Constructor + * + * @param void + */ + function HTTP_WebDAV_Server() + { + // PHP messages destroy XML output -> switch them off + ini_set("display_errors", 0); + + // copy $_SERVER variables to local _SERVER array + // so that derived classes can simply modify these + $this->_SERVER = $_SERVER; + } + + // }}} + + // {{{ ServeRequest() + /** + * Serve WebDAV HTTP request + * + * dispatch WebDAV HTTP request to the apropriate method handler + * + * @param $prefix=null prefix filesystem path with given path, eg. "/webdav" for owncloud 4.5 remote.php + * @return void + */ + function ServeRequest($prefix=null) + { + // prevent warning in litmus check 'delete_fragment' + if (strstr($this->_SERVER["REQUEST_URI"], '#')) { + $this->http_status("400 Bad Request"); + return; + } + + // default is currently to use just the path, extending class can set $this->client_require_href_as_url depending on user-agent + if ($this->client_require_href_as_url) + { + // default uri is the complete request uri + $uri = (@$this->_SERVER["HTTPS"] === "on" ? "https:" : "http:") . '//'.$this->_SERVER['HTTP_HOST']; + } + $uri .= $this->_SERVER["SCRIPT_NAME"]; + + // WebDAV has no concept of a query string and clients (including cadaver) + // seem to pass '?' unencoded, so we need to extract the path info out + // of the request URI ourselves + // if request URI contains a full url, remove schema and domain + if (preg_match('|^https?://[^/]+(/.*)$|', $path_info=$this->_SERVER["REQUEST_URI"], $matches)) + { + $path_info = $matches[1]; + } + $path_info = substr($path_info, strlen($this->_SERVER["SCRIPT_NAME"])); + + // just in case the path came in empty ... + if (empty($path_info)) { + $path_info = "/"; + } + + $path_info = $this->_urldecode($path_info); + + if ($prefix && strpos($path_info, $prefix) === 0) + { + $uri .= $prefix; + list(,$path_info) = explode($prefix, $path_info, 2); + } + + $this->base_uri = $uri; + $this->uri = $uri . $path_info; + + // set path + // $_SERVER['PATH_INFO'] is already urldecoded + //$this->path = $this->_urldecode($path_info); + // quote '#' (e.g. OpenOffice uses this for lock-files) + $this->path = strtr($path_info,array( + '%' => '%25', + '#' => '%23', + '?' => '%3F', + )); + if (!strlen($this->path)) { + if ($this->_SERVER["REQUEST_METHOD"] == "GET") { + // redirect clients that try to GET a collection + // WebDAV clients should never try this while + // regular HTTP clients might ... + header("Location: ".$this->base_uri."/"); + return; + } else { + // if a WebDAV client didn't give a path we just assume '/' + $this->path = "/"; + } + } + + if (ini_get("magic_quotes_gpc")) { + $this->path = stripslashes($this->path); + } + + + // identify ourselves + if (empty($this->dav_powered_by)) { + header("X-Dav-Powered-By: PHP class: ".get_class($this)); + } else { + header("X-Dav-Powered-By: ".$this->dav_powered_by); + } + + // check authentication + // for the motivation for not checking OPTIONS requests on / see + // http://pear.php.net/bugs/bug.php?id=5363 + if ( ( !(($this->_SERVER['REQUEST_METHOD'] == 'OPTIONS') && ($this->path == "/"))) + && (!$this->_check_auth())) { + // RFC2518 says we must use Digest instead of Basic + // but Microsoft Clients do not support Digest + // and we don't support NTLM and Kerberos + // so we are stuck with Basic here + header('WWW-Authenticate: Basic realm="'.($this->http_auth_realm).'"'); + + // Windows seems to require this being the last header sent + // (changed according to PECL bug #3138) + $this->http_status('401 Unauthorized'); + + return; + } + + // check + if (! $this->_check_if_header_conditions()) { + return; + } + + // detect requested method names + $method = strtolower($this->_SERVER["REQUEST_METHOD"]); + $wrapper = "http_".$method; + + // activate HEAD emulation by GET if no HEAD method found + if ($method == "head" && !method_exists($this, "head")) { + $method = "get"; + } + + if (method_exists($this, $wrapper) && ($method == "options" || method_exists($this, $method))) { + $this->$wrapper(); // call method by name + } else { // method not found/implemented + if ($this->_SERVER["REQUEST_METHOD"] == "LOCK") { + $error = '412 Precondition failed'; + ; + } else { + $error = '405 Method not allowed'; + header("Allow: ".join(", ", $this->_allow())); // tell client what's allowed + } + $this->http_status($error); + echo "Error $error\n"; + echo "

$error

\n"; + echo "The requested could not by handled by this server.\n"; + echo '(URI ' . $this->_SERVER['REQUEST_URI'] . ")
\n
\n"; + echo "\n"; + } + } + + // }}} + + // {{{ abstract WebDAV methods + + // {{{ GET() + /** + * GET implementation + * + * overload this method to retrieve resources from your server + *
+ * + * + * @abstract + * @param array &$params Array of input and output parameters + *
input + *
output + * @returns int HTTP-Statuscode + */ + + /* abstract + function GET(&$params) + { + // dummy entry for PHPDoc + } + */ + + // }}} + + // {{{ PUT() + /** + * PUT implementation + * + * PUT implementation + * + * @abstract + * @param array &$params + * @returns int HTTP-Statuscode + */ + + /* abstract + function PUT() + { + // dummy entry for PHPDoc + } + */ + + // }}} + + // {{{ COPY() + + /** + * COPY implementation + * + * COPY implementation + * + * @abstract + * @param array &$params + * @returns int HTTP-Statuscode + */ + + /* abstract + function COPY() + { + // dummy entry for PHPDoc + } + */ + + // }}} + + // {{{ MOVE() + + /** + * MOVE implementation + * + * MOVE implementation + * + * @abstract + * @param array &$params + * @returns int HTTP-Statuscode + */ + + /* abstract + function MOVE() + { + // dummy entry for PHPDoc + } + */ + + // }}} + + // {{{ DELETE() + + /** + * DELETE implementation + * + * DELETE implementation + * + * @abstract + * @param array &$params + * @returns int HTTP-Statuscode + */ + + /* abstract + function DELETE() + { + // dummy entry for PHPDoc + } + */ + // }}} + + // {{{ PROPFIND() + + /** + * PROPFIND implementation + * + * PROPFIND implementation + * + * @abstract + * @param array &$params + * @returns int HTTP-Statuscode + */ + + /* abstract + function PROPFIND() + { + // dummy entry for PHPDoc + } + */ + + // }}} + + // {{{ PROPPATCH() + + /** + * PROPPATCH implementation + * + * PROPPATCH implementation + * + * @abstract + * @param array &$params + * @returns int HTTP-Statuscode + */ + + /* abstract + function PROPPATCH() + { + // dummy entry for PHPDoc + } + */ + // }}} + + // {{{ LOCK() + + /** + * LOCK implementation + * + * LOCK implementation + * + * @abstract + * @param array &$params + * @returns int HTTP-Statuscode + */ + + /* abstract + function LOCK() + { + // dummy entry for PHPDoc + } + */ + // }}} + + // {{{ UNLOCK() + + /** + * UNLOCK implementation + * + * UNLOCK implementation + * + * @abstract + * @param array &$params + * @returns int HTTP-Statuscode + */ + + /* abstract + function UNLOCK() + { + // dummy entry for PHPDoc + } + */ + // }}} + + // {{{ ACL() + + /** + * ACL implementation + * + * ACL implementation + * + * @abstract + * @param array &$params + * @returns int HTTP-Statuscode + */ + + /* abstract + function ACL() + { + // dummy entry for PHPDoc + } + */ + // }}} + + // }}} + + // {{{ other abstract methods + + // {{{ check_auth() + + /** + * check authentication + * + * overload this method to retrieve and confirm authentication information + * + * @abstract + * @param string type Authentication type, e.g. "basic" or "digest" + * @param string username Transmitted username + * @param string passwort Transmitted password + * @returns bool Authentication status + */ + + /* abstract + function checkAuth($type, $username, $password) + { + // dummy entry for PHPDoc + } + */ + + // }}} + + // {{{ checklock() + + /** + * check lock status for a resource + * + * overload this method to return shared and exclusive locks + * active for this resource + * + * @abstract + * @param string resource Resource path to check + * @returns array An array of lock entries each consisting + * of 'type' ('shared'/'exclusive'), 'token' and 'timeout' + */ + + /* abstract + function checklock($resource) + { + // dummy entry for PHPDoc + } + */ + + // }}} + + // }}} + + // {{{ WebDAV HTTP method wrappers + + // {{{ http_OPTIONS() + + /** + * OPTIONS method handler + * + * The OPTIONS method handler creates a valid OPTIONS reply + * including Dav: and Allowed: headers + * based on the implemented methods found in the actual instance + * + * @param void + * @return void + */ + function http_OPTIONS() + { + // Microsoft clients default to the Frontpage protocol + // unless we tell them to use WebDAV + header("MS-Author-Via: DAV"); + + // get allowed methods + $allow = $this->_allow(); + + // dav header + $dav = array(1); // assume we are always dav class 1 compliant + if (isset($allow['LOCK'])) { + $dav[] = 2; // dav class 2 requires that locking is supported + } + + // allow extending class to modify DAV and Allow headers + if (method_exists($this,'OPTIONS')) { + $this->OPTIONS($this->path,$dav,$allow); + } + + // tell clients what we found + $this->http_status("200 OK"); + header("DAV: " .join(", ", $dav)); + header("Allow: ".join(", ", $allow)); + + header("Content-length: 0"); + } + + // }}} + + + // {{{ http_PROPFIND() + + /** + * Should the whole PROPFIND request (xml) be stored + * + * @var boolean + */ + var $store_request = false; + /** + * Content of (last) PROPFIND request + * + * @var string + */ + var $request; + + /** + * PROPFIND method handler + * + * @param string $handler='PROPFIND' allows to use method eg. for CalDAV REPORT + * @return void + */ + function http_PROPFIND($handler='PROPFIND') + { + $options = Array(); + $files = Array(); + + $options["path"] = $this->path; + + // search depth from header (default is "infinity) + if (isset($this->_SERVER['HTTP_DEPTH'])) { + $options["depth"] = $this->_SERVER["HTTP_DEPTH"]; + } else { + $options["depth"] = "infinity"; + } + + // analyze request payload + $propinfo = new _parse_propfind("php://input", $this->store_request); + if ($this->store_request) $this->request = $propinfo->request; + if (!$propinfo->success) { + $this->http_status("400 Error"); + return; + } + $options['root'] = $propinfo->root; + $options['props'] = $propinfo->props; + if ($propinfo->filters) + $options['filters'] = $propinfo->filters; + if ($propinfo->other) + $options['other'] = $propinfo->other; + + // call user handler + if (!($retval =$this->$handler($options, $files))) { + $files = array("files" => array()); + if (method_exists($this, "checkLock")) { + // is locked? + $lock = $this->checkLock($this->path); + + if (is_array($lock) && count($lock)) { + $created = isset($lock['created']) ? $lock['created'] : time(); + $modified = isset($lock['modified']) ? $lock['modified'] : time(); + $files['files'][] = array("path" => $this->_slashify($this->path), + "props" => array($this->mkprop("displayname", $this->path), + $this->mkprop("creationdate", $created), + $this->mkprop("getlastmodified", $modified), + $this->mkprop("resourcetype", ""), + $this->mkprop("getcontenttype", ""), + $this->mkprop("getcontentlength", 0)) + ); + } + } + + if (empty($files['files'])) { + $this->http_status("404 Not Found"); + return; + } + } + + // now we generate the reply header ... + if ($retval === true) + { + $this->http_status('207 Multi-Status'); + } + elseif (is_string($retval)) + { + $this->http_status($retval); + header('Content-Type: text/html'); + echo "Error $retval\n"; + echo "

$retval

\n"; + switch (substr($retval, 0 ,3)) + { + case '501': // Not Implemented + echo "The requested feature is not (yet) supported by this server.\n"; + break; + default: + echo "The request could not be handled by this server.\n"; + } + echo '(URI ' . $this->_SERVER['REQUEST_URI'] . ")
\n
\n"; + echo "\n"; + return; + } + // dav header + $dav = array(1); // assume we are always dav class 1 compliant + $allow = false; + + // allow extending class to modify DAV + if (method_exists($this,'OPTIONS')) { + $this->OPTIONS($this->path,$dav,$allow); + } + header("DAV: " .join(", ", $dav)); + header('Content-Type: text/xml; charset="utf-8"'); + + // add Vary and Preference-Applied header for Prefer: return=minimal + if (isset($this->_SERVER['HTTP_PREFER']) && in_array('return=minimal', preg_split('/, ?/', $this->_SERVER['HTTP_PREFER']))) + { + header("Preference-Applied: return=minimal"); + header("Vary: Prefer"); + } + + // ... and payload + echo "\n"; + echo $this->crrnd ? "\n" : "\n"; + + $this->multistatus_responses($files['files'], $options['props']); + + // WebDAV sync report sync-token, can be either the sync-token or a callback (called with params in $files['sync-token-params']) + if (isset($files['sync-token'])) + { + echo ($this->crrnd ? " <" : " ". + htmlspecialchars(!is_callable($files['sync-token']) ? $files['sync-token'] : + call_user_func_array($files['sync-token'], (array)$files['sync-token-params'])). + ($this->crrnd ? "\n"; + } + + echo 'crrnd?'':'D:')."multistatus>\n"; + } + + /** + * Render (echo) XML for given multistatus responses + * + * @param array|Iterator $files + * @param array|string $props + */ + function multistatus_responses(&$files, $props, $initial_ns_hash=null, $initial_ns_defs=null) + { + if (!isset($initial_ns_hash)) $initial_ns_hash = array('DAV:' => 'D'); + if (!isset($initial_ns_defs)) $initial_ns_defs = 'xmlns:ns0="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/"'; + + // using an ArrayIterator to prevent foreach from copying the array, + // as we cant loop by reference, when an iterator is given in $files + if (is_array($files)) + { + $files = new ArrayIterator($files); + } + // support for "Prefer: depth-noroot" header on PROPFIND + $skip_root = $this->_SERVER['REQUEST_METHOD'] == 'PROPFIND' && + !isset($initial_ns_hash) && // multistatus_response calls itself, do NOT apply skip in that case + isset($this->_SERVER['HTTP_PREFER']) && in_array('depth-noroot', preg_split('/, ?/', $this->_SERVER['HTTP_PREFER'])); + + // now we loop over all returned file entries + foreach ($files as $file) { + + // skip first element (root), if requested by Prefer: depth-noroot + if ($skip_root) { + $skip_root = false; + continue; + } + + // collect namespaces here + $ns_hash = $initial_ns_hash; + + // Microsoft Clients need this special namespace for date and time values + $ns_defs = $initial_ns_defs; + + // nothing to do if no properties were returend for a file + if (isset($file["props"]) && is_array($file["props"])) { + + // now loop over all returned properties + foreach ($file["props"] as &$prop) { + // as a convenience feature we do not require that user handlers + // restrict returned properties to the requested ones + // here we strip all unrequested entries out of the response + + // this can happen if we have allprop and prop in one propfind: + // , eg. blah is not automatic returned by allprop + switch(is_array($props) ? $props[0] : $props) { + case "all": + // nothing to remove + break; + + case "names": + // only the names of all existing properties were requested + // so we remove all values + unset($prop["val"]); + break; + + default: + $found = false; + + // search property name in requested properties + foreach ((array)$props as $reqprop) { + if ( $reqprop["name"] == $prop["name"] + && @$reqprop["xmlns"] == $prop["ns"]) { + $found = true; + break; + } + } + + // unset property and continue with next one if not found/requested + if (!$found) { + $prop=""; + continue(2); + } + break; + } + + // namespace handling + if (empty($prop["ns"])) continue; // no namespace + $ns = $prop["ns"]; + //if ($ns == "DAV:") continue; // default namespace + if (isset($ns_hash[$ns])) continue; // already known + + // register namespace + $ns_name = "ns".(count($ns_hash) + 1); + $ns_hash[$ns] = $ns_name; + $ns_defs .= " xmlns:$ns_name=\"$ns\""; + } + + // we also need to add empty entries for properties that were requested + // but for which no values where returned by the user handler + if (is_array($props)) { + foreach ($props as $reqprop) { + if (!is_array($reqprop) || $reqprop['name']=="") continue; // skip empty entries, or 'all' if used together with + + $found = false; + + // check if property exists in result + foreach ($file["props"] as &$prop) { + if (is_array($prop) && $reqprop["name"] == $prop["name"] + && @$reqprop["xmlns"] == $prop["ns"]) { + $found = true; + break; + } + } + + if (!$found) { + if ($reqprop["xmlns"]==="DAV:" && $reqprop["name"]==="lockdiscovery") { + // lockdiscovery is handled by the base class + $file["props"][] + = $this->mkprop("DAV:", + "lockdiscovery", + $this->lockdiscovery($file['path'])); + // only collect $file['noprops'] if we have NO Brief: t and NO Prefer: return=minimal HTTP Header + } elseif ((!isset($this->_SERVER['HTTP_BRIEF']) || $this->_SERVER['HTTP_BRIEF'] != 't') && + (!isset($this->_SERVER['HTTP_PREFER']) || !in_array('return=minimal', preg_split('/, ?/', $this->_SERVER['HTTP_PREFER'])))) { + // add empty value for this property + $file["noprops"][] = + $this->mkprop($reqprop["xmlns"], $reqprop["name"], ""); + + // register property namespace if not known yet + if ($reqprop["xmlns"] != "DAV:" && !isset($ns_hash[$reqprop["xmlns"]])) { + $ns_name = "ns".(count($ns_hash) + 1); + $ns_hash[$reqprop["xmlns"]] = $ns_name; + $ns_defs .= " xmlns:$ns_name=\"$reqprop[xmlns]\""; + } + } + } + } + } + } + // ignore empty or incomplete entries + if (!is_array($file) || empty($file) || !isset($file["path"])) continue; + $path = $file['path']; + if (!is_string($path) || $path==="") continue; + + if ($this->crrnd) + { + echo " \n"; + } + else + { + echo " \n"; + } + + /* TODO right now the user implementation has to make sure + collections end in a slash, this should be done in here + by checking the resource attribute */ + $href = $this->_mergePaths($this->base_uri, $path); + + /* minimal urlencoding is needed for the resource path */ + $href = $this->_urlencode($href); + + if ($this->crrnd) + { + echo " $href\n"; + } + else + { + echo " $href\n"; + } + + // report all found properties and their values (if any) + if (isset($file["props"]) && is_array($file["props"])) { + echo ' <'.($this->crrnd?'':'D:')."propstat>\n"; + echo ' <'.($this->crrnd?'':'D:')."prop>\n"; + + foreach ($file["props"] as &$prop) { + + if (!is_array($prop)) continue; + if (!isset($prop["name"])) continue; + + if (!isset($prop["val"]) || $prop["val"] === "" || $prop["val"] === false) { + // empty properties (cannot use empty() for check as "0" is a legal value here) + if ($prop["ns"]=="DAV:") { + echo ' <'.($this->crrnd?'':'D:')."$prop[name]/>\n"; + } else if (!empty($prop["ns"])) { + echo " <".$ns_hash[$prop["ns"]].":$prop[name]/>\n"; + } else { + echo " <$prop[name] xmlns=\"\"/>"; + } + } + // multiple level of responses required for expand-property reports + elseif(isset($prop['props']) && is_array($prop['val'])) + { + if ($prop['ns'] && !isset($ns_hash[$prop['ns']])) { + $ns_name = "ns".(count($ns_hash) + 1); + $ns_hash[$prop['ns']] = $ns_name; + } + echo ' <'.$ns_hash[$prop['ns']].":$prop[name]>\n"; + $this->multistatus_responses($prop['val'], $prop['props'], $ns_hash, ''); + echo ' \n"; + } else if ($prop["ns"] == "DAV:") { + // some WebDAV properties need special treatment + switch ($prop["name"]) { + case "creationdate": + echo ' <'.($this->crrnd?'':'D:')."creationdate ns0:dt=\"dateTime.tz\">" + . gmdate("Y-m-d\\TH:i:s\\Z", $prop['val']) + . 'crrnd?'':'D:')."creationdate>\n"; + break; + case "getlastmodified": + echo ' <'.($this->crrnd?'':'D:')."getlastmodified ns0:dt=\"dateTime.rfc1123\">" + . gmdate("D, d M Y H:i:s ", $prop['val']) + . "GMTcrrnd?'':'D:')."getlastmodified>\n"; + break; + case "supportedlock": + echo ' <'.($this->crrnd?'':'D:')."supportedlock>$prop[val]crrnd?'':'D:')."supportedlock>\n"; + break; + case "lockdiscovery": + echo ' <'.($this->crrnd?'':'D:')."lockdiscovery>\n"; + echo $prop["val"]; + echo ' crrnd?'':'D:')."lockdiscovery>\n"; + break; + // the following are non-standard Microsoft extensions to the DAV namespace + case "lastaccessed": + echo ' <'.($this->crrnd?'':'D:')."lastaccessed ns0:dt=\"dateTime.rfc1123\">" + . gmdate("D, d M Y H:i:s ", $prop['val']) + . 'GMTcrrnd?'':'D:')."lastaccessed>\n"; + break; + case "ishidden": + echo ' <'.($this->crrnd?'':'D:')."ishidden>" + . is_string($prop['val']) ? $prop['val'] : ($prop['val'] ? 'true' : 'false') + . 'crrnd?'':'D:')."\n"; + break; + default: + $ns_defs = ''; + if (is_array($prop['val'])) + { + $hns_hash = $ns_hash; + $val = $this->_hierarchical_prop_encode($prop['val'], 'DAV:', $ns_defs, $hns_hash); + } elseif (isset($prop['raw'])) { + $val = $this->_prop_encode(''); + } else { + $val = $this->_prop_encode(htmlspecialchars($prop['val'], ENT_NOQUOTES, 'utf-8')); + } + echo ' <'.($this->crrnd?'':'D:')."$prop[name]$ns_defs>$val". + 'crrnd?'':'D:')."$prop[name]>\n"; + break; + } + } else { + // allow multiple values and attributes, required eg. for caldav:supported-calendar-component-set + if ($prop['ns'] && is_array($prop['val'])) { + if (!isset($ns_hash[$prop['ns']])) { + $ns_name = "ns".(count($ns_hash) + 1); + $ns_hash[$prop['ns']] = $ns_name; + } + $vals = $extra_ns = ''; + foreach($prop['val'] as $subprop) + { + if ($subprop['ns'] && $subprop['ns'] != 'DAV:') { + // register property namespace if not known yet + if (!isset($ns_hash[$subprop['ns']])) { + $ns_name = "ns".(count($ns_hash) + 1); + $ns_hash[$subprop['ns']] = $ns_name; + } else { + $ns_name = $ns_hash[$subprop['ns']]; + } + if (strchr($extra_ns,$extra=' xmlns:'.$ns_name.'="'.$subprop['ns'].'"') === false) { + $extra_ns .= $extra; + } + $ns_name .= ':'; + } elseif ($subprop['ns'] == 'DAV:') { + $ns_name = 'D:'; + } else { + $ns_name = ''; + } + $vals .= "<$ns_name$subprop[name]"; + if (is_array($subprop['val'])) + { + if (isset($subprop['val'][0])) + { + $vals .= '>'; + $vals .= $this->_hierarchical_prop_encode($subprop['val'], $subprop['ns'], $ns_defs, $ns_hash); + $vals .= ""; + } + else // val contains only attributes, no value + { + foreach($subprop['val'] as $attr => $val) + { + $vals .= ' '.$attr.'="'.htmlspecialchars($val, ENT_NOQUOTES, 'utf-8').'"'; + } + $vals .= '/>'; + } + } + else + { + $vals .= '>'; + if (isset($subprop['raw'])) { + $vals .= ''; + } else { + if($subprop['name'] == 'href') $subprop['val'] = $this->_urlencode($subprop['val']); + $vals .= htmlspecialchars($subprop['val'], ENT_NOQUOTES, 'utf-8'); + } + $vals .= ""; + } + } + echo ' <'.$ns_hash[$prop['ns']].":$prop[name]$extra_ns>$vals\n"; + } else { + if ($prop['raw']) + { + $val = ''; + } else { + $val = htmlspecialchars($prop['val'], ENT_NOQUOTES, 'utf-8'); + } + $val = $this->_prop_encode($val); + // properties from namespaces != "DAV:" or without any namespace + if ($prop['ns']) { + if ($this->crrnd) { + echo " <$prop[name] xmlns=".'"'.$prop["ns"].'">' + . $val . "\n"; + } else { + echo " <" . $ns_hash[$prop["ns"]] . ":$prop[name]>" + . $val . '\n"; + } + } else { + echo " <$prop[name] xmlns=\"\">$val\n"; + } + } + } + } + + if ($this->crrnd) + { + echo " \n"; + echo " HTTP/1.1 200 OK\n"; + echo " \n"; + } + else + { + echo " \n"; + echo " HTTP/1.1 200 OK\n"; + echo " \n"; + } + } + + // now report all properties requested but not found + if (isset($file["noprops"])) { + echo ' <'.($this->crrnd?'':'D:')."propstat>\n"; + echo ' <'.($this->crrnd?'':'D:')."prop>\n"; + + foreach ($file["noprops"] as &$prop) { + if ($prop["ns"] == "DAV:") { + echo ' <'.($this->crrnd?'':'D:')."$prop[name]/>\n"; + } else if ($prop["ns"] == "") { + echo " <$prop[name] xmlns=\"\"/>\n"; + } else { + echo " <" . $ns_hash[$prop["ns"]] . ":$prop[name]/>\n"; + } + } + + if ($this->crrnd) + { + echo " \n"; + echo " HTTP/1.1 404 Not Found\n"; + echo " \n"; + } + else + { + echo " \n"; + echo " HTTP/1.1 404 Not Found\n"; + echo " \n"; + } + } + + // 404 Not Found status element for WebDAV sync report + if (!isset($file['props']) && !isset($file['noprops'])) + { + if ($this->crrnd) + { + echo " HTTP/1.1 404 Not Found\n"; + } + else + { + echo " HTTP/1.1 404 Not Found\n"; + } + } + + echo ' crrnd?'':'D:')."response>\n"; + } + } + + + // }}} + + // {{{ http_PROPPATCH() + + /** + * PROPPATCH method handler + * + * @param void + * @return void + */ + function http_PROPPATCH() + { + if ($this->_check_lock_status($this->path)) { + $options = Array(); + + $options["path"] = $this->path; + + $propinfo = new _parse_proppatch("php://input", $this->store_request); + if ($this->store_request) $this->request = $propinfo->request; + + if (!$propinfo->success) { + $this->http_status("400 Error"); + return; + } + + $options['props'] = $propinfo->props; + + $responsedescr = $this->PROPPATCH($options); + + $this->http_status("207 Multi-Status"); + header('Content-Type: text/xml; charset="utf-8"'); + + echo "\n"; + + echo "\n"; + echo ' <'.($this->crrnd?'':'D:')."response>\n"; + echo ' <'.($this->crrnd?'':'D:')."href>".$this->_urlencode($this->_mergePaths($this->_SERVER["SCRIPT_NAME"], $this->path)).'crrnd?'':'D:')."href>\n"; + + foreach ($options["props"] as $prop) { + echo ' <'.($this->crrnd?'':'D:')."propstat>\n"; + echo ' <'.($this->crrnd?'':'D:')."prop><$prop[name] xmlns=\"$prop[ns]\"/>crrnd?'':'D:')."prop>\n"; + echo ' <'.($this->crrnd?'':'D:')."status>HTTP/1.1 $prop[status]crrnd?'':'D:')."status>\n"; + echo ' crrnd?'':'D:')."propstat>\n"; + } + + if ($responsedescr) { + echo ' <'.($this->crrnd?'':'D:')."responsedescription>". + $this->_prop_encode(htmlspecialchars($responsedescr, ENT_NOQUOTES, 'utf-8')). + 'crrnd?'':'D:')."responsedescription>\n"; + } + + echo ' crrnd?'':'D:')."response>\n"; + echo 'crrnd?'':'D:')."multistatus>\n"; + } else { + $this->http_status("423 Locked"); + } + } + + // }}} + + + // {{{ http_MKCOL() + + /** + * MKCOL method handler + * + * @param void + * @return void + */ + function http_MKCOL() + { + $options = Array(); + + $options["path"] = $this->path; + + $stat = $this->MKCOL($options); + + $this->http_status($stat); + } + + // }}} + + /** + * Check or set if we want ot use compression as transfer encoding + * + * If we use compression via zlib.output_compression as transfer encoding, + * we can NOT send Content-Length headers, as the have to reflect size + * AFTER applying compression/transfer encoding. + * + * @param boolean $set=null + * @return boolean true if we use compression, false otherwise + */ + public static function use_compression($set=null) + { + static $compression = null; + if (isset($set)) + { + ini_set('zlib.output_compression', $compression=(boolean)$set); + } + elseif (!isset($compression)) + { + $compression = (boolean)ini_get('zlib.output_compression'); + } + //error_log(__METHOD__."(".array2string($set).") returning ".array2string($compression)); + return $compression; + } + + // {{{ http_GET() + + /** + * GET method handler + * + * @param void + * @return void + */ + function http_GET() + { + // TODO check for invalid stream + $options = Array(); + $options["path"] = $this->path; + + $this->_get_ranges($options); + + if (true === ($status = $this->GET($options))) { + if (!headers_sent()) { + $status = "200 OK"; + + if (!isset($options['mimetype'])) { + $options['mimetype'] = "application/octet-stream"; + } + // switching off zlib.output_compression for everything but text files, + // as the double compression of zip files makes problems eg. with lighttpd + // and anyway little sense with with other content like pictures + if (substr($options['mimetype'],0,5) != 'text/') + { + self::use_compression(false); + } + header("Content-type: $options[mimetype]"); + + if (isset($options['mtime'])) { + header("Last-modified:".gmdate("D, d M Y H:i:s ", $options['mtime'])."GMT"); + } + // fix for IE and https, thanks to rob_burcham@yahoo.com + // see http://us3.php.net/manual/en/function.header.php#83219 + // and http://support.microsoft.com/kb/812935 + header("Cache-Control: maxage=1"); //In seconds + header("Pragma: public"); + + if (isset($options['stream'])) { + // GET handler returned a stream + if (!empty($options['ranges']) && (0===fseek($options['stream'], 0, SEEK_SET))) { + // partial request and stream is seekable + + if (count($options['ranges']) === 1) { + $range = $options['ranges'][0]; + + if (isset($range['start'])) { + fseek($options['stream'], $range['start'], SEEK_SET); + if (feof($options['stream'])) { + $this->http_status("416 Requested range not satisfiable"); + return; + } + + if (isset($range['end'])) { + $size = $range['end']-$range['start']+1; + $this->http_status("206 Partial content"); + if (!self::use_compression()) header("Content-Length: $size"); + header("Content-Range: bytes $range[start]-$range[end]/" + . (isset($options['size']) ? $options['size'] : "*")); + while ($size && !feof($options['stream'])) { + $buffer = fread($options['stream'], 4096); + $size -= $this->bytes($buffer); + echo $buffer; + } + } else { + $this->http_status("206 Partial content"); + if (isset($options['size'])) { + if (!self::use_compression()) header("Content-Length: ".($options['size'] - $range['start'])); + header("Content-Range: bytes ".$range['start']."-".$range['end']."/" + . (isset($options['size']) ? $options['size'] : "*")); + } + fpassthru($options['stream']); + } + } else { + if (!self::use_compression()) header("Content-length: ".$range['last']); + fseek($options['stream'], -$range['last'], SEEK_END); + fpassthru($options['stream']); + } + } else { + $this->_multipart_byterange_header(); // init multipart + foreach ($options['ranges'] as $range) { + // TODO what if size unknown? 500? + if (isset($range['start'])) { + $from = $range['start']; + $to = !empty($range['end']) ? $range['end'] : $options['size']-1; + } else { + $from = $options['size'] - $range['last']-1; + $to = $options['size'] -1; + } + $total = isset($options['size']) ? $options['size'] : "*"; + $size = $to - $from + 1; + $this->_multipart_byterange_header($options['mimetype'], $from, $to, $total); + + + fseek($options['stream'], $from, SEEK_SET); + while ($size && !feof($options['stream'])) { + $buffer = fread($options['stream'], 4096); + $size -= $this->bytes($buffer); + echo $buffer; + } + } + $this->_multipart_byterange_header(); // end multipart + } + } else { + // normal request or stream isn't seekable, return full content + if (isset($options['size']) && !self::use_compression()) { + header("Content-Length: ".$options['size']); + } + fpassthru($options['stream']); + return; // no more headers + } + } elseif (isset($options['data'])) { + if (is_array($options['data'])) { + // reply to partial request + } else { + if (!self::use_compression()) header("Content-Length: ".$this->bytes($options['data'])); + echo $options['data']; + } + } + } + } + + if (!headers_sent()) { + if (false === $status) { + $this->http_status("404 not found"); + } else { + // TODO: check setting of headers in various code paths above + $this->http_status("$status"); + } + } + } + + + /** + * parse HTTP Range: header + * + * @param array options array to store result in + * @return void + */ + function _get_ranges(&$options) + { + // process Range: header if present + if (isset($this->_SERVER['HTTP_RANGE'])) { + + // we only support standard "bytes" range specifications for now + if (preg_match('/bytes\s*=\s*(.+)/', $this->_SERVER['HTTP_RANGE'], $matches)) { + $options["ranges"] = array(); + + // ranges are comma separated + foreach (explode(",", $matches[1]) as $range) { + // ranges are either from-to pairs or just end positions + list($start, $end) = explode("-", $range); + $options["ranges"][] = ($start==="") + ? array("last"=>$end) + : array("start"=>$start, "end"=>$end); + } + } + } + } + + /** + * generate separator headers for multipart response + * + * first and last call happen without parameters to generate + * the initial header and closing sequence, all calls inbetween + * require content mimetype, start and end byte position and + * optionaly the total byte length of the requested resource + * + * @param string mimetype + * @param int start byte position + * @param int end byte position + * @param int total resource byte size + */ + function _multipart_byterange_header($mimetype = false, $from = false, $to=false, $total=false) + { + if ($mimetype === false) { + if (!isset($this->multipart_separator)) { + // initial + + // a little naive, this sequence *might* be part of the content + // but it's really not likely and rather expensive to check + $this->multipart_separator = "SEPARATOR_".md5(microtime()); + + // generate HTTP header + header("Content-type: multipart/byteranges; boundary=".$this->multipart_separator); + } else { + // final + + // generate closing multipart sequence + echo "\n--{$this->multipart_separator}--"; + } + } else { + // generate separator and header for next part + echo "\n--{$this->multipart_separator}\n"; + echo "Content-type: $mimetype\n"; + echo "Content-range: $from-$to/". ($total === false ? "*" : $total); + echo "\n\n"; + } + } + + + + // }}} + + // {{{ http_HEAD() + + /** + * HEAD method handler + * + * @param void + * @return void + */ + function http_HEAD() + { + $status = false; + $options = Array(); + $options["path"] = $this->path; + + if (method_exists($this, "HEAD")) { + $status = $this->head($options); + } else if (method_exists($this, "GET")) { + ob_start(); + $status = $this->GET($options); + if (!isset($options['size'])) { + $options['size'] = ob_get_length(); + } + ob_end_clean(); + } + + if (!isset($options['mimetype'])) { + $options['mimetype'] = "application/octet-stream"; + } + header("Content-type: $options[mimetype]"); + + if (isset($options['mtime'])) { + header("Last-modified:".gmdate("D, d M Y H:i:s ", $options['mtime'])."GMT"); + } + + if (isset($options['size'])) { + header("Content-Length: ".$options['size']); + } + + if ($status === true) $status = "200 OK"; + if ($status === false) $status = "404 Not found"; + + $this->http_status($status); + } + + // }}} + + // {{{ http_POST() + + /** + * POST method handler + * + * @param void + * @return void + */ + function http_POST() + { + $status = '405 Method not allowed'; + $options = Array(); + $options['path'] = $this->path; + + if (isset($this->_SERVER['CONTENT_LENGTH'])) + { + $options['content_length'] = $this->_SERVER['CONTENT_LENGTH']; + } + elseif (isset($this->_SERVER['X-Expected-Entity-Length'])) + { + // MacOS gives us that hint + $options['content_length'] = $this->_SERVER['X-Expected-Entity-Length']; + } + + // get the Content-type + if (isset($this->_SERVER["CONTENT_TYPE"])) { + // for now we do not support any sort of multipart requests + if (!strncmp($this->_SERVER["CONTENT_TYPE"], 'multipart/', 10)) { + $this->http_status('501 not implemented'); + echo 'The service does not support mulipart POST requests'; + return; + } + $options['content_type'] = $this->_SERVER['CONTENT_TYPE']; + } else { + // default content type if none given + $options['content_type'] = 'application/octet-stream'; + } + + $options['stream'] = fopen('php://input', 'r'); + switch($this->_SERVER['HTTP_CONTENT_ENCODING']) + { + case 'gzip': + case 'deflate': //zlib + if (extension_loaded('zlib')) + { + stream_filter_append($options['stream'], 'zlib.inflate', STREAM_FILTER_READ); + } + } + // store request in $this->request, if requested via $this->store_request + if ($this->store_request) + { + $options['content'] = ''; + while(!feof($options['stream'])) + { + $options['content'] .= fread($options['stream'],8192); + } + $this->request =& $options['content']; + unset($options['stream']); + } + + /* RFC 2616 2.6 says: "The recipient of the entity MUST NOT + ignore any Content-* (e.g. Content-Range) headers that it + does not understand or implement and MUST return a 501 + (Not Implemented) response in such cases." + */ + foreach ($this->_SERVER as $key => $val) { + if (strncmp($key, 'HTTP_CONTENT', 11)) continue; + switch ($key) { + case 'HTTP_CONTENT_ENCODING': // RFC 2616 14.11 + switch($this->_SERVER['HTTP_CONTENT_ENCODING']) + { + case 'gzip': + case 'deflate': //zlib + if (extension_loaded('zlib')) break; + // fall through for no zlib support + default: + $this->http_status('415 Unsupported Media Type'); + echo "The service does not support '$val' content encoding"; + return; + } + break; + + case 'HTTP_CONTENT_LANGUAGE': // RFC 2616 14.12 + // we assume it is not critical if this one is ignored + // in the actual POST implementation ... + $options['content_language'] = $val; + break; + + case 'HTTP_CONTENT_LENGTH': + // defined on IIS and has the same value as CONTENT_LENGTH + break; + + case 'HTTP_CONTENT_LOCATION': // RFC 2616 14.14 + /* The meaning of the Content-Location header in PUT + or POST requests is undefined; servers are free + to ignore it in those cases. */ + break; + + case 'HTTP_CONTENT_RANGE': // RFC 2616 14.16 + // single byte range requests are supported + // the header format is also specified in RFC 2616 14.16 + // TODO we have to ensure that implementations support this or send 501 instead + if (!preg_match('@bytes\s+(\d+)-(\d+)/((\d+)|\*)@', $val, $matches)) { + $this->http_status('400 bad request'); + echo 'The service does only support single byte ranges'; + return; + } + + $range = array('start'=>$matches[1], 'end'=>$matches[2]); + if (is_numeric($matches[3])) { + $range['total_length'] = $matches[3]; + } + $option['ranges'][] = $range; + + // TODO make sure the implementation supports partial POST + // this has to be done in advance to avoid data being overwritten + // on implementations that do not support this ... + break; + + case 'HTTP_CONTENT_TYPE': + // defined on IIS and has the same value as CONTENT_TYPE + break; + + case 'HTTP_CONTENT_MD5': // RFC 2616 14.15 + // TODO: maybe we can just pretend here? + $this->http_status('501 not implemented'); + echo 'The service does not support content MD5 checksum verification'; + return; + + case 'HTTP_CONTENT_DISPOSITION': + // do NOT care about Content-Disposition in POST requests required by CalDAV managed attachments + break; + + default: + // any other unknown Content-* headers + $this->http_status('501 not implemented'); + echo "The service does not support '$key'"; + return; + } + } + + if (method_exists($this, 'POST')) { + $status = $this->POST($options); + + if ($status === false) { + $status = '400 Something went wrong'; + } else if ($status === true) { + $status = '200 OK'; + } else if (is_resource($status) && get_resource_type($status) == 'stream') { + $stream = $status; + + $status = empty($options['new']) ? '200 OK' : '201 Created'; + + if (!empty($options['ranges'])) { + // TODO multipart support is missing (see also above) + if (0 == fseek($stream, $range[0]['start'], SEEK_SET)) { + $length = $range[0]['end']-$range[0]['start']+1; + if (!fwrite($stream, fread($options['stream'], $length))) { + $status = '403 Forbidden'; + } + } else { + $status = '403 Forbidden'; + } + } else { + while (!feof($options['stream'])) { + if (false === fwrite($stream, fread($options['stream'], 4096))) { + $status = '403 Forbidden'; + break; + } + } + } + fclose($stream); + } + } + $this->http_status($status); + } + + // }}} + + // {{{ http_PUT() + + /** + * PUT method handler + * + * @param void + * @return void + */ + function http_PUT() + { + if ($this->_check_lock_status($this->path)) { + $options = Array(); + $options["path"] = $this->path; + + if (isset($this->_SERVER['CONTENT_LENGTH'])) + { + $options['content_length'] = $this->_SERVER['CONTENT_LENGTH']; + } + elseif (isset($this->_SERVER['X-Expected-Entity-Length'])) + { + // MacOS gives us that hint + $options['content_length'] = $this->_SERVER['X-Expected-Entity-Length']; + } + + // get the Content-type + if (isset($this->_SERVER["CONTENT_TYPE"])) { + // for now we do not support any sort of multipart requests + if (!strncmp($this->_SERVER["CONTENT_TYPE"], "multipart/", 10)) { + $this->http_status("501 not implemented"); + echo "The service does not support multipart PUT requests"; + return; + } + $options["content_type"] = $this->_SERVER["CONTENT_TYPE"]; + } else { + // default content type if none given + $options["content_type"] = "application/octet-stream"; + } + + $options["stream"] = fopen("php://input", "r"); + switch($this->_SERVER['HTTP_CONTENT_ENCODING']) + { + case 'gzip': + case 'deflate': //zlib + if (extension_loaded('zlib')) + { + stream_filter_append($options['stream'], 'zlib.inflate', STREAM_FILTER_READ); + } + } + // store request in $this->request, if requested via $this->store_request + if ($this->store_request) + { + $options['content'] = ''; + while(!feof($options['stream'])) + { + $options['content'] .= fread($options['stream'],8192); + } + $this->request =& $options['content']; + unset($options['stream']); + } + + /* RFC 2616 2.6 says: "The recipient of the entity MUST NOT + ignore any Content-* (e.g. Content-Range) headers that it + does not understand or implement and MUST return a 501 + (Not Implemented) response in such cases." + */ + foreach ($this->_SERVER as $key => $val) { + if (strncmp($key, "HTTP_CONTENT", 11)) continue; + switch ($key) { + case 'HTTP_CONTENT_ENCODING': // RFC 2616 14.11 + switch($this->_SERVER['HTTP_CONTENT_ENCODING']) + { + case 'gzip': + case 'deflate': //zlib + if (extension_loaded('zlib')) break; + // fall through for no zlib support + default: + $this->http_status('415 Unsupported Media Type'); + echo "The service does not support '$val' content encoding"; + return; + } + break; + + case 'HTTP_CONTENT_LANGUAGE': // RFC 2616 14.12 + // we assume it is not critical if this one is ignored + // in the actual PUT implementation ... + $options["content_language"] = $val; + break; + + case 'HTTP_CONTENT_LENGTH': + // defined on IIS and has the same value as CONTENT_LENGTH + break; + + case 'HTTP_CONTENT_LOCATION': // RFC 2616 14.14 + /* The meaning of the Content-Location header in PUT + or POST requests is undefined; servers are free + to ignore it in those cases. */ + break; + + case 'HTTP_CONTENT_RANGE': // RFC 2616 14.16 + // single byte range requests are supported + // the header format is also specified in RFC 2616 14.16 + // TODO we have to ensure that implementations support this or send 501 instead + if (!preg_match('@bytes\s+(\d+)-(\d+)/((\d+)|\*)@', $val, $matches)) { + $this->http_status("400 bad request"); + echo "The service does only support single byte ranges"; + return; + } + + $range = array("start" => $matches[1], "end" => $matches[2]); + if (is_numeric($matches[3])) { + $range["total_length"] = $matches[3]; + } + + if (!isset($options['ranges'])) { + $options['ranges'] = array(); + } + + $options["ranges"][] = $range; + + // TODO make sure the implementation supports partial PUT + // this has to be done in advance to avoid data being overwritten + // on implementations that do not support this ... + break; + + case 'HTTP_CONTENT_TYPE': + // defined on IIS and has the same value as CONTENT_TYPE + break; + + case 'HTTP_CONTENT_MD5': // RFC 2616 14.15 + // TODO: maybe we can just pretend here? + $this->http_status("501 not implemented"); + echo "The service does not support content MD5 checksum verification"; + return; + + default: + // any other unknown Content-* headers + $this->http_status("501 not implemented"); + echo "The service does not support '$key'"; + return; + } + } + + $stat = $this->PUT($options); + + if ($stat === false) { + $stat = "403 Forbidden"; + } else if (is_resource($stat) && get_resource_type($stat) == "stream") { + $stream = $stat; + + $stat = $options["new"] ? "201 Created" : "204 No Content"; + + if (!empty($options["ranges"])) { + // TODO multipart support is missing (see also above) + if (0 == fseek($stream, $options['ranges'][0]["start"], SEEK_SET)) { + $length = $options['ranges'][0]["end"] - $options['ranges'][0]["start"]+1; + + while (!feof($options['stream'])) { + if ($length <= 0) { + break; + } + + if ($length <= 8192) { + $data = fread($options['stream'], $length); + } else { + $data = fread($options['stream'], 8192); + } + + if ($data === false) { + $stat = "400 Bad request"; + } elseif (strlen($data)) { + if (false === fwrite($stream, $data)) { + $stat = "403 Forbidden"; + break; + } + $length -= strlen($data); + } + } + } else { + $stat = "403 Forbidden"; + } + } else { + while (!feof($options["stream"])) { + if (false === fwrite($stream, fread($options["stream"], 8192))) { + $stat = "403 Forbidden"; + break; + } + } + } + + fclose($stream); + } + + $this->http_status($stat); + } else { + $this->http_status("423 Locked"); + } + } + + // }}} + + + // {{{ http_DELETE() + + /** + * DELETE method handler + * + * @param void + * @return void + */ + function http_DELETE() + { + // check RFC 2518 Section 9.2, last paragraph + if (isset($this->_SERVER["HTTP_DEPTH"])) { + if ($this->_SERVER["HTTP_DEPTH"] != "infinity") { + if (stripos($_SERVER['HTTP_USER_AGENT'],'webdrive') !== false) + { + // pretend we didnt see it, as webdrive does not handle the depth parameter correctly while deleting collections + } + else + { + $this->http_status("400 Bad Request"); + return; + } + } + } + + // check lock status + if ($this->_check_lock_status($this->path)) { + // ok, proceed + $options = Array(); + $options["path"] = $this->path; + + $stat = $this->DELETE($options); + + $this->http_status($stat); + } else { + // sorry, its locked + $this->http_status("423 Locked"); + } + } + + // }}} + + // {{{ http_COPY() + + /** + * COPY method handler + * + * @param void + * @return void + */ + function http_COPY() + { + // no need to check source lock status here + // destination lock status is always checked by the helper method + $this->_copymove("copy"); + } + + // }}} + + // {{{ http_MOVE() + + /** + * MOVE method handler + * + * @param void + * @return void + */ + function http_MOVE() + { + if ($this->_check_lock_status($this->path)) { + // destination lock status is always checked by the helper method + $this->_copymove("move"); + } else { + $this->http_status("423 Locked"); + } + } + + // }}} + + + // {{{ http_LOCK() + + /** + * LOCK method handler + * + * @param void + * @return void + */ + function http_LOCK() + { + $options = Array(); + $options["path"] = $this->path; + + if (isset($this->_SERVER['HTTP_DEPTH'])) { + $options["depth"] = $this->_SERVER["HTTP_DEPTH"]; + } else { + $options["depth"] = "infinity"; + } + + if (isset($this->_SERVER["HTTP_TIMEOUT"])) { + $options["timeout"] = explode(",", $this->_SERVER["HTTP_TIMEOUT"]); + } + + if (empty($this->_SERVER['CONTENT_LENGTH']) && !empty($this->_SERVER['HTTP_IF'])) { + // check if locking is possible + if (!$this->_check_lock_status($this->path)) { + $this->http_status("423 Locked"); + return; + } + + // refresh lock + $options["locktoken"] = substr($this->_SERVER['HTTP_IF'], 2, -2); + $options["update"] = $options["locktoken"]; + + // setting defaults for required fields, LOCK() SHOULD overwrite these + $options['owner'] = "unknown"; + $options['scope'] = "exclusive"; + $options['type'] = "write"; + + + $stat = $this->LOCK($options); + } else { + // extract lock request information from request XML payload + $lockinfo = new _parse_lockinfo("php://input"); + if (!$lockinfo->success) { + $this->http_status("400 bad request"); + } + + // check if locking is possible + if (!$this->_check_lock_status($this->path, $lockinfo->lockscope === "shared")) { + $this->http_status("423 Locked"); + return; + } + + // new lock + $options["scope"] = $lockinfo->lockscope; + $options["type"] = $lockinfo->locktype; + // Todo: lockinfo::owner still contains D:href opening and closing tags, maybe they should be removed here with strip_tags + $options["owner"] = $lockinfo->owner; + $options["locktoken"] = $this->_new_locktoken(); + + $stat = $this->LOCK($options); + } + + if (is_bool($stat)) { + $http_stat = $stat ? "200 OK" : "423 Locked"; + } else { + $http_stat = (string)$stat; + } + $this->http_status($http_stat); + + if ($http_stat{0} == 2) { // 2xx states are ok + if ($options["timeout"]) { + // if multiple timeout values were given we take the first only + if (is_array($options["timeout"])) { + reset($options["timeout"]); + $options["timeout"] = current($options["timeout"]); + } + // if the timeout is numeric only we need to reformat it + if (is_numeric($options["timeout"])) { + // more than a million is considered an absolute timestamp + // less is more likely a relative value + if ($options["timeout"]>1000000) { + $timeout = "Second-".($options['timeout']-time()); + } else { + $timeout = "Second-$options[timeout]"; + } + } else { + // non-numeric values are passed on verbatim, + // no error checking is performed here in this case + // TODO: send "Infinite" on invalid timeout strings? + $timeout = $options["timeout"]; + } + } else { + $timeout = "Infinite"; + } + + header('Content-Type: text/xml; charset="utf-8"'); + header("Lock-Token: <$options[locktoken]>"); + echo "\n"; + echo "\n"; + echo ' <'.($this->crrnd?'':'D:')."lockdiscovery>\n"; + echo ' <'.($this->crrnd?'':'D:')."activelock>\n"; + echo ' <'.($this->crrnd?'':'D:')."lockscope>crrnd?'':'D:')."lockscope>\n"; + echo ' <'.($this->crrnd?'':'D:')."locktype>crrnd?'':'D:')."locktype>\n"; + echo ' <'.($this->crrnd?'':'D:')."depth>$options[depth]crrnd?'':'D:')."depth>\n"; + echo ' <'.($this->crrnd?'':'D:')."owner>$options[owner]crrnd?'':'D:')."owner>\n"; + echo ' <'.($this->crrnd?'':'D:')."timeout>$timeoutcrrnd?'':'D:')."timeout>\n"; + echo ' <'.($this->crrnd?'':'D:')."locktoken>$options[locktoken]crrnd?'':'D:')."locktoken>\n"; + echo ' crrnd?'':'D:')."activelock>\n"; + echo ' crrnd?'':'D:')."lockdiscovery>\n"; + echo 'crrnd?'':'D:')."prop>\n\n"; + } + } + + + // }}} + + // {{{ http_UNLOCK() + + /** + * UNLOCK method handler + * + * @param void + * @return void + */ + function http_UNLOCK() + { + $options = Array(); + $options["path"] = $this->path; + + if (isset($this->_SERVER['HTTP_DEPTH'])) { + $options["depth"] = $this->_SERVER["HTTP_DEPTH"]; + } else { + $options["depth"] = "infinity"; + } + + // strip surrounding <> + $options["token"] = substr(trim($this->_SERVER["HTTP_LOCK_TOKEN"]), 1, -1); + + // call user method + $stat = $this->UNLOCK($options); + + $this->http_status($stat); + } + + // }}} + + // {{{ http_ACL() + + /** + * ACL method handler + * + * @param void + * @return void + */ + function http_ACL() + { + $options = Array(); + $options['path'] = $this->path; + $options['errors'] = array(); + + if (isset($this->_SERVER['HTTP_DEPTH'])) { + $options['depth'] = $this->_SERVER['HTTP_DEPTH']; + } else { + $options['depth'] = 'infinity'; + } + + // call user method + $status = $this->ACL($options); + + // now we generate the reply header ... + $this->http_status($status); + $content = ''; + + if (is_array($options['errors']) && count($options['errors'])) { + header('Content-Type: text/xml; charset="utf-8"'); + // ... and payload + $content .= "\n"; + $content .= " \n"; + foreach ($options['errors'] as $violation) { + $content .= '<'.($this->crrnd?'':'D:')."$violation/>\n"; + } + $content .= 'crrnd?'':'D:')."error>\n"; + } + if (!self::use_compression()) header("Content-Length: ".$this->bytes($content)); + if ($content) echo $options['content']; + } + + // }}} + + // }}} + + // {{{ _copymove() + + function _copymove($what) + { + $options = Array(); + $options["path"] = $this->path; + + if (isset($this->_SERVER["HTTP_DEPTH"])) { + $options["depth"] = $this->_SERVER["HTTP_DEPTH"]; + } else { + $options["depth"] = "infinity"; + } + + $http_header_host = preg_replace("/:80$/", "", $this->_SERVER["HTTP_HOST"]); + + $url = parse_url($this->_SERVER["HTTP_DESTINATION"]); + $path = urldecode($url["path"]); + + if (isset($url["host"])) { + // TODO check url scheme, too + $http_host = $url["host"]; + if (isset($url["port"]) && $url["port"] != 80) + $http_host.= ":".$url["port"]; + } else { + // only path given, set host to self + $http_host == $http_header_host; + } + + if ($http_host == $http_header_host && + !strncmp($this->_SERVER["SCRIPT_NAME"], $path, + strlen($this->_SERVER["SCRIPT_NAME"]))) { + $options["dest"] = substr($path, strlen($this->_SERVER["SCRIPT_NAME"])); + if (!$this->_check_lock_status($options["dest"])) { + $this->http_status("423 Locked"); + return; + } + + } else { + $options["dest_url"] = $this->_SERVER["HTTP_DESTINATION"]; + } + + // see RFC 2518 Sections 9.6, 8.8.4 and 8.9.3 + if (isset($this->_SERVER["HTTP_OVERWRITE"])) { + $options["overwrite"] = $this->_SERVER["HTTP_OVERWRITE"] == "T"; + } else { + $options["overwrite"] = true; + } + + $stat = $this->$what($options); + $this->http_status($stat); + } + + // }}} + + // {{{ _allow() + + /** + * check for implemented HTTP methods + * + * @param void + * @return array something + */ + function _allow() + { + // OPTIONS is always there + $allow = array("OPTIONS" =>"OPTIONS"); + + // all other METHODS need both a http_method() wrapper + // and a method() implementation + // the base class supplies wrappers only + foreach (get_class_methods($this) as $method) { + if (!strncmp("http_", $method, 5)) { + $method = strtoupper(substr($method, 5)); + if (method_exists($this, $method)) { + $allow[$method] = $method; + } + } + } + + // we can emulate a missing HEAD implemetation using GET + if (isset($allow["GET"])) + $allow["HEAD"] = "HEAD"; + + // no LOCK without checklok() + if (!method_exists($this, "checklock")) { + unset($allow["LOCK"]); + unset($allow["UNLOCK"]); + } + + return $allow; + } + + // }}} + + /** + * helper for property element creation + * + * @param string XML namespace (optional) + * @param string property name + * @param string property value + * @praram boolen property raw-flag + * @return array property array + */ + function mkprop() + { + $args = func_get_args(); + switch (count($args)) { + case 4: + return array('ns' => $args[0], + 'name' => $args[1], + 'val' => $args[2], + 'raw' => true); + case 3: + return array('ns' => $args[0], + 'name' => $args[1], + 'val' => $args[2]); + default: + return array('ns' => 'DAV:', + 'name' => $args[0], + 'val' => $args[1]); + } + } + + // {{{ _check_auth + + /** + * check authentication if check is implemented + * + * @param void + * @return bool true if authentication succeded or not necessary + */ + function _check_auth() + { + if (method_exists($this, "checkAuth")) { + // PEAR style method name + return $this->checkAuth(@$this->_SERVER["AUTH_TYPE"], + @$this->_SERVER["PHP_AUTH_USER"], + @$this->_SERVER["PHP_AUTH_PW"]); + } else if (method_exists($this, "check_auth")) { + // old (pre 1.0) method name + return $this->check_auth(@$this->_SERVER["AUTH_TYPE"], + @$this->_SERVER["PHP_AUTH_USER"], + @$this->_SERVER["PHP_AUTH_PW"]); + } else { + // no method found -> no authentication required + return true; + } + } + + // }}} + + // {{{ UUID stuff + + /** + * generate Unique Universal IDentifier for lock token + * + * @param void + * @return string a new UUID + */ + function _new_uuid() + { + // use uuid extension from PECL if available + if (function_exists("uuid_create")) { + return uuid_create(); + } + + // fallback + $uuid = md5(microtime().getmypid()); // this should be random enough for now + + // set variant and version fields for 'true' random uuid + $uuid{12} = "4"; + $n = 8 + (ord($uuid{16}) & 3); + $hex = "0123456789abcdef"; + $uuid{16} = $hex{$n}; + + // return formated uuid + return substr($uuid, 0, 8)."-" + . substr($uuid, 8, 4)."-" + . substr($uuid, 12, 4)."-" + . substr($uuid, 16, 4)."-" + . substr($uuid, 20); + } + + /** + * create a new opaque lock token as defined in RFC2518 + * + * @param void + * @return string new RFC2518 opaque lock token + */ + function _new_locktoken() + { + return "opaquelocktoken:".HTTP_WebDAV_Server::_new_uuid(); + } + + // }}} + + // {{{ WebDAV If: header parsing + + /** + * + * + * @param string header string to parse + * @param int current parsing position + * @return array next token (type and value) + */ + function _if_header_lexer($string, &$pos) + { + // skip whitespace + while (ctype_space($string{$pos})) { + ++$pos; + } + + // already at end of string? + if (strlen($string) <= $pos) { + return false; + } + + // get next character + $c = $string{$pos++}; + + // now it depends on what we found + switch ($c) { + case "<": + // URIs are enclosed in <...> + $pos2 = strpos($string, ">", $pos); + $uri = substr($string, $pos, $pos2 - $pos); + $pos = $pos2 + 1; + return array("URI", $uri); + + case "[": + //Etags are enclosed in [...] + if ($string{$pos} == "W") { + $type = "ETAG_WEAK"; + $pos += 2; + } else { + $type = "ETAG_STRONG"; + } + $pos2 = strpos($string, "]", $pos); + $etag = substr($string, $pos + 1, $pos2 - $pos - 2); + $pos = $pos2 + 1; + return array($type, $etag); + + case "N": + // "N" indicates negation + $pos += 2; + return array("NOT", "Not"); + + default: + // anything else is passed verbatim char by char + return array("CHAR", $c); + } + } + + /** + * parse If: header + * + * @param string header string + * @return array URIs and their conditions + */ + function _if_header_parser($str) + { + $pos = 0; + $len = strlen($str); + $uris = array(); + + // parser loop + while ($pos < $len) { + // get next token + $token = $this->_if_header_lexer($str, $pos); + + // check for URI + if ($token[0] == "URI") { + $uri = $token[1]; // remember URI + $token = $this->_if_header_lexer($str, $pos); // get next token + } else { + $uri = ""; + } + + // sanity check + if ($token[0] != "CHAR" || $token[1] != "(") { + return false; + } + + $list = array(); + $level = 1; + $not = ""; + while ($level) { + $token = $this->_if_header_lexer($str, $pos); + if ($token[0] == "NOT") { + $not = "!"; + continue; + } + switch ($token[0]) { + case "CHAR": + switch ($token[1]) { + case "(": + $level++; + break; + case ")": + $level--; + break; + default: + return false; + } + break; + + case "URI": + $list[] = $not."<$token[1]>"; + break; + + case "ETAG_WEAK": + $list[] = $not."[W/'$token[1]']>"; + break; + + case "ETAG_STRONG": + $list[] = $not."['$token[1]']>"; + break; + + default: + return false; + } + $not = ""; + } + + if (@is_array($uris[$uri])) { + $uris[$uri] = array_merge($uris[$uri], $list); + } else { + $uris[$uri] = $list; + } + } + + return $uris; + } + + /** + * check if conditions from "If:" headers are meat + * + * the "If:" header is an extension to HTTP/1.1 + * defined in RFC 2518 section 9.4 + * + * @param void + * @return void + */ + function _check_if_header_conditions() + { + if (isset($this->_SERVER["HTTP_IF"])) { + $this->_if_header_uris = + $this->_if_header_parser($this->_SERVER["HTTP_IF"]); + + foreach ($this->_if_header_uris as $uri => $conditions) { + if ($uri == "") { + $uri = $this->uri; + } + // all must match + $state = true; + foreach ($conditions as $condition) { + // lock tokens may be free form (RFC2518 6.3) + // but if opaquelocktokens are used (RFC2518 6.4) + // we have to check the format (litmus tests this) + if (!strncmp($condition, "$/', $condition)) { + $this->http_status("423 Locked"); + return false; + } + } + if (!$this->_check_uri_condition($uri, $condition)) { + $this->http_status("412 Precondition failed"); + $state = false; + break; + } + } + + // any match is ok + if ($state == true) { + return true; + } + } + return false; + } + return true; + } + + /** + * Check a single URI condition parsed from an if-header + * + * Check a single URI condition parsed from an if-header + * + * @abstract + * @param string $uri URI to check + * @param string $condition Condition to check for this URI + * @returns bool Condition check result + */ + function _check_uri_condition($uri, $condition) + { + // not really implemented here, + // implementations must override + + // a lock token can never be from the DAV: scheme + // litmus uses DAV:no-lock in some tests + if (!strncmp(" ignored for now + if (method_exists($this, "checkLock")) { + // is locked? + $lock = $this->checkLock($path); + + // ... and lock is not owned? + if (is_array($lock) && count($lock)) { + // FIXME doesn't check uri restrictions yet + if (!isset($this->_SERVER["HTTP_IF"]) || !strstr($this->_SERVER["HTTP_IF"], $lock["token"])) { + if (!$exclusive_only || ($lock["scope"] !== "shared")) + return false; + } + } + } + return true; + } + + + // }}} + + + /** + * Generate lockdiscovery reply from checklock() result + * + * @param string resource path to check + * @return string lockdiscovery response + */ + function lockdiscovery($path) + { + // no lock support without checklock() method + if (!method_exists($this, "checklock")) { + return ""; + } + + // collect response here + $activelocks = ""; + + // get checklock() reply + $lock = $this->checklock($path); + + // generate block for returned data + if (is_array($lock) && count($lock)) { + // check for 'timeout' or 'expires' + if (!empty($lock["expires"])) { + $timeout = "Second-".($lock["expires"] - time()); + } else if (!empty($lock["timeout"])) { + $timeout = "Second-$lock[timeout]"; + } else { + $timeout = "Infinite"; + } + + // genreate response block + if ($this->crrnd) + { + $activelocks.= " + + <$lock[scope]/> + <$lock[type]/> + $lock[depth] + $lock[owner] + $timeout + $lock[token] + + "; + } + else + { + $activelocks.= " + + + + $lock[depth] + $lock[owner] + $timeout + $lock[token] + + "; + } + } + + // return generated response + //error_log(__METHOD__."\n".print_r($activelocks,true)); + return $activelocks; + } + + /** + * set HTTP return status and mirror it in a private header + * + * @param string status code and message + * @return void + */ + function http_status($status) + { + // simplified success case + if ($status === true) { + $status = "200 OK"; + } + + // remember status + $this->_http_status = $status; + + // generate HTTP status response + header("HTTP/1.1 $status"); + header("X-WebDAV-Status: $status", true); + } + + /** + * private minimalistic version of PHP urlencode() + * + * only blanks, percent and XML special chars must be encoded here + * full urlencode() encoding confuses some clients ... + * + * @param string URL to encode + * @return string encoded URL + */ + function _urlencode($url) + { + // cadaver (and probably all neon using agents) need a more complete url encoding + // otherwise special chars like "$,()'" in filenames do NOT work + // netdrive does NOT use a User-Agent, but requires full urlencoding for non-ascii chars (eg. German Umlauts) + if (strpos($_SERVER['HTTP_USER_AGENT'],'neon') !== false || !isset($_SERVER['HTTP_USER_AGENT'])) + { + return strtr(rawurlencode($url),array( + '%2F' => '/', + '%3A' => ':', + )); + } + //error_log( __METHOD__."\n" .print_r($url,true)); + return strtr($url, array(' ' => '%20', + '%' => '%25', + '&' => '%26', + '<' => '%3C', + '>' => '%3E', + '+' => '%2B', + )); + } + + /** + * private version of PHP urldecode + * + * not really needed but added for completenes + * + * @param string URL to decode + * @return string decoded URL + */ + function _urldecode($path) + { + return rawurldecode($path); + } + + /** + * Encode a hierarchical properties like: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * @param array $props + * @param string $ns + * @param strin $ns_defs + * @param array $ns_hash + * @return string + */ + function _hierarchical_prop_encode(array $props, $ns, &$ns_defs, array &$ns_hash) + { + $ret = ''; + + //error_log(__METHOD__.'('.array2string($props).')'); + if (isset($props['name'])) $props = array($props); + + foreach($props as $prop) + { + if (!isset($ns_hash[$prop['ns']])) // unknown namespace + { + // register namespace + $ns_name = 'ns'.(count($ns_hash) + 1); + $ns_hash[$prop['ns']] = $ns_name; + $ns_defs .= ' xmlns:'.$ns_name.'="'.$prop['ns'].'"'; + } + if (is_array($prop['val'])) + { + $subprop = $prop['val']; + if (isset($subprop['ns']) || isset($subprop[0]['ns'])) + { + $ret .= '<'.($prop['ns'] == $ns ? ($this->crrnd ? '' : $ns_hash[$ns].':') : $ns_hash[$prop['ns']].':').$prop['name']. + (empty($prop['val']) ? '/>' : '>'.$this->_hierarchical_prop_encode($prop['val'], $prop['ns'], $ns_defs, $ns_hash). + 'crrnd ? '' : $ns_hash[$ns].':') : ($this->crrnd ? '' : $ns_hash[$prop['ns']].':')).$prop['name'].'>'); + } + else // val contains only attributes, no value + { + $vals = ''; + + foreach($subprop as $attr => $val) + { + $vals .= ' '.$attr.'="'.htmlspecialchars($val, ENT_NOQUOTES, 'utf-8').'"'; + } + + $ret .= '<'.($prop['ns'] == $ns ? ($this->crrnd ? '' : $ns_hash[$ns].':') : $ns_hash[$prop['ns']].':').$prop['name']. + $vals .'/>'; + } + } + else + { + if (empty($prop['val'])) + { + $val = ''; + } + else + { + if(isset($prop['raw'])) + { + $val = $this->_prop_encode(''); + } else { + $val = $this->_prop_encode(htmlspecialchars($prop['val'], ENT_NOQUOTES, 'utf-8')); + // for href properties we need (minimalistic) urlencoding, eg. space + if ($prop['name'] == 'href') + { + $val = $this->_urlencode($val); + } + } + } + + $ret .= '<'.($prop['ns'] == $ns ? ($this->crrnd ? '' : $ns_hash[$ns].':') : $ns_hash[$prop['ns']].':').$prop['name']. + (empty($prop['val']) ? ' />' : '>'.$val.'crrnd ? '' : $ns_hash[$ns].':') : ($this->crrnd ? '' : $ns_hash[$prop['ns']].':')).$prop['name'].'>'); + } + } + + //error_log(__METHOD__.'('.array2string($props).") crrnd=$this->crrnd returning ".array2string($ret)); + return $ret; + } + + /** + * UTF-8 encode property values if not already done so + * + * @param string text to encode + * @return string utf-8 encoded text + */ + function _prop_encode($text) + { + //error_log( __METHOD__."\n" .print_r($text,true)); + //error_log("prop-encode:" . print_r($this->_prop_encoding,true)); + + switch (strtolower($this->_prop_encoding)) { + case "utf-8": + //error_log( __METHOD__."allready encoded\n" .print_r($text,true)); + return $text; + case "iso-8859-1": + case "iso-8859-15": + case "latin-1": + default: + error_log( __METHOD__."utf8 encode\n" .print_r(utf8_encode($text),true)); + return utf8_encode($text); + } + } + + /** + * Slashify - make sure path ends in a slash + * + * @param string directory path + * @returns string directory path wiht trailing slash + */ + function _slashify($path) + { + //error_log(__METHOD__." called with $path"); + if ($path[$this->bytes($path)-1] != '/') { + //error_log(__METHOD__." added slash at the end of path"); + $path = $path."/"; + } + return $path; + } + + /** + * Unslashify - make sure path doesn't in a slash + * + * @param string directory path + * @returns string directory path wihtout trailing slash + */ + function _unslashify($path) + { + //error_log(__METHOD__." called with $path"); + if ($path[$this->bytes($path)-1] == '/') { + $path = substr($path, 0, -1); + //error_log(__METHOD__." removed slash at the end of path"); + } + return $path; + } + + /** + * Merge two paths, make sure there is exactly one slash between them + * + * @param string parent path + * @param string child path + * @return string merged path + */ + function _mergePaths($parent, $child) + { + //error_log("merge called :\n$parent \n$child\n" . function_backtrace()); + //error_log("merge :\n".print_r($this->_mergePaths($this->_SERVER["SCRIPT_NAME"], $this->path)true)); + if ($child{0} == '/') { + return $this->_unslashify($parent).$child; + } else { + return $this->_slashify($parent).$child; + } + } + + /** + * mbstring.func_overload save strlen version: counting the bytes not the chars + * + * @param string $str + * @return int + */ + function bytes($str) + { + static $func_overload; + + if (is_null($func_overload)) + { + $func_overload = @extension_loaded('mbstring') ? ini_get('mbstring.func_overload') : 0; + } + return $func_overload & 2 ? mb_strlen($str,'ascii') : strlen($str); + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ +?> diff --git a/egw-pear/HTTP/WebDAV/Server/Filesystem.php b/egw-pear/HTTP/WebDAV/Server/Filesystem.php new file mode 100644 index 0000000000..a6979782e9 --- /dev/null +++ b/egw-pear/HTTP/WebDAV/Server/Filesystem.php @@ -0,0 +1,921 @@ + + * @version @package-version@ + */ +class HTTP_WebDAV_Server_Filesystem extends HTTP_WebDAV_Server +{ + /** + * Root directory for WebDAV access + * + * Defaults to webserver document root (set by ServeRequest) + * + * @access private + * @var string + */ + var $base = ""; + + /** + * MySQL Host where property and locking information is stored + * + * @access private + * @var string + */ + var $db_host = "localhost"; + + /** + * MySQL database for property/locking information storage + * + * @access private + * @var string + */ + var $db_name = "webdav"; + + /** + * MySQL table name prefix + * + * @access private + * @var string + */ + var $db_prefix = ""; + + /** + * MySQL user for property/locking db access + * + * @access private + * @var string + */ + var $db_user = "root"; + + /** + * MySQL password for property/locking db access + * + * @access private + * @var string + */ + var $db_passwd = ""; + + /** + * Serve a webdav request + * + * @access public + * @param string + */ + function ServeRequest($base = false) + { + // special treatment for litmus compliance test + // reply on its identifier header + // not needed for the test itself but eases debugging + if (isset($this->_SERVER['HTTP_X_LITMUS'])) { + error_log("Litmus test ".$this->_SERVER['HTTP_X_LITMUS']); + header("X-Litmus-reply: ".$this->_SERVER['HTTP_X_LITMUS']); + } + + // set root directory, defaults to webserver document root if not set + if ($base) { + $this->base = realpath($base); // TODO throw if not a directory + } else if (!$this->base) { + $this->base = $this->_SERVER['DOCUMENT_ROOT']; + } + + // establish connection to property/locking db + mysql_connect($this->db_host, $this->db_user, $this->db_passwd) or die(mysql_error()); + mysql_select_db($this->db_name) or die(mysql_error()); + // TODO throw on connection problems + + // let the base class do all the work + parent::ServeRequest(); + } + + /** + * No authentication is needed here + * + * @access private + * @param string HTTP Authentication type (Basic, Digest, ...) + * @param string Username + * @param string Password + * @return bool true on successful authentication + */ + function check_auth($type, $user, $pass) + { + return true; + } + + + /** + * PROPFIND method handler + * + * @param array general parameter passing array + * @param array return array for file properties + * @return bool true on success + */ + function PROPFIND(&$options, &$files) + { + // get absolute fs path to requested resource + $fspath = $this->base . $options["path"]; + + // sanity check + if (!file_exists($fspath)) { + return false; + } + + // prepare property array + $files["files"] = array(); + + // store information for the requested path itself + $files["files"][] = $this->fileinfo($options["path"]); + + // information for contained resources requested? + if (!empty($options["depth"]) && is_dir($fspath) && $this->_is_readable($fspath)) { + + // make sure path ends with '/' + $options["path"] = $this->_slashify($options["path"]); + + // try to open directory + $handle = opendir($fspath); + + if ($handle) { + // ok, now get all its contents + while ($filename = readdir($handle)) { + if ($filename != "." && $filename != "..") { + $files["files"][] = $this->fileinfo($options["path"].$filename); + } + } + // TODO recursion needed if "Depth: infinite" + closedir($handle); + } + } + + // ok, all done + return true; + } + + /** + * Get properties for a single file/resource + * + * @param string resource path + * @return array resource properties + */ + function fileinfo($path) + { + // map URI path to filesystem path + $fspath = $this->base . $path; + + // create result array + $info = array(); + // TODO remove slash append code when base clase is able to do it itself + $info["path"] = is_dir($fspath) ? $this->_slashify($path) : $path; + $info["props"] = array(); + + // no special beautified displayname here ... + $info["props"][] = $this->mkprop("displayname", strtoupper($path)); + + // creation and modification time + $info["props"][] = $this->mkprop("creationdate", filectime($fspath)); + $info["props"][] = $this->mkprop("getlastmodified", filemtime($fspath)); + + // Microsoft extensions: last access time and 'hidden' status + $info["props"][] = $this->mkprop("lastaccessed", fileatime($fspath)); + $info["props"][] = $this->mkprop("ishidden", ('.' === substr(basename($fspath), 0, 1))); + + // type and size (caller already made sure that path exists) + if (is_dir($fspath)) { + // directory (WebDAV collection) + $info["props"][] = $this->mkprop("resourcetype", "collection"); + $info["props"][] = $this->mkprop("getcontenttype", "httpd/unix-directory"); + } else { + // plain file (WebDAV resource) + $info["props"][] = $this->mkprop("resourcetype", ""); + if ($this->_is_readable($fspath)) { + $info["props"][] = $this->mkprop("getcontenttype", $this->_mimetype($fspath)); + } else { + $info["props"][] = $this->mkprop("getcontenttype", "application/x-non-readable"); + } + $info["props"][] = $this->mkprop("getcontentlength", filesize($fspath)); + } + + // get additional properties from database + $query = "SELECT ns, name, value + FROM {$this->db_prefix}properties + WHERE path = '$path'"; + $res = mysql_query($query); + while ($row = mysql_fetch_assoc($res)) { + $info["props"][] = $this->mkprop($row["ns"], $row["name"], $row["value"]); + } + mysql_free_result($res); + + return $info; + } + + /** + * detect if a given program is found in the search PATH + * + * helper function used by _mimetype() to detect if the + * external 'file' utility is available + * + * @param string program name + * @param string optional search path, defaults to $PATH + * @return bool true if executable program found in path + */ + function _can_execute($name, $path = false) + { + // path defaults to PATH from environment if not set + if ($path === false) { + $path = getenv("PATH"); + } + + // check method depends on operating system + if (!strncmp(PHP_OS, "WIN", 3)) { + // on Windows an appropriate COM or EXE file needs to exist + $exts = array(".exe", ".com"); + $check_fn = "file_exists"; + } else { + // anywhere else we look for an executable file of that name + $exts = array(""); + $check_fn = "is_executable"; + } + + // now check the directories in the path for the program + foreach (explode(PATH_SEPARATOR, $path) as $dir) { + // skip invalid path entries + if (!file_exists($dir)) continue; + if (!is_dir($dir)) continue; + + // and now look for the file + foreach ($exts as $ext) { + if ($check_fn("$dir/$name".$ext)) return true; + } + } + + return false; + } + + /** + * Check if path is readable by current user + * + * Allow extending classes to overwrite it + * + * @param string $fspath + * @return boolean + */ + function _is_readable($fspath) + { + return is_readable($fspath); + } + + /** + * Check if path is writable by current user + * + * Allow extending classes to overwrite it + * + * @param string $fspath + * @return boolean + */ + function _is_writable($fspath) + { + return is_writable($fspath); + } + + /** + * try to detect the mime type of a file + * + * @param string file path + * @return string guessed mime type + */ + function _mimetype($fspath) + { + if (is_dir($fspath)) { + // directories are easy + return "httpd/unix-directory"; + } else if (function_exists("mime_content_type")) { + // use mime magic extension if available + $mime_type = mime_content_type($fspath); + } else if ($this->_can_execute("file")) { + // it looks like we have a 'file' command, + // lets see it it does have mime support + $fp = popen("file -i '$fspath' 2>/dev/null", "r"); + $reply = fgets($fp); + pclose($fp); + + // popen will not return an error if the binary was not found + // and find may not have mime support using "-i" + // so we test the format of the returned string + + // the reply begins with the requested filename + if (!strncmp($reply, "$fspath: ", strlen($fspath)+2)) { + $reply = substr($reply, strlen($fspath)+2); + // followed by the mime type (maybe including options) + if (preg_match('|^[[:alnum:]_-]+/[[:alnum:]_-]+;?.*|', $reply, $matches)) { + $mime_type = $matches[0]; + } + } + } + + if (empty($mime_type)) { + // Fallback solution: try to guess the type by the file extension + // TODO: add more ... + // TODO: it has been suggested to delegate mimetype detection + // to apache but this has at least three issues: + // - works only with apache + // - needs file to be within the document tree + // - requires apache mod_magic + // TODO: can we use the registry for this on Windows? + // OTOH if the server is Windos the clients are likely to + // be Windows, too, and tend do ignore the Content-Type + // anyway (overriding it with information taken from + // the registry) + // TODO: have a seperate PEAR class for mimetype detection? + switch (strtolower(strrchr(basename($fspath), "."))) { + case ".html": + $mime_type = "text/html"; + break; + case ".gif": + $mime_type = "image/gif"; + break; + case ".jpg": + $mime_type = "image/jpeg"; + break; + default: + $mime_type = "application/octet-stream"; + break; + } + } + + return $mime_type; + } + + /** + * HEAD method handler + * + * @param array parameter passing array + * @return bool true on success + */ + function HEAD(&$options) + { + // get absolute fs path to requested resource + $fspath = $this->base . $options["path"]; + + // sanity check + if (!file_exists($fspath)) return false; + + // detect resource type + $options['mimetype'] = $this->_mimetype($fspath); + + // detect modification time + // see rfc2518, section 13.7 + // some clients seem to treat this as a reverse rule + // requiering a Last-Modified header if the getlastmodified header was set + $options['mtime'] = filemtime($fspath); + + // detect resource size + $options['size'] = filesize($fspath); + + return true; + } + + /** + * GET method handler + * + * @param array parameter passing array + * @return bool true on success + */ + function GET(&$options) + { + // get absolute fs path to requested resource + $fspath = $this->base . $options["path"]; + + // is this a collection? + if (is_dir($fspath)) { + return $this->GetDir($fspath, $options); + } + + // the header output is the same as for HEAD + if (!$this->HEAD($options)) { + return false; + } + + // no need to check result here, it is handled by the base class + $options['stream'] = fopen($fspath, "r"); + + return true; + } + + /** + * GET method handler for directories + * + * This is a very simple mod_index lookalike. + * See RFC 2518, Section 8.4 on GET/HEAD for collections + * + * @param string directory path + * @return void function has to handle HTTP response itself + */ + function GetDir($fspath, &$options) + { + $path = $this->_slashify($options["path"]); + if ($path != $options["path"]) { + header("Location: ".$this->base_uri.$path); + exit; + } + + // fixed width directory column format + $format = "%15s %-19s %-s\n"; + + if (!$this->_is_readable($fspath)) { + return false; + } + + $handle = opendir($fspath); + if (!$handle) { + return false; + } + + echo "Index of ".htmlspecialchars(urldecode($options['path']))."\n"; + + echo "

Index of ".htmlspecialchars($options['path'])."

\n"; + + echo "
";
+        printf($format, "Size", "Last modified", "Filename");
+        echo "
"; + + while ($filename = readdir($handle)) { + if ($filename != "." && $filename != "..") { + $fullpath = $fspath.$filename; + $name = htmlspecialchars($filename); + printf($format, + number_format(filesize($fullpath)), + strftime("%Y-%m-%d %H:%M:%S", filemtime($fullpath)), + ''.urldecode($name).''); + } + } + + echo "
"; + + closedir($handle); + + echo "\n"; + + exit; + } + + /** + * PUT method handler + * + * @param array parameter passing array + * @return bool true on success + */ + function PUT(&$options) + { + $fspath = $this->base . $options["path"]; + + $dir = dirname($fspath); + if (!file_exists($dir) || !is_dir($dir)) { + return "409 Conflict"; // TODO right status code for both? + } + + $options["new"] = ! file_exists($fspath); + + if ($options["new"] && !$this->_is_writable($dir)) { + return "403 Forbidden"; + } + if (!$options["new"] && !$this->_is_writable($fspath)) { + return "403 Forbidden"; + } + if (!$options["new"] && is_dir($fspath)) { + return "403 Forbidden"; + } + + $fp = fopen($fspath, "w"); + + return $fp; + } + + + /** + * MKCOL method handler + * + * @param array general parameter passing array + * @return bool true on success + */ + function MKCOL($options) + { + $path = $this->base .$options["path"]; + $parent = dirname($path); + $name = basename($path); + + if (!file_exists($parent)) { + return "409 Conflict"; + } + + if (!is_dir($parent)) { + return "403 Forbidden"; + } + + if ( file_exists($parent."/".$name) ) { + return "405 Method not allowed"; + } + + if (!empty($this->_SERVER["CONTENT_LENGTH"])) { // no body parsing yet + return "415 Unsupported media type"; + } + + $stat = mkdir($parent."/".$name, 0777); + if (!$stat) { + return "403 Forbidden"; + } + + return ("201 Created"); + } + + + /** + * DELETE method handler + * + * @param array general parameter passing array + * @return bool true on success + */ + function DELETE($options) + { + $path = $this->base . "/" .$options["path"]; + + if (!file_exists($path)) { + return "404 Not found"; + } + + if (is_dir($path)) { + $query = "DELETE FROM {$this->db_prefix}properties + WHERE path LIKE '".$this->_slashify($options["path"])."%'"; + mysql_query($query); + System::rm(array("-rf", $path)); + } else { + unlink($path); + } + $query = "DELETE FROM {$this->db_prefix}properties + WHERE path = '$options[path]'"; + mysql_query($query); + + return "204 No Content"; + } + + + /** + * MOVE method handler + * + * @param array general parameter passing array + * @return bool true on success + */ + function MOVE($options) + { + return $this->COPY($options, true); + } + + /** + * COPY method handler + * + * @param array general parameter passing array + * @return bool true on success + */ + function COPY($options, $del=false) + { + // TODO Property updates still broken (Litmus should detect this?) + + if (!empty($this->_SERVER["CONTENT_LENGTH"])) { // no body parsing yet + return "415 Unsupported media type"; + } + + // no copying to different WebDAV Servers yet + if (isset($options["dest_url"])) { + return "502 bad gateway"; + } + + $source = $this->base . $options["path"]; + if (!file_exists($source)) { + return "404 Not found"; + } + + if (is_dir($source)) { // resource is a collection + switch ($options["depth"]) { + case "infinity": // valid + break; + case "0": // valid for COPY only + if ($del) { // MOVE? + return "400 Bad request"; + } + break; + case "1": // invalid for both COPY and MOVE + default: + return "400 Bad request"; + } + } + + $dest = $this->base . $options["dest"]; + $destdir = dirname($dest); + + if (!file_exists($destdir) || !is_dir($destdir)) { + return "409 Conflict"; + } + + + $new = !file_exists($dest); + $existing_col = false; + + if (!$new) { + if ($del && is_dir($dest)) { + if (!$options["overwrite"]) { + return "412 precondition failed"; + } + $dest .= basename($source); + if (file_exists($dest)) { + $options["dest"] .= basename($source); + } else { + $new = true; + $existing_col = true; + } + } + } + + if (!$new) { + if ($options["overwrite"]) { + $stat = $this->DELETE(array("path" => $options["dest"])); + if (($stat{0} != "2") && (substr($stat, 0, 3) != "404")) { + return $stat; + } + } else { + return "412 precondition failed"; + } + } + + if ($del) { + if (!rename($source, $dest)) { + return "500 Internal server error"; + } + $destpath = $this->_unslashify($options["dest"]); + if (is_dir($source)) { + $query = "UPDATE {$this->db_prefix}properties + SET path = REPLACE(path, '".$options["path"]."', '".$destpath."') + WHERE path LIKE '".$this->_slashify($options["path"])."%'"; + mysql_query($query); + } + + $query = "UPDATE {$this->db_prefix}properties + SET path = '".$destpath."' + WHERE path = '".$options["path"]."'"; + mysql_query($query); + } else { + if (is_dir($source) && $options["depth"] == "infinity") { // no find for depth="0" + $files = System::find($source); + $files = array_reverse($files); + } else { + $files = array($source); + } + + if (!is_array($files) || empty($files)) { + return "500 Internal server error"; + } + + + foreach ($files as $file) { + if (is_dir($file)) { + $file = $this->_slashify($file); + } + + $destfile = str_replace($source, $dest, $file); + + if (is_dir($file)) { + if (!file_exists($destfile)) { + if (!$this->_is_writable(dirname($destfile))) { + return "403 Forbidden"; + } + if (!mkdir($destfile)) { + return "409 Conflict"; + } + } else if (!is_dir($destfile)) { + return "409 Conflict"; + } + } else { + + if (!copy($file, $destfile)) { + return "409 Conflict"; + } + } + } + + $query = "INSERT INTO {$this->db_prefix}properties + SELECT * + FROM {$this->db_prefix}properties + WHERE path = '".$options['path']."'"; + } + + return ($new && !$existing_col) ? "201 Created" : "204 No Content"; + } + + /** + * PROPPATCH method handler + * + * @param array general parameter passing array + * @return bool true on success + */ + function PROPPATCH(&$options) + { + global $prefs, $tab; + + $msg = ""; + $path = $options["path"]; + $dir = dirname($path)."/"; + $base = basename($path); + + foreach ($options["props"] as $key => $prop) { + if ($prop["ns"] == "DAV:") { + $options["props"][$key]['status'] = "403 Forbidden"; + } else { + if (isset($prop["val"])) { + $query = "REPLACE INTO {$this->db_prefix}properties + SET path = '$options[path]' + , name = '$prop[name]' + , ns= '$prop[ns]' + , value = '$prop[val]'"; + } else { + $query = "DELETE FROM {$this->db_prefix}properties + WHERE path = '$options[path]' + AND name = '$prop[name]' + AND ns = '$prop[ns]'"; + } + mysql_query($query); + } + } + + return ""; + } + + + /** + * LOCK method handler + * + * @param array general parameter passing array + * @return bool true on success + */ + function LOCK(&$options) + { + // get absolute fs path to requested resource + $fspath = $this->base . $options["path"]; + + // TODO recursive locks on directories not supported yet + // makes litmus test "32. lock_collection" fail + if (is_dir($fspath) && !empty($options["depth"])) { + return "409 Conflict"; + } + + $options["timeout"] = time()+300; // 5min. hardcoded + + if (isset($options["update"])) { // Lock Update + $where = "WHERE path = '$options[path]' AND token = '$options[update]'"; + + $query = "SELECT owner, exclusivelock FROM {$this->db_prefix}locks $where"; + $res = mysql_query($query); + $row = mysql_fetch_assoc($res); + mysql_free_result($res); + + if (is_array($row)) { + $query = "UPDATE {$this->db_prefix}locks + SET expires = '$options[timeout]' + , modified = ".time()." + $where"; + mysql_query($query); + + $options['owner'] = $row['owner']; + $options['scope'] = $row["exclusivelock"] ? "exclusive" : "shared"; + $options['type'] = $row["exclusivelock"] ? "write" : "read"; + + return true; + } else { + return false; + } + } + + $query = "INSERT INTO {$this->db_prefix}locks + SET token = '$options[locktoken]' + , path = '$options[path]' + , created = ".time()." + , modified = ".time()." + , owner = '$options[owner]' + , expires = '$options[timeout]' + , exclusivelock = " .($options['scope'] === "exclusive" ? "1" : "0") + ; + mysql_query($query); + + return mysql_affected_rows() ? "200 OK" : "409 Conflict"; + } + + /** + * UNLOCK method handler + * + * @param array general parameter passing array + * @return bool true on success + */ + function UNLOCK(&$options) + { + $query = "DELETE FROM {$this->db_prefix}locks + WHERE path = '$options[path]' + AND token = '$options[token]'"; + mysql_query($query); + + return mysql_affected_rows() ? "204 No Content" : "409 Conflict"; + } + + /** + * checkLock() helper + * + * @param string resource path to check for locks + * @return bool true on success + */ + function checkLock($path) + { + $result = false; + + $query = "SELECT owner, token, created, modified, expires, exclusivelock + FROM {$this->db_prefix}locks + WHERE path = '$path' + "; + $res = mysql_query($query); + + if ($res) { + $row = mysql_fetch_array($res); + mysql_free_result($res); + + if ($row) { + $result = array( "type" => "write", + "scope" => $row["exclusivelock"] ? "exclusive" : "shared", + "depth" => 0, + "owner" => $row['owner'], + "token" => $row['token'], + "created" => $row['created'], + "modified" => $row['modified'], + "expires" => $row['expires'] + ); + } + } + + return $result; + } + + + /** + * create database tables for property and lock storage + * + * @param void + * @return bool true on success + */ + function create_database() + { + // TODO + return false; + } +} + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * indent-tabs-mode:nil + * End: + */ diff --git a/egw-pear/HTTP/WebDAV/Tools/_parse_lockinfo.php b/egw-pear/HTTP/WebDAV/Tools/_parse_lockinfo.php new file mode 100644 index 0000000000..34afa0ace4 --- /dev/null +++ b/egw-pear/HTTP/WebDAV/Tools/_parse_lockinfo.php @@ -0,0 +1,251 @@ + + * @version @package-version@ + */ +class _parse_lockinfo +{ + /** + * success state flag + * + * @var bool + * @access public + */ + var $success = false; + + /** + * lock type, currently only "write" + * + * @var string + * @access public + */ + var $locktype = ""; + + /** + * lock scope, "shared" or "exclusive" + * + * @var string + * @access public + */ + var $lockscope = ""; + + /** + * lock owner information + * + * @var string + * @access public + */ + var $owner = ""; + + /** + * flag that is set during lock owner read + * + * @var bool + * @access private + */ + var $collect_owner = false; + + /** + * constructor + * + * @param string path of stream to read + * @access public + */ + function _parse_lockinfo($path) + { + // we assume success unless problems occur + $this->success = true; + + // remember if any input was parsed + $had_input = false; + + // open stream + $f_in = fopen($path, "r"); + if (!$f_in) { + $this->success = false; + return; + } + + // create namespace aware parser + $xml_parser = xml_parser_create_ns("UTF-8", " "); + + // set tag and data handlers + xml_set_element_handler($xml_parser, + array(&$this, "_startElement"), + array(&$this, "_endElement")); + xml_set_character_data_handler($xml_parser, + array(&$this, "_data")); + + // we want a case sensitive parser + xml_parser_set_option($xml_parser, + XML_OPTION_CASE_FOLDING, false); + + // parse input + while ($this->success && !feof($f_in)) { + $line = fgets($f_in); + if (is_string($line)) { + $had_input = true; + $this->success &= xml_parse($xml_parser, $line, false); + } + } + + // finish parsing + if ($had_input) { + $this->success &= xml_parse($xml_parser, "", true); + } + + // check if required tags where found + $this->success &= !empty($this->locktype); + $this->success &= !empty($this->lockscope); + + // free parser resource + xml_parser_free($xml_parser); + + // close input stream + fclose($f_in); + } + + + /** + * tag start handler + * + * @param resource parser + * @param string tag name + * @param array tag attributes + * @return void + * @access private + */ + function _startElement($parser, $name, $attrs) + { + // namespace handling + if (strstr($name, " ")) { + list($ns, $tag) = explode(" ", $name); + } else { + $ns = ""; + $tag = $name; + } + + + if ($this->collect_owner) { + // everything within the tag needs to be collected + $ns_short = ""; + $ns_attr = ""; + if ($ns) { + if ($ns == "DAV:") { + $ns_short = "D:"; + } else { + $ns_attr = " xmlns='$ns'"; + } + } + $this->owner .= "<$ns_short$tag$ns_attr>"; + } else if ($ns == "DAV:") { + // parse only the essential tags + switch ($tag) { + case "write": + $this->locktype = $tag; + break; + case "exclusive": + case "shared": + $this->lockscope = $tag; + break; + case "owner": + $this->collect_owner = true; + break; + } + } + } + + /** + * data handler + * + * @param resource parser + * @param string data + * @return void + * @access private + */ + function _data($parser, $data) + { + // only the tag has data content + if ($this->collect_owner) { + $this->owner .= $data; + } + } + + /** + * tag end handler + * + * @param resource parser + * @param string tag name + * @return void + * @access private + */ + function _endElement($parser, $name) + { + // namespace handling + if (strstr($name, " ")) { + list($ns, $tag) = explode(" ", $name); + } else { + $ns = ""; + $tag = $name; + } + + // finished? + if (($ns == "DAV:") && ($tag == "owner")) { + $this->collect_owner = false; + } + + // within we have to collect everything + if ($this->collect_owner) { + $ns_short = ""; + $ns_attr = ""; + if ($ns) { + if ($ns == "DAV:") { + $ns_short = "D:"; + } else { + $ns_attr = " xmlns='$ns'"; + } + } + $this->owner .= ""; + } + } +} + +?> diff --git a/egw-pear/HTTP/WebDAV/Tools/_parse_propfind.php b/egw-pear/HTTP/WebDAV/Tools/_parse_propfind.php new file mode 100644 index 0000000000..30c2df6db7 --- /dev/null +++ b/egw-pear/HTTP/WebDAV/Tools/_parse_propfind.php @@ -0,0 +1,285 @@ + + * @version @package-version@ + */ +class _parse_propfind +{ + /** + * success state flag + * + * @var bool + * @access public + */ + var $success = false; + + /** + * found properties are collected here + * + * @var array + * @access public + */ + var $props = false; + + /** + * found (CalDAV) filters are collected here + * + * @var array + * @access public + */ + var $filters = false; + + /** + * found other tags, eg. CalDAV calendar-multiget href's + * + * @var array + * @access public + */ + var $other = false; + + /** + * what we are currently parsing: props or filters + * + * @var array + * @access private + */ + var $use = 'props'; + + /** + * Root tag, usually 'propfind' for PROPFIND, but can be eg. 'calendar-query' or 'calendar-multiget' for CalDAV REPORT + * + * @var array with keys 'name' and 'ns' + */ + var $root; + + /** + * internal tag nesting depth counter + * + * @var int + * @access private + */ + var $depth = 0; + + /** + * On return whole request, if $store_request == true was specified in constructor + * + * @var string + */ + var $request; + + /** + * constructor + * + * @access public + * @param string $path + * @param boolean $store_request=false if true whole request data will be made available in $this->request + */ + function _parse_propfind($path, $store_request=false) + { + // success state flag + $this->success = true; + + // property storage array + $this->props = array(); + + // internal tag depth counter + $this->depth = 0; + + // remember if any input was parsed + $had_input = false; + + // open input stream + $f_in = fopen($path, "r"); + if (!$f_in) { + $this->success = false; + return; + } + + // create XML parser + $xml_parser = xml_parser_create_ns("UTF-8", " "); + + // set tag and data handlers + xml_set_element_handler($xml_parser, + array(&$this, "_startElement"), + array(&$this, "_endElement")); + + xml_set_character_data_handler($xml_parser, + array(&$this,'_charData') + ); + + // we want a case sensitive parser + xml_parser_set_option($xml_parser, + XML_OPTION_CASE_FOLDING, false); + + // parse input + while ($this->success && !feof($f_in)) { + $line = fgets($f_in); + if ($store_request) $this->request .= $line; + if (is_string($line)) { + $had_input = true; + $this->success &= xml_parse($xml_parser, $line, false); + } + } + + // finish parsing + if ($had_input) { + $this->success &= xml_parse($xml_parser, "", true); + } + + // free parser + xml_parser_free($xml_parser); + + // close input stream + fclose($f_in); + + // if no input was parsed it was a request + if(!count($this->props)) $this->props = "all"; // default + } + + + /** + * start tag handler + * + * @access private + * @param resource parser + * @param string tag name + * @param array tag attributes + */ + function _startElement($parser, $name, $attrs) + { + // name space handling + if (strstr($name, " ")) { + list($ns, $tag) = explode(" ", $name); + if ($ns == "") + $this->success = false; + } else { + $ns = ""; + $tag = $name; + } + + // record root tag + if ($this->depth == 0) { + $this->root = array('name' => $tag, 'xmlns' => $ns, 'attrs' => $attrs); + } + // special tags at level 1: and + if ($this->depth == 1) { + $this->use = 'props'; + switch ($tag) + { + case "allprop": + $this->props = "all"; + break; + case "propname": + $this->props = "names"; + break; + case 'prop': + break; + case 'filter': + $this->use = 'filters'; + $this->filters['attrs'] = $attrs; // need attrs eg. + break; + default: + $this->use = 'other'; + break; + } + } + //echo "$this->depth: use=$this->use $ns:$tag attrs=".array2string($attrs)."\n"; + + // requested properties are found at level 2 + // CalDAV filters can be at deeper levels too and we need the attrs, same for other tags (eg. multiget href's) + if ($this->depth == 2 || $this->use == 'filters' && $this->depth >= 2 || $this->use == 'other' || + $this->use == 'props' && $this->depth >= 2) { + $prop = array("name" => $tag); + if ($ns) + $prop["xmlns"] = $ns; + if ($this->use != 'props' || $this->depth > 2) { + $prop['attrs'] = $attrs; + $prop['depth'] = $this->depth; + } + // collect sub-elements of props in the original props children attribute + // eg. required for CalDAV + if ($this->use == 'props' && $this->depth > 2) + { + $this->last_prop['children'][$tag] = $prop; + } + else + { + // this can happen if we have allprop and prop in one propfind: + // , eg. blah is not automatic returned by allprop + if (!is_array($this->{$this->use}) && $this->{$this->use}) $this->{$this->use} = array($this->{$this->use}); + $this->{$this->use}[] =& $prop; + $this->last_prop =& $prop; + unset($prop); + } + } + + // increment depth count + $this->depth++; + } + + + /** + * end tag handler + * + * @access private + * @param resource parser + * @param string tag name + */ + function _endElement($parser, $name) + { + // here we only need to decrement the depth count + $this->depth--; + } + + + /** + * char data handler for non prop tags, eg. href's in CalDAV multiget, or filter contents + * + * @access private + * @param resource parser + * @param string character data + */ + function _charData($parser, $data) + { + if ($this->use != 'props' && ($n = count($this->{$this->use})) && ($data = trim($data))) { + $this->{$this->use}[$n-1]['data'] = $data; + } + } +} diff --git a/egw-pear/HTTP/WebDAV/Tools/_parse_proppatch.php b/egw-pear/HTTP/WebDAV/Tools/_parse_proppatch.php new file mode 100644 index 0000000000..25a0bbef9a --- /dev/null +++ b/egw-pear/HTTP/WebDAV/Tools/_parse_proppatch.php @@ -0,0 +1,246 @@ + + * @version @package-version@ + */ +class _parse_proppatch +{ + /** + * + * + * @var + * @access + */ + var $success; + + /** + * + * + * @var + * @access + */ + var $props; + + /** + * + * + * @var + * @access + */ + var $depth; + + /** + * + * + * @var + * @access + */ + var $mode; + + /** + * + * + * @var + * @access + */ + var $current; + + /** + * On return whole request, if $store_request == true was specified in constructor + * + * @var string + */ + var $request; + + /** + * constructor + * + * @param string path of input stream + * @param boolean $store_request=false if true whole request data will be made available in $this->request + * @access public + */ + function _parse_proppatch($path, $store_request=false) + { + $this->success = true; + + $this->depth = 0; + $this->props = array(); + $had_input = false; + + $f_in = fopen($path, "r"); + if (!$f_in) { + $this->success = false; + return; + } + + $xml_parser = xml_parser_create_ns("UTF-8", " "); + + xml_set_element_handler($xml_parser, + array(&$this, "_startElement"), + array(&$this, "_endElement")); + + xml_set_character_data_handler($xml_parser, + array(&$this, "_data")); + + xml_parser_set_option($xml_parser, + XML_OPTION_CASE_FOLDING, false); + + while($this->success && !feof($f_in)) { + $line = fgets($f_in); + if ($store_request) $this->request .= $line; + if (is_string($line)) { + $had_input = true; + $this->success &= xml_parse($xml_parser, $line, false); + } + } + + if($had_input) { + $this->success &= xml_parse($xml_parser, "", true); + } + + xml_parser_free($xml_parser); + + fclose($f_in); + } + + /** + * tag start handler + * + * @param resource parser + * @param string tag name + * @param array tag attributes + * @return void + * @access private + */ + function _startElement($parser, $name, $attrs) + { + if (strstr($name, " ")) { + list($ns, $tag) = explode(" ", $name); + if ($ns == "") + $this->success = false; + } else { + $ns = ""; + $tag = $name; + } + + if ($this->depth == 1) { + $this->mode = $tag; + } + + if ($this->depth == 3) { + $prop = array("name" => $tag); + $this->current = array("name" => $tag, "ns" => $ns, "status"=> 200); + if ($this->mode == "set") { + $this->current["val"] = ""; // default set val + } + } + + if ($this->depth >= 4) { + $this->current["val"] .= "<$tag"; + if (isset($attr)) { + foreach ($attr as $key => $val) { + $this->current["val"] .= ' '.$key.'="'.str_replace('"','"', $val).'"'; + } + } + $this->current["val"] .= ">"; + } + + + + $this->depth++; + } + + /** + * tag end handler + * + * @param resource parser + * @param string tag name + * @return void + * @access private + */ + function _endElement($parser, $name) + { + if (strstr($name, " ")) { + list($ns, $tag) = explode(" ", $name); + if ($ns == "") + $this->success = false; + } else { + $ns = ""; + $tag = $name; + } + + $this->depth--; + + if ($this->depth >= 4) { + $this->current["val"] .= ""; + } + + if ($this->depth == 3) { + if (isset($this->current)) { + $this->props[] = $this->current; + unset($this->current); + } + } + } + + /** + * input data handler + * + * @param resource parser + * @param string data + * @return void + * @access private + */ + function _data($parser, $data) + { + if (isset($this->current)) { + $this->current["val"] .= $data; + } + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * indent-tabs-mode:nil + * End: + */ diff --git a/egw-pear/setup/setup.inc.php b/egw-pear/setup/setup.inc.php new file mode 100644 index 0000000000..1d3a8ccb3d --- /dev/null +++ b/egw-pear/setup/setup.inc.php @@ -0,0 +1,44 @@ + 'PEAR - PHP Extension and Application Repository', + 'url' => 'http://pear.php.net', +); +$setup_info['egw-pear']['license'] = 'PHP'; +$setup_info['egw-pear']['description'] = + 'A place for PEAR modules modified for eGroupWare.'; + +$setup_info['egw-pear']['note'] = + 'This application is a place for PEAR modules used by eGroupWare, which are NOT YET available from pear, + because we patched them somehow and the PEAR modules are not released upstream. + This application is under the LGPL license because the GPL is not compatible with the PHP license. + If the modules are available from PEAR they do NOT belong here anymore.'; + +$setup_info['egw-pear']['maintainer'] = array( + 'name' => 'eGroupWare coreteam', + 'email' => 'egroupware-developers@lists.sourceforge.net', +); + +// installation checks for egw-pear +$setup_info['egw-pear']['check_install'] = array( + // we need pear itself to be installed + '' => array( + 'func' => 'pear_check', + 'from' => 'FMail', + ), +); diff --git a/emailadmin/doc/dbmail.schema b/emailadmin/doc/dbmail.schema new file mode 100644 index 0000000000..a1e3469371 --- /dev/null +++ b/emailadmin/doc/dbmail.schema @@ -0,0 +1,71 @@ +# +# dbmail-ldap v3 directory schema +# +# Based on the Qmail schema +# Modified for dbmail by Paul Stevens +# Modified for dbmail by Lars Kneschke too +# +# This schema depends on: +# - core.schema +# - cosine.schema +# - nis.schema +# +# This schema conflicts with +# - qmailuser.schema + +# Attribute Type Definitions + +attributetype ( 1.3.6.1.4.1.12340.6.2.1.1 NAME 'mailQuota' + DESC 'The amount of space the user can use until all further messages get bounced.' + SYNTAX 1.3.6.1.4.1.1466.115.121.1.44 + SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.12340.6.2.1.2 NAME 'mailForwardingAddress' + DESC 'Address(es) to forward all incoming messages to.' + EQUALITY caseIgnoreIA5Match + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) + +attributetype ( 1.3.6.1.4.1.12340.6.2.1.3 NAME 'mailHost' + DESC 'Name or address of the MTA host to use for recipient' + EQUALITY caseIgnoreIA5Match + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) + +attributetype ( 1.3.6.1.4.1.12340.6.2.1.4 NAME 'dbmailUID' + DESC 'UID of the user on the mailsystem' + EQUALITY caseIgnoreIA5Match + SUBSTR caseIgnoreIA5SubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 + SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.12340.6.2.1.5 NAME 'dbmailGID' + DESC 'GID of the user on the mailsystem' + EQUALITY caseIgnoreIA5Match + SUBSTR caseIgnoreIA5SubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 + SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.12340.6.2.1.6 NAME 'mailAlternateAddress' + DESC 'Secondary (alias) mailaddresses for the same user' + EQUALITY caseIgnoreIA5Match + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} ) + +attributetype ( 1.3.6.1.4.1.12340.6.2.1.7 NAME 'deliveryMode' + DESC 'multi field entries of: normal, forwardonly' + EQUALITY caseIgnoreMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.44 SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.12340.6.2.1.8 NAME 'accountStatus' + DESC 'The status of a user account: active, disabled' + EQUALITY caseIgnoreMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.44 SINGLE-VALUE ) + +# Object Class Definitions + +objectclass ( 1.3.6.1.4.1.12340.6.2.2.1 NAME 'dbmailUser' + DESC 'DBMail-LDAP User' SUP top AUXILIARY + MUST ( uid $ mail ) + MAY ( userPassword $ uidNumber $ gidNumber $ mailQuota $ mailForwardingAddress $ mailHost $ mailAlternateAddress $ dbmailUID $ dbmailGID $ deliveryMode $ accountStatus ) ) + +objectclass ( 1.3.6.1.4.1.12340.6.2.2.2 NAME 'dbmailForwardingAddress' + DESC 'DBMail-LDAP Forwarding Address' SUP top AUXILIARY + MUST ( mail $ mailForwardingAddress ) ) diff --git a/emailadmin/doc/dovecot_checkpassword_ads.php b/emailadmin/doc/dovecot_checkpassword_ads.php new file mode 100755 index 0000000000..260194141b --- /dev/null +++ b/emailadmin/doc/dovecot_checkpassword_ads.php @@ -0,0 +1,330 @@ +#!/usr/bin/php -Cq + 'samaccountname', // do NOT remove! +// '%n' => 'uidnumber', +// '%h' => 'mailmessagestore', + '%q' => '{quota:}proxyaddresses', + '%x' => 'dn', +); +$user_name = '%u'; // '%u@%d'; +$user_home = '/var/dovecot/imap/gruene/%u'; //'/var/dovecot/imap/%d/%u'; // mailbox location +$extra = array( + 'userdb_quota_rule' => '*:bytes=%q', +/* only for director + 'proxy' => 'Y', + 'nologin' => 'Y', + 'nopassword' => 'Y', +*/ +); +// get host by not set l attribute +/* only for director +$host_filter = 'o=%d'; +$host_base = 'dc=egroupware'; +$host_attr = 'l'; +$host_default = '10.40.8.200'; +*/ + +// to return Dovecot extra system_groups_user +$group_base = $ldap_base; +$group_filter = '(&(objectCategory=group)(member=%x))'; +$group_attr = 'cn'; +$group_append = ''; //'@%d'; + +$master_dn = $bind_dn; //"cn=admin,dc=egroupware"; +//$domain_master_dn = "cn=admin,o=%d,dc=egroupware"; + +ini_set('display_errors',false); +error_reporting(E_ALL & ~E_NOTICE); +if ($log) ini_set('error_log',$log); + +if ($_SERVER['argc'] < 2) +{ + fwrite(STDERR,"\nUsage: {$_SERVER['argv'][0]} prog-to-exec\n\n"); + fwrite(STDERR,"To test run:\n"); + fwrite(STDERR,"echo -en 'username\\0000''password\\0000' | {$_SERVER['argv'][0]} env 3<&0 ; echo $?\n"); + fwrite(STDERR,"echo -en 'username\\0000' | AUTHORIZED=1 {$_SERVER['argv'][0]} env 3<&0 ; echo $?\n"); + fwrite(STDERR,"echo -en '(dovecode-admin@domain|dovecot|cyrus)\\0000''master-password\\0000' | AUTH_LOGIN_USER=username {$_SERVER['argv'][0]} env 3<&0 ; echo $?\n\n"); + exit(2); +} + +list($username,$password) = explode("\0",file_get_contents('php://fd/3')); +if (isset($_SERVER['AUTH_LOGIN_USER'])) +{ + $master = $username; + $username = $_SERVER['AUTH_LOGIN_USER']; +} +//error_log("dovecot_checkpassword '{$_SERVER['argv'][1]}': username='$username', password='$password', master='$master'"); + +$ds = ldap_connect($ldap_uri); +if ($version) ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, $version); +if ($use_tls) ldap_start_tls($ds); + +if (!@ldap_bind($ds, $bind_dn, $bind_pw)) +{ + error_log("Can't connect to LDAP server $ldap_uri!"); + exit(111); // 111 = temporary problem +} +list(,$domain) = explode('@',$username); +if (preg_match('/^(.*)\.imapc$/',$domain,$matches)) +{ + $domain = $matches[1]; + + $username = explode('.', $username); + array_pop($username); + $username = implode('.',$username); + + $user_home = '/var/tmp/imapc-%d/%s'; + $extra = array( + 'userdb_mail' => 'imapc:/var/tmp/imapc-'.$domain.'/'.$username, + //'userdb_imapc_password' => $password, + //'userdb_imapc_host' => 'hugo.de', + ); +} + +$replace = array( + '%d' => $domain, + '%s' => $username, +); +$base = strtr($search_base, $replace); + +if (($passdb_query = !isset($_SERVER['AUTHORIZED']) || $_SERVER['AUTHORIZED'] != 1)) +{ + $filter = $passdb_filter; + + // authenticate with master user/password + // master user name is hardcoded "dovecot", "cyrus" or "dovecot-admin@domain" and mapped currently to cn=admin,[o=domain,]dc=egroupware + if (isset($master)) + { + list($n,$d) = explode('@', $master); + if (!($n === 'dovecot-admin' && $d === $domain || in_array($master,array('dovecot','cyrus')))) + { + // no valid master-user for given domain + exit(1); + } + $dn = $d ? strtr($domain_master_dn,array('%d'=>$domain)) : $master_dn; + if (!@ldap_bind($ds, $dn, $password)) + { + if ($log_verbose) error_log("Can't bind as '$dn' with password '$password'! Authentication as master '$master' for user '$username' failed!"); + exit(111); // 111 = temporary problem + } + if ($log_verbose) error_log("Authentication as master '$master' for user '$username' succeeded!"); + $passdb_query = false; + $filter = $userdb_filter; + } +} +else +{ + $filter = $userdb_filter; + putenv('AUTHORIZED=2'); +} +$filter = strtr($filter, quote($replace)); + +// remove prefixes eg. "{quota:}proxyaddresses" +$attrs = $user_attrs; +foreach($attrs as &$a) if ($a[0] == '{') list(,$a) = explode('}', $a); + +if (!($sr = ldap_search($ds, $base, $filter, array_values($attrs)))) +{ + error_log("Error ldap_search(\$ds, '$base', '$filter')!"); + exit(111); // 111 = temporary problem +} +$entries = ldap_get_entries($ds, $sr); + +if (!$entries['count']) +{ + if ($log_verbose) error_log("User '$username' NOT found!"); + exit(3); +} + +if ($entries['count'] > 1) +{ + // should not happen for passdb, but could happen for aliases ... + error_log("Error ldap_search(\$ds, '$base', '$filter') returned more then one user!"); + exit(111); // 111 = temporary problem +} +//print_r($entries); + +if ($passdb_query) +{ + // now authenticate user by trying to bind to found dn with given password + if (!@ldap_bind($ds, $entries[0]['dn'], $password)) + { + if ($log_verbose) error_log("Can't bind as '{$entries[0]['dn']}' with password '$password'! Authentication for user '$username' failed!"); + exit(1); + } + if ($log_verbose) error_log("Successfull authentication user '$username' dn='{$entries[0]['dn']}'."); +} +else // user-db query, no authentication +{ + if ($log_verbose) error_log("User-db query for user '$username' dn='{$entries[0]['dn']}'."); +} + +// add additional placeholders from $user_attrs +foreach($user_attrs as $placeholder => $attr) +{ + if ($attr[0] == '{') // prefix given --> ignore all values without and remove it + { + list($prefix, $attr) = explode('}', substr($attr, 1)); + foreach($entries[0][$attr] as $key => $value) + { + if ($key === 'count') continue; + if (strpos($value, $prefix) !== 0) continue; + $replace[$placeholder] = substr($value, strlen($prefix)); + break; + } + } + else + { + $replace[$placeholder] = is_array($entries[0][$attr]) ? $entries[0][$attr][0] : $entries[0][$attr]; + } +} + +// search memberships +if (isset($group_base) && $group_filter && $group_attr) +{ + $base = strtr($group_base, $replace); + $filter = strtr($group_filter, quote($replace)); + $append = strtr($group_append, $replace); + if (($sr = ldap_search($ds, $base, $filter, array($group_attr))) && + ($groups = ldap_get_entries($ds, $sr)) && $groups['count']) + { + //print_r($groups); + $system_groups_user = array(); + foreach($groups as $key => $group) + { + if ($key === 'count') continue; + $system_groups_user[] = $group[$group_attr][0].$append; + } + $extra['system_groups_user'] = implode(',', $system_groups_user); // todo: check separator + } + else + { + error_log("Error searching for memberships ldap_search(\$ds, '$base', '$filter')!"); + } +} + +// set host attribute for director to old imap +if (isset($host_base) && isset($host_filter)) +{ + if (!($sr = ldap_search($ds, $host_base, $filter=strtr($host_filter, quote($replace)), array($host_attr)))) + { + error_log("Error ldap_search(\$ds, '$host_base', '$filter')!"); + exit(111); // 111 = temporary problem + } + $entries = ldap_get_entries($ds, $sr); + if ($entries['count'] && !isset($entries[0][$host_attr])) + { + $extra['host'] = $host_default; + } +} +// close ldap connection +ldap_unbind($ds); + +// build command to run +array_shift($_SERVER['argv']); +$cmd = array_shift($_SERVER['argv']); +foreach($_SERVER['argv'] as $arg) +{ + $cmd .= ' '.escapeshellarg($arg); +} + +// setting USER, HOME, EXTRA +putenv('USER='.strtr($user_name, $replace)); +if ($user_home) putenv('HOME='.strtr($user_home, $replace)); +if ($extra) +{ + foreach($extra as $name => $value) + { + if (($pos = strpos($value,'%')) !== false) + { + // check if replacement is set, otherwise skip whole extra-value + if (!isset($replace[substr($value,$pos,2)])) + { + unset($extra[$name]); + continue; + } + $value = strtr($value,$replace); + } + putenv($name.'='.$value); + } + putenv('EXTRA='.implode(' ', array_keys($extra))); +} + +// call given command and exit with it's exit-status +passthru($cmd, $ret); + +exit($ret); + +/** + * escapes a string for use in searchfilters meant for ldap_search. + * + * Escaped Characters are: '*', '(', ')', ' ', '\', NUL + * It's actually a PHP-Bug, that we have to escape space. + * For all other Characters, refer to RFC2254. + * + * @param string|array $string either a string to be escaped, or an array of values to be escaped + * @return string + */ +function quote($string) +{ + return str_replace(array('\\','*','(',')','\0',' '),array('\\\\','\*','\(','\)','\\0','\20'),$string); +} diff --git a/emailadmin/doc/main.cf b/emailadmin/doc/main.cf new file mode 100644 index 0000000000..e7c741dd90 --- /dev/null +++ b/emailadmin/doc/main.cf @@ -0,0 +1,754 @@ +# Global Postfix configuration file. This file lists only a subset +# of all 300+ parameters. See the sample-xxx.cf files for a full list. +# +# The general format is lines with parameter = value pairs. Lines +# that begin with whitespace continue the previous line. A value can +# contain references to other $names or ${name}s. +# +# NOTE - CHANGE NO MORE THAN 2-3 PARAMETERS AT A TIME, AND TEST IF +# POSTFIX STILL WORKS AFTER EVERY CHANGE. + +# SOFT BOUNCE +# +# The soft_bounce parameter provides a limited safety net for +# testing. When soft_bounce is enabled, mail will remain queued that +# would otherwise bounce. This parameter disables locally-generated +# bounces, and prevents the SMTP server from rejecting mail permanently +# (by changing 5xx replies into 4xx replies). However, soft_bounce +# is no cure for address rewriting mistakes or mail routing mistakes. +# +#soft_bounce = no + +# LOCAL PATHNAME INFORMATION +# +# The queue_directory specifies the location of the Postfix queue. +# This is also the root directory of Postfix daemons that run chrooted. +# See the files in examples/chroot-setup for setting up Postfix chroot +# environments on different UNIX systems. +# +queue_directory = /var/spool/postfix + +# The command_directory parameter specifies the location of all +# postXXX commands. +# +command_directory = /usr/sbin + +# The daemon_directory parameter specifies the location of all Postfix +# daemon programs (i.e. programs listed in the master.cf file). This +# directory must be owned by root. +# +daemon_directory = /usr/lib/postfix + +# QUEUE AND PROCESS OWNERSHIP +# +# The mail_owner parameter specifies the owner of the Postfix queue +# and of most Postfix daemon processes. Specify the name of a user +# account THAT DOES NOT SHARE ITS USER OR GROUP ID WITH OTHER ACCOUNTS +# AND THAT OWNS NO OTHER FILES OR PROCESSES ON THE SYSTEM. In +# particular, don't specify nobody or daemon. PLEASE USE A DEDICATED +# USER. +# +mail_owner = postfix + +# The default_privs parameter specifies the default rights used by +# the local delivery agent for delivery to external file or command. +# These rights are used in the absence of a recipient user context. +# DO NOT SPECIFY A PRIVILEGED USER OR THE POSTFIX OWNER. +# +#default_privs = nobody + +# INTERNET HOST AND DOMAIN NAMES +# +# The myhostname parameter specifies the internet hostname of this +# mail system. The default is to use the fully-qualified domain name +# from gethostname(). $myhostname is used as a default value for many +# other configuration parameters. +# +#myhostname = host.domain.tld +#myhostname = virtual.domain.tld + +# The mydomain parameter specifies the local internet domain name. +# The default is to use $myhostname minus the first component. +# $mydomain is used as a default value for many other configuration +# parameters. +# +#mydomain = domain.tld + +# SENDING MAIL +# +# The myorigin parameter specifies the domain that locally-posted +# mail appears to come from. The default is to append $myhostname, +# which is fine for small sites. If you run a domain with multiple +# machines, you should (1) change this to $mydomain and (2) set up +# a domain-wide alias database that aliases each user to +# user@that.users.mailhost. +# +# For the sake of consistency between sender and recipient addresses, +# myorigin also specifies the default domain name that is appended +# to recipient addresses that have no @domain part. +# +#myorigin = $myhostname +#myorigin = $mydomain + +# RECEIVING MAIL + +# The inet_interfaces parameter specifies the network interface +# addresses that this mail system receives mail on. By default, +# the software claims all active interfaces on the machine. The +# parameter also controls delivery of mail to user@[ip.address]. +# +# See also the proxy_interfaces parameter, for network addresses that +# are forwarded to us via a proxy or network address translator. +# +# Note: you need to stop/start Postfix when this parameter changes. +# +#inet_interfaces = all +#inet_interfaces = $myhostname +#inet_interfaces = $myhostname, localhost + +# The proxy_interfaces parameter specifies the network interface +# addresses that this mail system receives mail on by way of a +# proxy or network address translation unit. This setting extends +# the address list specified with the inet_interfaces parameter. +# +# You must specify your proxy/NAT addresses when your system is a +# backup MX host for other domains, otherwise mail delivery loops +# will happen when the primary MX host is down. +# +#proxy_interfaces = +#proxy_interfaces = 1.2.3.4 + +# The mydestination parameter specifies the list of domains that this +# machine considers itself the final destination for. +# +# These domains are routed to the delivery agent specified with the +# local_transport parameter setting. By default, that is the UNIX +# compatible delivery agent that lookups all recipients in /etc/passwd +# and /etc/aliases or their equivalent. +# +# The default is $myhostname + localhost.$mydomain. On a mail domain +# gateway, you should also include $mydomain. +# +# Do not specify the names of virtual domains - those domains are +# specified elsewhere (see sample-virtual.cf). +# +# Do not specify the names of domains that this machine is backup MX +# host for. Specify those names via the relay_domains settings for +# the SMTP server, or use permit_mx_backup if you are lazy (see +# sample-smtpd.cf). +# +# The local machine is always the final destination for mail addressed +# to user@[the.net.work.address] of an interface that the mail system +# receives mail on (see the inet_interfaces parameter). +# +# Specify a list of host or domain names, /file/name or type:table +# patterns, separated by commas and/or whitespace. A /file/name +# pattern is replaced by its contents; a type:table is matched when +# a name matches a lookup key (the right-hand side is ignored). +# Continue long lines by starting the next line with whitespace. +# +# DO NOT LIST RELAY DESTINATIONS IN MYDESTINATION. +# SPECIFY RELAY DESTINATIONS IN RELAY_DOMAINS. +# +# See also below, section "REJECTING MAIL FOR UNKNOWN LOCAL USERS". +# +#mydestination = $myhostname, localhost.$mydomain +#mydestination = $myhostname, localhost.$mydomain $mydomain +#mydestination = $myhostname, localhost.$mydomain, $mydomain, +# mail.$mydomain, www.$mydomain, ftp.$mydomain +mydestination = $myhostname, localhost.$mydomain $mydomain, + kneschke.de, phpgw.de, egroupware.org, linux-at-work.de, lists.kneschke.de + +# REJECTING MAIL FOR UNKNOWN LOCAL USERS +# +# The local_recipient_maps parameter specifies optional lookup tables +# with all names or addresses of users that are local with respect +# to $mydestination and $inet_interfaces. +# +# If this parameter is defined, then the SMTP server will reject +# mail for unknown local users. This parameter is defined by default. +# +# To turn off local recipient checking in the SMTP server, specify +# local_recipient_maps = (i.e. empty). +# +# The default setting assumes that you use the default Postfix local +# delivery agent for local delivery. You need to update the +# local_recipient_maps setting if: +# +# - You define $mydestination domain recipients in files other than +# /etc/passwd, /etc/aliases, or the $virtual_alias_maps files. +# For example, you define $mydestination domain recipients in +# the $virtual_mailbox_maps files. +# +# - You redefine the local delivery agent in master.cf. +# +# - You redefine the "local_transport" setting in main.cf. +# +# - You use the "luser_relay", "mailbox_transport", or "fallback_transport" +# feature of the Postfix local delivery agent (see sample-local.cf). +# +# Details are described in the LOCAL_RECIPIENT_README file. +# +# Beware: if the Postfix SMTP server runs chrooted, you probably have +# to access the passwd file via the proxymap service, in order to +# overcome chroot restrictions. The alternative, having a copy of +# the system passwd file in the chroot jail is just not practical. +# +# The right-hand side of the lookup tables is conveniently ignored. +# In the left-hand side, specify a bare username, an @domain.tld +# wild-card, or specify a user@domain.tld address. +# +#local_recipient_maps = unix:passwd.byname $alias_maps +#local_recipient_maps = proxy:unix:passwd.byname $alias_maps +#local_recipient_maps = + +# The unknown_local_recipient_reject_code specifies the SMTP server +# response code when a recipient domain matches $mydestination or +# $inet_interfaces, while $local_recipient_maps is non-empty and the +# recipient address or address local-part is not found. +# +# The default setting is 550 (reject mail) but it is safer to start +# with 450 (try again later) until you are certain that your +# local_recipient_maps settings are OK. +# +unknown_local_recipient_reject_code = 550 +#unknown_local_recipient_reject_code = 450 + +# TRUST AND RELAY CONTROL + +# The mynetworks parameter specifies the list of "trusted" SMTP +# clients that have more privileges than "strangers". +# +# In particular, "trusted" SMTP clients are allowed to relay mail +# through Postfix. See the smtpd_recipient_restrictions parameter +# in file sample-smtpd.cf. +# +# You can specify the list of "trusted" network addresses by hand +# or you can let Postfix do it for you (which is the default). +# +# By default (mynetworks_style = subnet), Postfix "trusts" SMTP +# clients in the same IP subnetworks as the local machine. +# On Linux, this does works correctly only with interfaces specified +# with the "ifconfig" command. +# +# Specify "mynetworks_style = class" when Postfix should "trust" SMTP +# clients in the same IP class A/B/C networks as the local machine. +# Don't do this with a dialup site - it would cause Postfix to "trust" +# your entire provider's network. Instead, specify an explicit +# mynetworks list by hand, as described below. +# +# Specify "mynetworks_style = host" when Postfix should "trust" +# only the local machine. +# +#mynetworks_style = class +#mynetworks_style = subnet +#mynetworks_style = host + +# Alternatively, you can specify the mynetworks list by hand, in +# which case Postfix ignores the mynetworks_style setting. +# +# Specify an explicit list of network/netmask patterns, where the +# mask specifies the number of bits in the network part of a host +# address. +# +# You can also specify the absolute pathname of a pattern file instead +# of listing the patterns here. Specify type:table for table-based lookups +# (the value on the table right-hand side is not used). +# +#mynetworks = 168.100.189.0/28, 127.0.0.0/8 +#mynetworks = $config_directory/mynetworks +#mynetworks = hash:/etc/postfix/network_table + +# The relay_domains parameter restricts what destinations this system will +# relay mail to. See the smtpd_recipient_restrictions restriction in the +# file sample-smtpd.cf for detailed information. +# +# By default, Postfix relays mail +# - from "trusted" clients (IP address matches $mynetworks) to any destination, +# - from "untrusted" clients to destinations that match $relay_domains or +# subdomains thereof, except addresses with sender-specified routing. +# The default relay_domains value is $mydestination. +# +# In addition to the above, the Postfix SMTP server by default accepts mail +# that Postfix is final destination for: +# - destinations that match $inet_interfaces, +# - destinations that match $mydestination +# - destinations that match $virtual_alias_domains, +# - destinations that match $virtual_mailbox_domains. +# These destinations do not need to be listed in $relay_domains. +# +# Specify a list of hosts or domains, /file/name patterns or type:name +# lookup tables, separated by commas and/or whitespace. Continue +# long lines by starting the next line with whitespace. A file name +# is replaced by its contents; a type:name table is matched when a +# (parent) domain appears as lookup key. +# +# NOTE: Postfix will not automatically forward mail for domains that +# list this system as their primary or backup MX host. See the +# permit_mx_backup restriction in the file sample-smtpd.cf. +# +#relay_domains = $mydestination + +# INTERNET OR INTRANET + +# The relayhost parameter specifies the default host to send mail to +# when no entry is matched in the optional transport(5) table. When +# no relayhost is given, mail is routed directly to the destination. +# +# On an intranet, specify the organizational domain name. If your +# internal DNS uses no MX records, specify the name of the intranet +# gateway host instead. +# +# In the case of SMTP, specify a domain, host, host:port, [host]:port, +# [address] or [address]:port; the form [host] turns off MX lookups. +# +# If you're connected via UUCP, see also the default_transport parameter. +# +#relayhost = $mydomain +#relayhost = gateway.my.domain +#relayhost = uucphost +#relayhost = [an.ip.add.ress] + +# REJECTING UNKNOWN RELAY USERS +# +# The relay_recipient_maps parameter specifies optional lookup tables +# with all addresses in the domains that match $relay_domains. +# +# If this parameter is defined, then the SMTP server will reject +# mail for unknown relay users. This feature is off by default. +# +# The right-hand side of the lookup tables is conveniently ignored. +# In the left-hand side, specify an @domain.tld wild-card, or specify +# a user@domain.tld address. +# +#relay_recipient_maps = hash:/etc/postfix/relay_recipients + +# INPUT RATE CONTROL +# +# The in_flow_delay configuration parameter implements mail input +# flow control. This feature is turned on by default, although it +# still needs further development (it's disabled on SCO UNIX due +# to an SCO bug). +# +# A Postfix process will pause for $in_flow_delay seconds before +# accepting a new message, when the message arrival rate exceeds the +# message delivery rate. With the default 100 SMTP server process +# limit, this limits the mail inflow to 100 messages a second more +# than the number of messages delivered per second. +# +# Specify 0 to disable the feature. Valid delays are 0..10. +# +#in_flow_delay = 1s + +# ADDRESS REWRITING +# +# Insert text from sample-rewrite.cf if you need to do address +# masquerading. +# +# Insert text from sample-canonical.cf if you need to do address +# rewriting, or if you need username->Firstname.Lastname mapping. + +# ADDRESS REDIRECTION (VIRTUAL DOMAIN) +# +# Insert text from sample-virtual.cf if you need virtual domain support. + +# "USER HAS MOVED" BOUNCE MESSAGES +# +# Insert text from sample-relocated.cf if you need "user has moved" +# style bounce messages. Alternatively, you can bounce recipients +# with an SMTP server access table. See sample-smtpd.cf. + +# TRANSPORT MAP +# +# Insert text from sample-transport.cf if you need explicit routing. + +# ALIAS DATABASE +# +# The alias_maps parameter specifies the list of alias databases used +# by the local delivery agent. The default list is system dependent. +# +# On systems with NIS, the default is to search the local alias +# database, then the NIS alias database. See aliases(5) for syntax +# details. +# +# If you change the alias database, run "postalias /etc/aliases" (or +# wherever your system stores the mail alias file), or simply run +# "newaliases" to build the necessary DBM or DB file. +# +# It will take a minute or so before changes become visible. Use +# "postfix reload" to eliminate the delay. +# +#alias_maps = dbm:/etc/aliases +#alias_maps = hash:/etc/aliases +#alias_maps = hash:/etc/aliases, nis:mail.aliases +#alias_maps = netinfo:/aliases + +# The alias_database parameter specifies the alias database(s) that +# are built with "newaliases" or "sendmail -bi". This is a separate +# configuration parameter, because alias_maps (see above) may specify +# tables that are not necessarily all under control by Postfix. +# +#alias_database = dbm:/etc/aliases +#alias_database = dbm:/etc/mail/aliases +#alias_database = hash:/etc/aliases +#alias_database = hash:/etc/aliases, hash:/opt/majordomo/aliases + +# ADDRESS EXTENSIONS (e.g., user+foo) +# +# The recipient_delimiter parameter specifies the separator between +# user names and address extensions (user+foo). See canonical(5), +# local(8), relocated(5) and virtual(5) for the effects this has on +# aliases, canonical, virtual, relocated and .forward file lookups. +# Basically, the software tries user+foo and .forward+foo before +# trying user and .forward. +# +#recipient_delimiter = + + +# DELIVERY TO MAILBOX +# +# The home_mailbox parameter specifies the optional pathname of a +# mailbox file relative to a user's home directory. The default +# mailbox file is /var/spool/mail/user or /var/mail/user. Specify +# "Maildir/" for qmail-style delivery (the / is required). +# +#home_mailbox = Mailbox +#home_mailbox = Maildir/ + +# The mail_spool_directory parameter specifies the directory where +# UNIX-style mailboxes are kept. The default setting depends on the +# system type. +# +#mail_spool_directory = /var/mail +#mail_spool_directory = /var/spool/mail + +# The mailbox_command parameter specifies the optional external +# command to use instead of mailbox delivery. The command is run as +# the recipient with proper HOME, SHELL and LOGNAME environment settings. +# Exception: delivery for root is done as $default_user. +# +# Other environment variables of interest: USER (recipient username), +# EXTENSION (address extension), DOMAIN (domain part of address), +# and LOCAL (the address localpart). +# +# Unlike other Postfix configuration parameters, the mailbox_command +# parameter is not subjected to $parameter substitutions. This is to +# make it easier to specify shell syntax (see example below). +# +# Avoid shell meta characters because they will force Postfix to run +# an expensive shell process. Procmail alone is expensive enough. +# +# IF YOU USE THIS TO DELIVER MAIL SYSTEM-WIDE, YOU MUST SET UP AN +# ALIAS THAT FORWARDS MAIL FOR ROOT TO A REAL USER. +# +#mailbox_command = /some/where/procmail +#mailbox_command = /some/where/procmail -a "$EXTENSION" + +# The mailbox_transport specifies the optional transport in master.cf +# to use after processing aliases and .forward files. This parameter +# has precedence over the mailbox_command, fallback_transport and +# luser_relay parameters. +# +# Specify a string of the form transport:nexthop, where transport is +# the name of a mail delivery transport defined in master.cf. The +# :nexthop part is optional. For more details see the sample transport +# configuration file. +# +# NOTE: if you use this feature for accounts not in the UNIX password +# file, then you must update the "local_recipient_maps" setting in +# the main.cf file, otherwise the SMTP server will reject mail for +# non-UNIX accounts with "User unknown in local recipient table". +# +#mailbox_transport = lmtp:unix:/file/name +mailbox_transport = lmtp:unix:/var/imap/socket/lmtp +#mailbox_transport = cyrus + +# The fallback_transport specifies the optional transport in master.cf +# to use for recipients that are not found in the UNIX passwd database. +# This parameter has precedence over the luser_relay parameter. +# +# Specify a string of the form transport:nexthop, where transport is +# the name of a mail delivery transport defined in master.cf. The +# :nexthop part is optional. For more details see the sample transport +# configuration file. +# +# NOTE: if you use this feature for accounts not in the UNIX password +# file, then you must update the "local_recipient_maps" setting in +# the main.cf file, otherwise the SMTP server will reject mail for +# non-UNIX accounts with "User unknown in local recipient table". +# +#fallback_transport = lmtp:unix:/file/name +#fallback_transport = cyrus +#fallback_transport = + +# The luser_relay parameter specifies an optional destination address +# for unknown recipients. By default, mail for unknown@$mydestination +# and unknown@[$inet_interfaces] is returned as undeliverable. +# +# The following expansions are done on luser_relay: $user (recipient +# username), $shell (recipient shell), $home (recipient home directory), +# $recipient (full recipient address), $extension (recipient address +# extension), $domain (recipient domain), $local (entire recipient +# localpart), $recipient_delimiter. Specify ${name?value} or +# ${name:value} to expand value only when $name does (does not) exist. +# +# luser_relay works only for the default Postfix local delivery agent. +# +# NOTE: if you use this feature for accounts not in the UNIX password +# file, then you must specify "local_recipient_maps =" (i.e. empty) in +# the main.cf file, otherwise the SMTP server will reject mail for +# non-UNIX accounts with "User unknown in local recipient table". +# +#luser_relay = $user@other.host +#luser_relay = $local@other.host +#luser_relay = admin+$local + +# JUNK MAIL CONTROLS +# +# The controls listed here are only a very small subset. See the file +# sample-smtpd.cf for an elaborate list of anti-UCE controls. + +# The header_checks parameter specifies an optional table with patterns +# that each logical message header is matched against, including +# headers that span multiple physical lines. +# +# By default, these patterns also apply to MIME headers and to the +# headers of attached messages. With older Postfix versions, MIME and +# attached message headers were treated as body text. +# +# For details, see the sample-filter.cf file. +# +#header_checks = regexp:/etc/postfix/header_checks + +# FAST ETRN SERVICE +# +# Postfix maintains per-destination logfiles with information about +# deferred mail, so that mail can be flushed quickly with the SMTP +# "ETRN domain.tld" command, or by executing "sendmail -qRdomain.tld". +# +# By default, Postfix maintains deferred mail logfile information +# only for destinations that Postfix is willing to relay to (as +# specified in the relay_domains parameter). For other destinations, +# Postfix attempts to deliver ALL queued mail after receiving the +# SMTP "ETRN domain.tld" command, or after execution of "sendmail +# -qRdomain.tld". This can be slow when a lot of mail is queued. +# +# The fast_flush_domains parameter controls what destinations are +# eligible for this "fast ETRN/sendmail -qR" service. +# +#fast_flush_domains = $relay_domains +#fast_flush_domains = + +# The disable_vrfy_command parameter allows you to disable the SMTP +# VRFY command. This stops some techniques used by spammers to harvest +# email addresses. +# +disable_vrfy_command = yes + +# SHOW SOFTWARE VERSION OR NOT +# +# The smtpd_banner parameter specifies the text that follows the 220 +# code in the SMTP server's greeting banner. Some people like to see +# the mail version advertised. By default, Postfix shows no version. +# +# You MUST specify $myhostname at the start of the text. That is an +# RFC requirement. Postfix itself does not care. +# +#smtpd_banner = $myhostname ESMTP $mail_name +#smtpd_banner = $myhostname ESMTP $mail_name ($mail_version) + +# PARALLEL DELIVERY TO THE SAME DESTINATION +# +# How many parallel deliveries to the same user or domain? With local +# delivery, it does not make sense to do massively parallel delivery +# to the same user, because mailbox updates must happen sequentially, +# and expensive pipelines in .forward files can cause disasters when +# too many are run at the same time. With SMTP deliveries, 10 +# simultaneous connections to the same domain could be sufficient to +# raise eyebrows. +# +# Each message delivery transport has its XXX_destination_concurrency_limit +# parameter. The default is $default_destination_concurrency_limit for +# most delivery transports. For the local delivery agent the default is 2. + +#local_destination_concurrency_limit = 2 +#default_destination_concurrency_limit = 20 + +# DEBUGGING CONTROL +# +# The debug_peer_level parameter specifies the increment in verbose +# logging level when an SMTP client or server host name or address +# matches a pattern in the debug_peer_list parameter. +# +debug_peer_level = 2 + +# The debug_peer_list parameter specifies an optional list of domain +# or network patterns, /file/name patterns or type:name tables. When +# an SMTP client or server host name or address matches a pattern, +# increase the verbose logging level by the amount specified in the +# debug_peer_level parameter. +# +#debug_peer_list = 127.0.0.1 +#debug_peer_list = some.domain + +# The debugger_command specifies the external command that is executed +# when a Postfix daemon program is run with the -D option. +# +# Use "command .. & sleep 5" so that the debugger can attach before +# the process marches on. If you use an X-based debugger, be sure to +# set up your XAUTHORITY environment variable before starting Postfix. +# +debugger_command = + PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin + xxgdb $daemon_directory/$process_name $process_id & sleep 5 + +# If you don't have X installed on the Postfix machine, try: +# debugger_command = +# PATH=/bin:/usr/bin:/usr/local/bin; export PATH; (echo cont; +# echo where) | gdb $daemon_directory/$process_name $process_id 2>&1 +# >$config_directory/$process_name.$process_id.log & sleep 5 + +# INSTALL-TIME CONFIGURATION INFORMATION +# +# The following parameters are used when installing a new Postfix version. +# +# sendmail_path: The full pathname of the Postfix sendmail command. +# This is the Sendmail-compatible mail posting interface. +# +sendmail_path = /usr/sbin/sendmail + +# newaliases_path: The full pathname of the Postfix newaliases command. +# This is the Sendmail-compatible command to build alias databases. +# +newaliases_path = /usr/bin/newaliases + +# mailq_path: The full pathname of the Postfix mailq command. This +# is the Sendmail-compatible mail queue listing command. +# +mailq_path = /usr/bin/mailq + +# setgid_group: The group for mail submission and queue management +# commands. This must be a group name with a numerical group ID that +# is not shared with other accounts, not even with the Postfix account. +# +setgid_group = postdrop + +# manpage_directory: The location of the Postfix on-line manual pages. +# +manpage_directory = /usr/share/man + +# sample_directory: The location of the Postfix sample configuration files. +# +sample_directory = /usr/share/doc/postfix-2.0.19/sample + +# readme_directory: The location of the Postfix README files. +# +readme_directory = /usr/share/doc/postfix-2.0.19/readme +default_destination_concurrency_limit = 2 +#alias_database = hash:/etc/mail/aliases +local_destination_concurrency_limit = 2 +alias_maps = hash:/etc/mail/aliases + +content_filter = smtp-amavis:[127.0.0.1]:10024 +queue_minfree = 100000000 +message_size_limit = 50000000 +mailbox_size_limit = 500000000 +smtpd_helo_required=yes +smtpd_helo_restrictions=permit_mynetworks, reject_invalid_hostname, reject_invalid_hostname +smtpd_sender_restrictions=permit_mynetworks, reject_unknown_sender_domain, reject_non_fqdn_sender + +virtual_maps = ldap:aliases, ldap:mailboxes + +aliases_server_host = 127.0.0.1 +aliases_search_base = dc=domain,dc=loc +aliases_query_filter = (&(|(mail=%s)(mailalternateaddress=%s))(objectclass=posixaccount)(deliveryMode=forwardonly)(accountstatus=active)) +aliases_bind_dn = cn=thepostfixadmin,dc=domain,dc=loc +aliases_bind_pw = thepassword +aliases_result_attribute = mailforwardingaddress +aliases_version = 3 + +mailboxes_server_host = 127.0.0.1 +mailboxes_search_base = dc=domain,dc=loc +mailboxes_query_filter = (&(|(mail=%s)(mailalternateaddress=%s))(objectclass=posixaccount)(accountstatus=active)) +mailboxes_bind_dn = cn=thepostfixadmin,dc=domain,dc=loc +mailboxes_bind_pw = thepassword +mailboxes_result_attribute = uid, mailforwardingaddress +mailboxes_version = 3 + + +#SMTPD mit SASL-Authentification verwenden +smtpd_sasl_auth_enable = yes + +#Zusatz-Optionen: Keine anonyme-Anmeldung verwenden +smtpd_sasl_security_options = noanonymous + +#Wieder ein Workaround für ältere Clients und Outlook +broken_sasl_auth_clients = yes + +# ODER meine Netze und SASL erlauben +smtpd_recipient_restrictions = + permit_mynetworks, + permit_sasl_authenticated, + reject_rbl_client relays.ordb.org, + reject_rbl_client sbl-xbl.spamhaus.org, + reject_rbl_client opm.blitzed.org, + reject_rbl_client dnsbl.njabl.org, + reject_rbl_client blackholes.wirehub.net, + reject_rbl_client list.dsbl.org, + reject_rbl_client dnsbl.sorbs.net, + reject_unauth_destination, + reject_non_fqdn_sender, + reject_non_fqdn_recipient, + reject_unauth_pipelining, + reject_unknown_sender_domain, + reject_unknown_recipient_domain + +# reject_unknown_client +# reject_rbl_client proxies.relays.monkeys.com, + +# incoming SSL +smtpd_use_tls = yes +#smtpd_tls_auth_only = yes +smtpd_tls_key_file = /etc/ssl/private/smtp.linux-at-work.de/smtp.linux-at-work.de.key +smtpd_tls_cert_file = /etc/ssl/private/smtp.linux-at-work.de/smtp.linux-at-work.de.crt +smtpd_tls_CAfile = /etc/ssl/certs/ca-cert.pem +smtpd_tls_loglevel = 1 +smtpd_tls_received_header = yes +smtpd_tls_session_cache_timeout = 3600s +tls_random_source = dev:/dev/urandom + +#outgoing SSL +smtp_tls_key_file = /etc/ssl/private/smtp.linux-at-work.de/smtp.linux-at-work.de.key +smtp_tls_cert_file = /etc/ssl/private/smtp.linux-at-work.de/smtp.linux-at-work.de.crt +smtp_tls_CAfile = /etc/ssl/certs/ca-cert.pem +smtp_tls_CApath = /etc/ssl/certs +smtp_tls_loglevel = 2 +# The server and client negotiate a session, which takes some computer time +# and network bandwidth. The session is cached only in the smtpd process +# actually using this session and is lost when the process dies. +# To share the session information between the smtp processes, a disc based +# session cache can be used based on the SDBM databases (routines included +# in Postfix/TLS). Since concurrent writing must be supported, only SDBM +# can be used. +# +smtp_tls_session_cache_database = sdbm:/etc/postfix/smtp_scache + +# By default TLS is disabled, so no difference to plain postfix is visible. +# If you enable TLS it will be used when offered by the server. +# WARNING: I didn't have access to other software (except those explicitely +# listed) to test the interaction. On corresponding mailing list +# there was a discussion going on about MS exchange servers offering +# STARTTLS even if it is not configured, so it might be wise to not +# use this option on your central mail hub, as you don't know in advance +# whether you are going to hit such host. Use the recipient/site specific +# options instead. +# HINT: I have it switched on on my mailservers and did experience one +# single failure since client side TLS is implemented. (There was one +# misconfired MS Exchange server; I contacted ths admin.) Hence, I am happy +# with it running all the time, but I am interested in testing anyway. +# You have been warned, however :-) +# +# In case of failure, a "4xx" code is issued and the mail stays in the queue. +# +# Explicitely switch it on here, if you want it. +# +#smtp_use_tls = yes diff --git a/emailadmin/doc/postfix_tcp_map_ads.php b/emailadmin/doc/postfix_tcp_map_ads.php new file mode 100755 index 0000000000..e3e276b091 --- /dev/null +++ b/emailadmin/doc/postfix_tcp_map_ads.php @@ -0,0 +1,408 @@ +#!/usr/bin/php -Cq +" allows to receive mail for given + * (includes aliases AND primary email) + * - "forward:" forwards received mail to given + * (requires account to have at an "smtp:" value!) + * - ("forwardOnly" is used for no local mailbox, only forwards, not implemented!) + * - ("quota:" is used to store quota) + * + * Groups can be used as distribution lists by assigning them an + * email address via there mail attribute (no proxyAddress) + * + * PROTOCOL DESCRIPTION + * The TCP map class implements a very simple protocol: the + * client sends a request, and the server sends one reply. + * Requests and replies are sent as one line of ASCII text, + * terminated by the ASCII newline character. Request and + * reply parameters (see below) are separated by whitespace. + * + * REQUEST FORMAT + * Each request specifies a command, a lookup key, and possi- + * bly a lookup result. + * + * get SPACE key NEWLINE + * Look up data under the specified key. + * + * put SPACE key SPACE value NEWLINE + * This request is currently not implemented. + * + * REPLY FORMAT + * Each reply specifies a status code and text. Replies must + * be no longer than 4096 characters including the newline + * terminator. + * + * 500 SPACE text NEWLINE + * In case of a lookup request, the requested data + * does not exist. In case of an update request, the + * request was rejected. The text describes the + * nature of the problem. + * + * 400 SPACE text NEWLINE + * This indicates an error condition. The text + * describes the nature of the problem. The client + * should retry the request later. + * + * 200 SPACE text NEWLINE + * The request was successful. In the case of a lookup + * request, the text contains an encoded version of + * the requested data. + * + * ENCODING + * In request and reply parameters, the character %, each + * non-printing character, and each whitespace character must + * be replaced by %XX, where XX is the corresponding ASCII + * hexadecimal character value. The hexadecimal codes can be + * specified in any case (upper, lower, mixed). + * + * The Postfix client always encodes a request. The server + * may omit the encoding as long as the reply is guaranteed + * to not contain the % or NEWLINE character. + * + * @author rb(at)stylite.de + * @copyright (c) 2012-13 by rb(at)stylite.de + * @package emailadmin + * @link http://www.postfix.org/tcp_table.5.html + * @version $Id$ + */ + +// protect from being called via HTTP +if (php_sapi_name() !== 'cli') die('This is a command line only script!'); + +// our defaults +$default_host = 'localhost'; +$verbose = false; + +// allow only clients matching that preg to access, should be only mserver IP +//$only_client = '/^10\.40\.8\.210:/'; + +// uncomment to write to log-file, otherwise errors go to stderr +//$log = 'syslog'; // or not set (stderr) or filename '/var/log/postfix_tcp_map.log'; +//$log_verbose = true; // error's are always logged, set to true to log failures and success too + +// ldap server settings +$ldap_uri = 'ldaps://10.7.102.13/'; +$base = 'CN=Users,DC=gruene,DC=intern'; +//$bind_dn = "CN=Administrator,$base"; +//$bind_dn = "Administrator@gruene.intern"; +//$bind_pw = 'secret'; +$version = 3; +$use_tls = false; +// supported maps +$maps = array( + // virtual mailbox map + 'mailboxes' => array( + 'base' => $base, + 'filter' => '(&(objectCategory=person)(proxyAddresses=smtp:%s))', + 'attrs' => 'samaccountname', // result-attrs must be lowercase! + 'port' => 2001, + ), + // virtual alias maps + 'aliases' => array( + 'base' => $base, + 'filter' => '(&(objectCategory=person)(proxyAddresses=smtp:%s))', + 'attrs' => array('samaccountname','{forward:}proxyaddresses'), + 'port' => 2002, + ), + // groups as distribution list + 'groups' => array( + 'base' => $base, + 'filter' => '(&(objectCategory=group)(mail=%s))', + 'attrs' => 'dn', + // continue with resulting dn + 'filter1' => '(&(objectCategory=person)(proxyAddresses=smtp:*)(memberOf=%s))', + 'attrs1' => array('samaccountname','{forward:}proxyaddresses'), + 'port' => 2003, + ), +); + +ini_set('display_errors',false); +error_reporting(E_ALL & ~E_NOTICE); +if ($log) ini_set('error_log',$log); + +function usage($extra=null) +{ + global $maps; + fwrite(STDERR, "\nUsage: $cmd [-v|--verbose] [-h|--help] [-l|--log (syslog|path)] [-q|--query (mailboxes|alias|groups)] [host]\n\n"); + fwrite(STDERR, print_r($maps,true)."\n"); + if ($extra) fwrite(STDERR, "\n\n$extra\n\n"); + exit(2); +} + +$cmd = basename(array_shift($_SERVER['argv'])); + +while (($arg = array_shift($_SERVER['argv'])) && $arg[0] == '-') +{ + switch($arg) + { + case '-v': case '--verbose': + $verbose = $log_verbose = true; + break; + + case '-h': case '--help': + usage(); + break; + + case '-l': case '--log': + $log = array_shift($_SERVER['argv']); + break; + + case '-q': case '--query': + if (count($_SERVER['argv']) == 2) // need 2 arguments + { + $request = 'get '.array_shift($_SERVER['argv'])."\n"; + $map = array_shift($_SERVER['argv']); + echo respond($request, $map)."\n"; + exit; + } + usage(); + break; + + default: + usage("Unknown option '$arg'!"); + } +} +if ($_SERVER['argv']) usage(); + +if ($arg) +{ + $host = $arg; +} +else +{ + $host = $default_host; +} + +if ($verbose) echo "using $host\n"; + +$servers = $clients = $buffers = array(); + +// Create the server socket +foreach($maps as $map => $data) +{ + $addr = 'tcp://'.$host.':'.$data['port']; + if (!($server = stream_socket_server($addr, $errno, $errstr))) + { + fwrite(STDERR, date('Y-m-d H:i:s').": Error calling stream_socket_server('$addr')!\n"); + fwrite(STDERR, $errstr." ($errno)\n"); + exit($errno); + } + $servers[$data['port']] = $server; + $clients[$data['port']] = array(); +} +while (true) // mail loop of tcp server --> never exits +{ + $read = $servers; + if ($clients) $read = array_merge($read, call_user_func_array('array_merge', array_values($clients))); + if ($verbose) print 'about to call socket_select(array('.implode(',',$read).', ...) waiting... '; + if (stream_select($read, $write=null, $except=null, null)) // null = block forever + { + foreach($read as $sock) + { + if (($port = array_search($sock, $servers)) !== false) + { + $client = stream_socket_accept($sock,$timeout,$client_addr); // @ required to get not timeout warning! + + if ($verbose) echo "accepted connection $client from $client_addr on port $port\n"; + + if ($only_client && !preg_match($only_client,$client_addr)) + { + fwrite($client,"Go away!\r\n"); + fclose($client); + error_log(date('Y-m-d H:i:s').": Connection $client from wrong client $client_addr (does NOT match '$only_client') --> terminated"); + continue; + } + $clients[$port][] = $client; + } + elseif (feof($sock)) // client connection closed + { + if ($verbose) echo "client $sock closed connection\n"; + + foreach($clients as $port => &$socks) + { + if (($key = array_search($sock, $socks, true)) !== false) + { + unset($socks[$key]); + } + } + } + else // client send something + { + $buffer =& $buffers[$sock]; + + $buffer .= fread($sock, 8096); + + if (strpos($buffer, "\n") !== false) + { + list($request, $buffer) = explode("\n", $buffer, 2); + + foreach($maps as $map => $data) + { + if (($key = array_search($sock, $clients[$data['port']], true)) !== false) + { + if ($verbose) echo date('Y-m-d H:i:s').": client send: $request for map $map\n"; + + // Respond to client + fwrite($sock, respond($request, $map)); + break; + } + } + } + } + } + if ($except) + { + echo "Exception: "; print_r($except); + } + } + else + { + // timeout expired + } +} + +/** + * escapes a string for use in searchfilters meant for ldap_search. + * + * Escaped Characters are: '*', '(', ')', ' ', '\', NUL + * It's actually a PHP-Bug, that we have to escape space. + * For all other Characters, refer to RFC2254. + * + * @param string|array $string either a string to be escaped, or an array of values to be escaped + * @return string + */ +function quote($string) +{ + return str_replace(array('\\','*','(',')','\0',' '),array('\\\\','\*','\(','\)','\\0','\20'),$string); +} + +function respond($request, $map, $extra='', $reconnect=false) +{ + static $ds; + global $ldap_uri, $version, $use_tls, $bind_dn, $bind_pw; + global $maps, $log_verbose; + + if (($map == 'aliases' || $map == 'groups') && strpos($request,'@') === false && !$extra) + { + return "500 No domain aliases yet\n"; + } + if (!isset($ds) || $reconnect) + { + $ds = ldap_connect($ldap_uri); + if ($version) ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, $version); + if ($use_tls) ldap_start_tls($ds); + + if (!@ldap_bind($ds, $bind_dn, $bind_pw)) + { + error_log("$map: Can't connect to LDAP server $ldap_uri!"); + $ds = null; + return "400 Can't connect to LDAP server $ldap_uri!\n"; // 400 (temp.) error + } + } + if (!preg_match('/^get ([^\n]+)\n?$/', $request, $matches)) + { + error_log("$map: Wrong format '$request'!"); + return "400 Wrong format '$request'!\n"; // 400 (temp.) error + } + $username = $matches[1]; + + list($name,$domain) = explode('@',$username); + + /* check if we are responsible for the given domain + if ($domain && $map != 'domains' && (int)($response = respond("get $domain", 'domains')) != 200) + { + return $response; + }*/ + $replace = array( + '%n' => quote($name), + '%d' => quote($domain), + '%s' => quote($username), + ); + $base = strtr($maps[$map]['base'], $replace); + $filter = strtr($maps[$map]['filter'.$extra], $replace); + $prefix = isset($maps[$map]['prefix'.$extra]) ? str_replace(array('%n','%d','%s'),array($name,$domain,$username),$maps[$map]['prefix']) : ''; + $search_attrs = $attrs = (array)$maps[$map]['attrs'.$extra]; + // remove prefix like "{smtp:}proxyaddresses" + foreach($search_attrs as &$attr) + { + if ($attr[0] == '{') list(,$attr) = explode('}', $attr); + } + unset($attr); + + if (!($sr = @ldap_search($ds, $base, $filter, $search_attrs))) + { + $errno = ldap_errno($ds); + $error = ldap_error($ds).' ('.$errno.')'; + + if ($errno == -1) // eg. -1 lost connection to ldap + { + // as DC closes connections quickly, first try to reconnect once, before returning a temp. failure + if (!$reconnect) return respond($request, $map, $extra, true); + + error_log("$map: get '$username' --> 400 $error: !ldap_search(\$ds, '$base', '$filter')"); + ldap_close($ds); + $ds = null; // force new connection on next lookup + return "400 $error\n"; // 400 (temp.) error + } + else // happens if base containing domain does not exist + { + if ($log_verbose) error_log("$map: get '$username' --> 500 Not found: $error: !ldap_search(\$ds, '$base', '$filter')"); + return "500 Not found: $error\n"; // 500 not found + } + } + $entries = ldap_get_entries($ds, $sr); + + if (!$entries['count']) + { + if ($log_verbose) error_log("$map: get '$username' --> 500 not found ldap_search(\$ds, '$base', '$filter') no entries"); + return "500 Not found\n"; // 500: Query returned no result + } + $response = array(); + foreach($entries as $key => $entry) + { + if ($key === 'count') continue; + + foreach($attrs as $attr) + { + unset($filter_prefix); + if ($attr[0] == '{') + { + list($filter_prefix, $attr) = explode('}', substr($attr, 1)); + } + foreach((array)$entry[$attr] as $k => $mail) + { + if ($k !== 'count' && ($mail = trim($mail))) + { + if ($filter_prefix) + { + if (stripos($mail, $filter_prefix) === 0) + { + $mail = substr($mail, strlen($filter_prefix)); + } + else + { + continue; + } + } + $response[] = isset($maps[$map]['return']) ? $maps[$map]['return'] : $prefix.$mail; + } + } + } + } + if (!$response) + { + if ($log_verbose) error_log("$map: get '$username' --> 500 not found ldap_search(\$ds, '$base', '$filter') no response"); + return "500 Not found\n"; // 500: Query returned no result + } + if (isset($maps[$map]['filter'.(1+$extra)]) && isset($maps[$map]['attrs'.(1+$extra)])) + { + return respond('get '.$response[0], $map, 1+$extra); + } + $response = '200 '.implode(',',$response)."\n"; + if ($log_verbose) error_log("$map: get '$username' --> $response"); + return $response; +} diff --git a/emailadmin/doc/qmail.new.schema b/emailadmin/doc/qmail.new.schema new file mode 100644 index 0000000000..f9d9e3289d --- /dev/null +++ b/emailadmin/doc/qmail.new.schema @@ -0,0 +1,271 @@ +# +# qmail-ldap (20030901) ldapv3 directory schema +# +# The offical qmail-ldap OID assigned by IANA is 7914 +# +# Created by: David E. Storey +# Modified and included into qmail-ldap by Andre Oppermann +# Schema fixes by Mike Jackson +# Schema fixes by Christian Zoffoli (XMerlin) +# +# +# This schema depends on: +# - core.schema +# - cosine.schema +# - nis.schema +# + +# Attribute Type Definitions + +attributetype ( 1.3.6.1.4.1.7914.1.2.1.1 NAME 'qmailUID' +DESC 'UID of the user on the mailsystem' +EQUALITY integerMatch +SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.7914.1.2.1.2 NAME 'qmailGID' +DESC 'GID of the user on the mailsystem' +EQUALITY integerMatch +SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.7914.1.2.1.3 NAME 'mailMessageStore' +DESC 'Path to the maildir/mbox on the mail system' +EQUALITY caseExactIA5Match +SUBSTR caseIgnoreIA5SubstringsMatch +SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.7914.1.2.1.4 NAME 'mailAlternateAddress' +DESC 'Secondary (alias) mailaddresses for the same user' +EQUALITY caseIgnoreIA5Match +SUBSTR caseIgnoreIA5SubstringsMatch +SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} ) + +# +# mailQuota format is no longer supported from qmail-ldap 20030901 on, +# user mailQuotaSize and mailQuotaCount instead. +# +#attributetype ( 1.3.6.1.4.1.7914.1.2.1.5 NAME 'mailQuota' +# DESC 'The amount of space the user can use until all further messages get bounced.' +# SYNTAX 1.3.6.1.4.1.1466.115.121.1.44 SINGLE-VALUE ) +# + +attributetype ( 1.3.6.1.4.1.7914.1.2.1.6 NAME 'mailHost' +DESC 'On which qmail server the messagestore of this user is located.' +EQUALITY caseIgnoreIA5Match +SUBSTR caseIgnoreIA5SubstringsMatch +SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} SINGLE-VALUE) + +attributetype ( 1.3.6.1.4.1.7914.1.2.1.7 NAME 'mailForwardingAddress' +DESC 'Address(es) to forward all incoming messages to.' +EQUALITY caseIgnoreIA5Match +SUBSTR caseIgnoreIA5SubstringsMatch +SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} ) + +attributetype ( 1.3.6.1.4.1.7914.1.2.1.8 NAME 'deliveryProgramPath' +DESC 'Program to execute for all incoming mails.' +EQUALITY caseExactIA5Match +SUBSTR caseIgnoreIA5SubstringsMatch +SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} ) + +attributetype ( 1.3.6.1.4.1.7914.1.2.1.9 NAME 'qmailDotMode' +DESC 'Interpretation of .qmail files: both, dotonly, ldaponly, ldapwithprog' +EQUALITY caseIgnoreIA5Match +SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{32} SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.7914.1.2.1.10 NAME 'deliveryMode' +DESC 'multi field entries of: nolocal, noforward, noprogram, reply' +EQUALITY caseIgnoreIA5Match +SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{32} ) + +attributetype ( 1.3.6.1.4.1.7914.1.2.1.11 NAME 'mailReplyText' +DESC 'A reply text for every incoming message' +EQUALITY caseIgnoreMatch +SUBSTR caseIgnoreSubstringsMatch +SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{4096} SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.7914.1.2.1.12 NAME 'accountStatus' +DESC 'The status of a user account: active, noaccess, disabled, deleted' +EQUALITY caseIgnoreIA5Match +SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.7914.1.2.1.14 NAME 'qmailAccountPurge' + DESC 'The earliest date when a mailMessageStore will be purged' + EQUALITY numericStringMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.36 SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.7914.1.2.1.15 NAME 'mailQuotaSize' +DESC 'The size of space the user can have until further messages get bounced.' +EQUALITY integerMatch +SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.7914.1.2.1.16 NAME 'mailQuotaCount' +DESC 'The number of messages the user can have until further messages get bounced.' +EQUALITY integerMatch +SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.7914.1.2.1.17 NAME 'mailSizeMax' +DESC 'The maximum size of a single messages the user accepts.' +EQUALITY integerMatch +SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) + +# +# qmailGroup attributes +# + +attributetype ( 1.3.6.1.4.1.7914.1.3.1.1 NAME 'dnmember' +DESC 'Group member specified as distinguished name.' +EQUALITY distinguishedNameMatch +SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 ) + +attributetype ( 1.3.6.1.4.1.7914.1.3.1.2 NAME 'rfc822member' +DESC 'Group member specified as normal rf822 email address.' +EQUALITY caseIgnoreIA5Match +SUBSTR caseIgnoreIA5SubstringsMatch +SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} ) + +attributetype ( 1.3.6.1.4.1.7914.1.3.1.3 NAME 'filtermember' +DESC 'Group member specified as ldap search filter.' +EQUALITY caseIgnoreIA5Match +SUBSTR caseIgnoreIA5SubstringsMatch +SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{512} ) + +attributetype ( 1.3.6.1.4.1.7914.1.3.1.4 NAME 'senderconfirm' +DESC 'Sender to Group has to answer confirmation email.' +EQUALITY booleanMatch +SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.7914.1.3.1.5 NAME 'membersonly' +DESC 'Sender to Group must be group member itself.' +EQUALITY booleanMatch +SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.7914.1.3.1.6 NAME 'confirmtext' +DESC 'Text that will be sent with sender confirmation email.' +EQUALITY caseIgnoreMatch +SUBSTR caseIgnoreSubstringsMatch +SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{4096} SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.7914.1.3.1.7 NAME 'dnmoderator' +DESC 'Group moderator specified as Distinguished name.' +EQUALITY distinguishedNameMatch +SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 ) + +attributetype ( 1.3.6.1.4.1.7914.1.3.1.8 NAME 'rfc822moderator' +DESC 'Group moderator specified as normal rfc822 email address.' +EQUALITY caseIgnoreIA5Match +SUBSTR caseIgnoreIA5SubstringsMatch +SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} ) + +attributetype ( 1.3.6.1.4.1.7914.1.3.1.9 NAME 'moderatortext' +DESC 'Text that will be sent with request for moderation email.' +EQUALITY caseIgnoreMatch +SUBSTR caseIgnoreSubstringsMatch +SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{4096} SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.7914.1.3.1.10 NAME 'dnsender' +DESC 'Allowed sender specified as distinguished name.' +EQUALITY distinguishedNameMatch +SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 ) + +attributetype ( 1.3.6.1.4.1.7914.1.3.1.11 NAME 'rfc822sender' +DESC 'Allowed sender specified as normal rf822 email address.' +EQUALITY caseIgnoreIA5Match +SUBSTR caseIgnoreIA5SubstringsMatch +SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} ) + +attributetype ( 1.3.6.1.4.1.7914.1.3.1.12 NAME 'filtersender' +DESC 'Allowed sender specified as ldap search filter.' +EQUALITY caseIgnoreIA5Match +SUBSTR caseIgnoreIA5SubstringsMatch +SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{512} ) + + +# +# qldapAdmin Attributes +# + +attributetype ( 1.3.6.1.4.1.7914.1.4.1.1 NAME 'qladnmanager' +DESC '' +EQUALITY distinguishedNameMatch +SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 ) + +attributetype ( 1.3.6.1.4.1.7914.1.4.1.2 NAME 'qlaDomainList' +DESC '' +EQUALITY caseIgnoreIA5Match +SUBSTR caseIgnoreIA5SubstringsMatch +SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} ) + +attributetype ( 1.3.6.1.4.1.7914.1.4.1.3 NAME 'qlaUidPrefix' +DESC '' +EQUALITY caseIgnoreIA5Match +SUBSTR caseIgnoreIA5SubstringsMatch +SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.7914.1.4.1.4 NAME 'qlaQmailUid' +DESC '' +EQUALITY integerMatch +SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.7914.1.4.1.5 NAME 'qlaQmailGid' +DESC '' +EQUALITY integerMatch +SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.7914.1.4.1.6 NAME 'qlaMailMStorePrefix' +DESC '' +EQUALITY caseIgnoreIA5Match +SUBSTR caseIgnoreIA5SubstringsMatch +SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.7914.1.4.1.7 NAME 'qlaMailQuotaSize' +DESC '' +EQUALITY integerMatch +SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.7914.1.4.1.8 NAME 'qlaMailQuotaCount' +DESC '' +EQUALITY integerMatch +SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.7914.1.4.1.9 NAME 'qlaMailSizeMax' +DESC '' +EQUALITY integerMatch +SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.7914.1.4.1.10 NAME 'qlaMailHostList' +DESC '' +EQUALITY caseIgnoreIA5Match +SUBSTR caseIgnoreIA5SubstringsMatch +SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} ) + + +# Object Class Definitions + +objectclass ( 1.3.6.1.4.1.7914.1.2.2.1 NAME 'qmailUser' +DESC 'QMail-LDAP User' +SUP top +AUXILIARY +MUST ( mail ) +MAY ( uid $ mailMessageStore $ homeDirectory $ userPassword $ + mailAlternateAddress $ qmailUID $ qmailGID $ + mailHost $ mailForwardingAddress $ deliveryProgramPath $ + qmailDotMode $ deliveryMode $ mailReplyText $ + accountStatus $ qmailAccountPurge $ + mailQuotaSize $ mailQuotaCount $ mailSizeMax ) ) + +objectclass ( 1.3.6.1.4.1.7914.1.3.2.1 NAME 'qmailGroup' +DESC 'QMail-LDAP Group' +SUP top +AUXILIARY +MUST ( mail $ mailAlternateAddress $ mailMessageStore ) +MAY ( dnmember $ rfc822member $ filtermember $ senderconfirm $ + membersonly $ confirmtext $ dnmoderator $ rfc822moderator $ + moderatortext $ dnsender $ rfc822sender $ filtersender) ) + +objectclass ( 1.3.6.1.4.1.7914.1.4.2.1 NAME 'qldapAdmin' +DESC 'QMail-LDAP Subtree Admin' +SUP top +AUXILIARY +MUST ( qlaDnManager $ qlaDomainList $ qlaMailMStorePrefix $ + qlaMailHostList ) +MAY ( qlaUidPrefix $ qlaQmailUid $ qlaQmailGid $ qlaMailQuotaSize $ + qlaMailQuotaCount $ qlaMailSizeMax ) ) diff --git a/emailadmin/doc/qmailuser.schema b/emailadmin/doc/qmailuser.schema new file mode 100644 index 0000000000..336761e6af --- /dev/null +++ b/emailadmin/doc/qmailuser.schema @@ -0,0 +1,103 @@ +# +# qmail-ldap v3 directory schema +# +# The offical qmail-ldap OID assigned by IANA is 7914 +# +# Created by: David E. Storey +# +# Modified and included into qmail-ldap by Andre Oppermann +# +# Schema fixes by Mike Jackson +# +# +# This schema depends on: +# - core.schema +# - cosine.schema +# - nis.schema +# + +# +# Example from new format +# +# attributetype ( 1.3.6.1.1.1.1.0 NAME 'uidNumber' +# DESC 'An integer uniquely identifying a user in an administrative domain' +# EQUALITY integerMatch +# SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) + +# Attribute Type Definitions + +attributetype ( 1.3.6.1.4.1.7914.1.2.1.1 NAME 'qmailUID' + DESC 'UID of the user on the mailsystem' + EQUALITY integerMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.7914.1.2.1.2 NAME 'qmailGID' + DESC 'GID of the user on the mailsystem' + EQUALITY integerMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.7914.1.2.1.3 NAME 'mailMessageStore' + DESC 'Path to the maildir/mbox on the mail system' + EQUALITY caseExactIA5Match + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.7914.1.2.1.4 NAME 'mailAlternateAddress' + DESC 'Secondary (alias) mailaddresses for the same user' + EQUALITY caseIgnoreIA5Match + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} ) + +attributetype ( 1.3.6.1.4.1.7914.1.2.1.5 NAME 'mailQuota' + DESC 'The amount of space the user can use until all further messages get bounced.' + SYNTAX 1.3.6.1.4.1.1466.115.121.1.44 SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.7914.1.2.1.6 NAME 'mailHost' + DESC 'On which qmail server the messagestore of this user is located.' + EQUALITY caseIgnoreIA5Match + SUBSTR caseIgnoreIA5SubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} SINGLE-VALUE) + +attributetype ( 1.3.6.1.4.1.7914.1.2.1.7 NAME 'mailForwardingAddress' + DESC 'Address(es) to forward all incoming messages to.' + EQUALITY caseIgnoreIA5Match + SUBSTR caseIgnoreIA5SubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} ) + +attributetype ( 1.3.6.1.4.1.7914.1.2.1.8 NAME 'deliveryProgramPath' + DESC 'Program to execute for all incoming mails.' + SYNTAX 1.3.6.1.4.1.1466.115.121.1.44 SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.7914.1.2.1.9 NAME 'qmailDotMode' + DESC 'Interpretation of .qmail files: both, dotonly, ldaponly, ldapwithprog, none' + EQUALITY caseIgnoreMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.44 SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.7914.1.2.1.10 NAME 'deliveryMode' + DESC 'multi field entries of: normal, forwardonly, nombox, localdelivery, reply, echo' + EQUALITY caseIgnoreMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.44 ) + +attributetype ( 1.3.6.1.4.1.7914.1.2.1.11 NAME 'mailReplyText' + DESC 'A reply text for every incoming message' + SUBSTR caseIgnoreSubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{4096} SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.7914.1.2.1.12 NAME 'accountStatus' + DESC 'The status of a user account: active, nopop, disabled, deleted' + EQUALITY caseIgnoreMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.44 SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.7914.1.2.1.14 NAME 'qmailAccountPurge' + DESC 'The earliest date when a mailMessageStore will be purged' + EQUALITY numericStringMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.36 SINGLE-VALUE ) + +# Object Class Definitions + +objectclass ( 1.3.6.1.4.1.7914.1.2.2.1 NAME 'qmailUser' + DESC 'QMail-LDAP User' SUP top AUXILIARY + MUST ( mail $ uid ) + MAY ( mailMessageStore $ homeDirectory $ userPassword $ + mailAlternateAddress $ qmailUID $ qmailGID $ mailQuota $ + mailHost $ mailForwardingAddress $ deliveryProgramPath $ + qmailDotMode $ deliveryMode $ mailReplyText $ + accountStatus $ qmailAccountPurge ) ) diff --git a/emailadmin/doc/smartsieve-NOTICE b/emailadmin/doc/smartsieve-NOTICE new file mode 100644 index 0000000000..c5ec127049 --- /dev/null +++ b/emailadmin/doc/smartsieve-NOTICE @@ -0,0 +1,19 @@ +SMARTSIEVE - SIEVE SCRIPT MANAGER +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Copyright 2002 Stephen Grier + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + diff --git a/emailadmin/doc/tcpmaps.conf b/emailadmin/doc/tcpmaps.conf new file mode 100644 index 0000000000..6e41c3e67b --- /dev/null +++ b/emailadmin/doc/tcpmaps.conf @@ -0,0 +1,16 @@ +# # Start it with: +# # initctl reload-configuration +# # initctl start tcpmaps + + +description "TCPMAP" +author "Ralf und Wim" + +start on runlevel [235] or starting postfix +stop on runlevel [S016] + +#pre-start exec /etc/vmware-tools/services.sh start +#post-stop exec /etc/vmware-tools/services.sh stop +respawn +instance tcpmaps +exec /usr/local/bin/postfix_tcp_map_ads.php --log syslog localhost diff --git a/emailadmin/inc/class.dbmaildbmailuser.inc.php b/emailadmin/inc/class.dbmaildbmailuser.inc.php new file mode 100755 index 0000000000..4cb951df79 --- /dev/null +++ b/emailadmin/inc/class.dbmaildbmailuser.inc.php @@ -0,0 +1,185 @@ + + * @author Klaus Leithoff + * @author Lars Kneschke + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @version $Id$ + */ + +/** + * Support for DBMail IMAP with qmailUser LDAP schema + * + * @todo base this class on dbmailqmailuser or the other way around + */ +class dbmaildbmailuser extends emailadmin_imap +{ + /** + * Label shown in EMailAdmin + */ + const DESCRIPTION = 'dbmail (dbmailUser Schema)'; + + /** + * Capabilities of this class (pipe-separated): default, sieve, admin, logintypeemail + */ + const CAPABILITIES = 'default|sieve'; + + function addAccount($_hookValues) { + return $this->updateAccount($_hookValues); + } + + #function deleteAccount($_hookValues) { + #} + function getUserData($_username) { + $userData = array(); + + $ds = $GLOBALS['egw']->ldap->ldapConnect( + $GLOBALS['egw_info']['server']['ldap_host'], + $GLOBALS['egw_info']['server']['ldap_root_dn'], + $GLOBALS['egw_info']['server']['ldap_root_pw'] + ); + + if(!is_resource($ds)) { + return false; + } + + $filter = '(&(objectclass=posixaccount)(uid='. $_username .')(dbmailGID='. sprintf("%u", crc32($GLOBALS['egw_info']['server']['install_id'])) .'))'; + $justthese = array('dn', 'objectclass', 'mailQuota'); + if($sri = ldap_search($ds, $GLOBALS['egw_info']['server']['ldap_context'], $filter, $justthese)) { + + if($info = ldap_get_entries($ds, $sri)) { + if(isset($info[0]['mailquota'][0])) { + $userData['quotaLimit'] = $info[0]['mailquota'][0] / 1048576; + } + } + } + return $userData; + } + + function updateAccount($_hookValues) { + if(!$uidnumber = (int)$_hookValues['account_id']) { + return false; + } + + $ds = $GLOBALS['egw']->ldap->ldapConnect( + $GLOBALS['egw_info']['server']['ldap_host'], + $GLOBALS['egw_info']['server']['ldap_root_dn'], + $GLOBALS['egw_info']['server']['ldap_root_pw'] + ); + + if(!is_resource($ds)) { + return false; + } + + $filter = '(&(objectclass=posixaccount)(uidnumber='. $uidnumber .'))'; + $justthese = array('dn', 'objectclass', 'dbmailUID', 'dbmailGID', 'mail'); + $sri = ldap_search($ds, $GLOBALS['egw_info']['server']['ldap_context'], $filter, $justthese); + + if($info = ldap_get_entries($ds, $sri)) { + if((!in_array('dbmailuser',$info[0]['objectclass']) && !in_array('dbmailUser',$info[0]['objectclass'])) && $info[0]['mail']) { + $newData['objectclass'] = $info[0]['objectclass']; + unset($newData['objectclass']['count']); + $newData['objectclass'][] = 'dbmailuser'; + sort($newData['objectclass']); + $newData['dbmailGID'] = sprintf("%u", crc32($GLOBALS['egw_info']['server']['install_id'])); + $newData['dbmailUID'] = (!empty($this->domainName)) ? $_hookValues['account_lid'] .'@'. $this->domainName : $_hookValues['account_lid']; + + if(!ldap_modify($ds, $info[0]['dn'], $newData)) { + #print ldap_error($ds); + } + + return true; + } else { + $newData = array(); + $newData['dbmailUID'] = (!empty($this->domainName)) ? $_hookValues['account_lid'] .'@'. $this->domainName : $_hookValues['account_lid']; + $newData['dbmailGID'] = sprintf("%u", crc32($GLOBALS['egw_info']['server']['install_id'])); + + if(!ldap_modify($ds, $info[0]['dn'], $newData)) { + print ldap_error($ds); + _debug_array($newData); + exit; + #return false; + } + } + } + + return false; + } + + function setUserData($_username, $_quota) { + $ds = $GLOBALS['egw']->ldap->ldapConnect( + $GLOBALS['egw_info']['server']['ldap_host'], + $GLOBALS['egw_info']['server']['ldap_root_dn'], + $GLOBALS['egw_info']['server']['ldap_root_pw'] + ); + + if(!is_resource($ds)) { + return false; + } + + $filter = '(&(objectclass=posixaccount)(uid='. $_username .'))'; + $justthese = array('dn', 'objectclass', 'dbmailGID', 'dbmailUID', 'mail'); + $sri = ldap_search($ds, $GLOBALS['egw_info']['server']['ldap_context'], $filter, $justthese); + + if($info = ldap_get_entries($ds, $sri)) { + $validLDAPConfig = false; + if(in_array('dbmailuser',$info[0]['objectclass']) || in_array('dbmailUser',$info[0]['objectclass'])) { + $validLDAPConfig = true; + } + + if(!in_array('dbmailuser',$info[0]['objectclass']) && !in_array('dbmailUser',$info[0]['objectclass']) && $info[0]['mail']) { + $newData['objectclass'] = $info[0]['objectclass']; + unset($newData['objectclass']['count']); + $newData['objectclass'][] = 'dbmailUser'; + sort($newData['objectclass']); + $newData['dbmailGID'] = sprintf("%u", crc32($GLOBALS['egw_info']['server']['install_id'])); + $newData['dbmailUID'] = (!empty($this->domainName)) ? $_username .'@'. $this->domainName : $_username; + + if(ldap_modify($ds, $info[0]['dn'], $newData)) { + $validLDAPConfig = true; + } + } else { + if ((in_array('dbmailuser',$info[0]['objectclass']) || in_array('dbmailUser',$info[0]['objectclass'])) && !$info[0]['dbmailuid']) { + $newData = array(); + $newData['dbmailUID'] = (!empty($this->domainName)) ? $_username .'@'. $this->domainName : $_username; + + if(!ldap_modify($ds, $info[0]['dn'], $newData)) { + #print ldap_error($ds); + #return false; + } + } + + if ((in_array('dbmailuser',$info[0]['objectclass']) || in_array('dbmailUser',$info[0]['objectclass'])) && !$info[0]['dbmailgid']) { + $newData = array(); + $newData['dbmailGID'] = sprintf("%u", crc32($GLOBALS['egw_info']['server']['install_id'])); + + if(!ldap_modify($ds, $info[0]['dn'], $newData)) { + #print ldap_error($ds); + #return false; + } + } + } + + if($validLDAPConfig) { + $newData = array(); + + if((int)$_quota >= 0) { + $newData['mailQuota'] = (int)$_quota * 1048576; + } else { + $newData['mailQuota'] = array(); + } + + if(!ldap_modify($ds, $info[0]['dn'], $newData)) { + #print ldap_error($ds); + return false; + } + } + return true; + } + return false; + } +} diff --git a/emailadmin/inc/class.dbmailqmailuser.inc.php b/emailadmin/inc/class.dbmailqmailuser.inc.php new file mode 100644 index 0000000000..ff0a59dc4a --- /dev/null +++ b/emailadmin/inc/class.dbmailqmailuser.inc.php @@ -0,0 +1,164 @@ + + * @author Klaus Leithoff + * @author Lars Kneschke + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @version $Id$ + */ + +/** + * Support for DBMail IMAP with qmailUser LDAP schema + * + * @todo base this class on dbmaildbmailuser or the other way around + */ +class dbmailqmailuser extends emailadmin_imap +{ + /** + * Label shown in EMailAdmin + */ + const DESCRIPTION = 'dbmail (qmailUser Schema)'; + + /** + * Capabilities of this class (pipe-separated): default, sieve, admin, logintypeemail + */ + const CAPABILITIES = 'default|sieve'; + + function addAccount($_hookValues) { + return $this->updateAccount($_hookValues); + } + + #function deleteAccount($_hookValues) { + #} + function getUserData($_username) { + $userData = array(); + + $ds = $GLOBALS['egw']->ldap->ldapConnect( + $GLOBALS['egw_info']['server']['ldap_host'], + $GLOBALS['egw_info']['server']['ldap_root_dn'], + $GLOBALS['egw_info']['server']['ldap_root_pw'] + ); + + if(!is_resource($ds)) { + return false; + } + + $filter = '(&(objectclass=posixaccount)(uid='. $_username .')(qmailGID='. sprintf("%u", crc32($GLOBALS['egw_info']['server']['install_id'])) .'))'; + $justthese = array('dn', 'objectclass', 'mailQuota'); + if($sri = ldap_search($ds, $GLOBALS['egw_info']['server']['ldap_context'], $filter, $justthese)) { + + if($info = ldap_get_entries($ds, $sri)) { + if(isset($info[0]['mailquota'][0])) { + $userData['quotaLimit'] = $info[0]['mailquota'][0] / 1048576; + } + } + } + return $userData; + } + + function updateAccount($_hookValues) { + if(!$uidnumber = (int)$_hookValues['account_id']) { + return false; + } + + $ds = $GLOBALS['egw']->ldap->ldapConnect( + $GLOBALS['egw_info']['server']['ldap_host'], + $GLOBALS['egw_info']['server']['ldap_root_dn'], + $GLOBALS['egw_info']['server']['ldap_root_pw'] + ); + + if(!is_resource($ds)) { + return false; + } + + $filter = '(&(objectclass=posixaccount)(uidnumber='. $uidnumber .'))'; + $justthese = array('dn', 'objectclass', 'qmailUID', 'qmailGID', 'mail'); + $sri = ldap_search($ds, $GLOBALS['egw_info']['server']['ldap_context'], $filter, $justthese); + + if($info = ldap_get_entries($ds, $sri)) { + if(!in_array('qmailuser',$info[0]['objectclass']) && $info[0]['email']) { + $newData['objectclass'] = $info[0]['objectclass']; + unset($newData['objectclass']['count']); + $newData['objectclass'][] = 'qmailuser'; + sort($newData['objectclass']); + $newData['qmailGID'] = sprintf("%u", crc32($GLOBALS['egw_info']['server']['install_id'])); + #$newData['qmailUID'] = (!empty($this->domainName)) ? $_username .'@'. $this->domainName : $_username; + + ldap_modify($ds, $info[0]['dn'], $newData); + + return true; + } else { + $newData = array(); + $newData['qmailGID'] = sprintf("%u", crc32($GLOBALS['egw_info']['server']['install_id'])); + #$newData['qmailUID'] = (!empty($this->domainName)) ? $_username .'@'. $this->domainName : $_username; + + if(!ldap_modify($ds, $info[0]['dn'], $newData)) { + #print ldap_error($ds); + #return false; + } + } + } + + return false; + } + + function setUserData($_username, $_quota) { + $ds = $GLOBALS['egw']->ldap->ldapConnect( + $GLOBALS['egw_info']['server']['ldap_host'], + $GLOBALS['egw_info']['server']['ldap_root_dn'], + $GLOBALS['egw_info']['server']['ldap_root_pw'] + ); + + if(!is_resource($ds)) { + return false; + } + + $filter = '(&(objectclass=posixaccount)(uid='. $_username .'))'; + $justthese = array('dn', 'objectclass', 'qmailGID', 'mail'); + $sri = ldap_search($ds, $GLOBALS['egw_info']['server']['ldap_context'], $filter, $justthese); + + if($info = ldap_get_entries($ds, $sri)) { + #_debug_array($info); + if(!in_array('qmailuser',$info[0]['objectclass']) && $info[0]['email']) { + $newData['objectclass'] = $info[0]['objectclass']; + unset($newData['objectclass']['count']); + $newData['objectclass'][] = 'qmailuser'; + sort($newData['objectclass']); + $newData['qmailGID'] = sprintf("%u", crc32($GLOBALS['egw_info']['server']['install_id'])); + + ldap_modify($ds, $info[0]['dn'], $newData); + } else { + if (in_array('qmailuser',$info[0]['objectclass']) && !$info[0]['qmailgid']) { + $newData = array(); + $newData['qmailGID'] = sprintf("%u", crc32($GLOBALS['egw_info']['server']['install_id'])); + + if(!ldap_modify($ds, $info[0]['dn'], $newData)) { + #print ldap_error($ds); + #return false; + } + } + } + + $newData = array(); + + if((int)$_quota >= 0) { + $newData['mailQuota'] = (int)$_quota * 1048576; + } else { + $newData['mailQuota'] = array(); + } + + if(!ldap_modify($ds, $info[0]['dn'], $newData)) { + #print ldap_error($ds); + return false; + } + + return true; + } + + return false; + } +} diff --git a/emailadmin/inc/class.defaultimap.inc.php b/emailadmin/inc/class.defaultimap.inc.php new file mode 100644 index 0000000000..79d8cef808 --- /dev/null +++ b/emailadmin/inc/class.defaultimap.inc.php @@ -0,0 +1,160 @@ + + * @author Stylite AG + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @version $Id$ + */ + +define('IMAP_NAMESPACE_PERSONAL', 'personal'); +define('IMAP_NAMESPACE_OTHERS' , 'others'); +define('IMAP_NAMESPACE_SHARED' , 'shared'); +define('IMAP_NAMESPACE_ALL' , 'all'); + +/** + * This class holds all information about the imap connection. + * This is the base class for all other imap classes. + * + * Also proxies Sieve calls to emailadmin_sieve (eg. it behaves like the former felamimail bosieve), + * to allow IMAP plugins to also manage Sieve connection. + */ +interface defaultimap +{ + /** + * adds a account on the imap server + * + * @param array $_hookValues + * @return bool true on success, false on failure + */ + function addAccount($_hookValues); + + /** + * updates a account on the imap server + * + * @param array $_hookValues + * @return bool true on success, false on failure + */ + function updateAccount($_hookValues); + + /** + * deletes a account on the imap server + * + * @param array $_hookValues + * @return bool true on success, false on failure + */ + function deleteAccount($_hookValues); + + /** + * converts a foldername from current system charset to UTF7 + * + * @param string $_folderName + * @return string the encoded foldername + */ + function encodeFolderName($_folderName); + + /** + * returns the supported capabilities of the imap server + * return false if the imap server does not support capabilities + * + * @return array the supported capabilites + */ + function getCapabilities(); + + /** + * return the delimiter used by the current imap server + * + * @return string the delimimiter + */ + function getDelimiter(); + + /** + * get the effective Username for the Mailbox, as it is depending on the loginType + * @param string $_username + * @return string the effective username to be used to access the Mailbox + */ + function getMailBoxUserName($_username); + + /** + * Create mailbox string from given mailbox-name and user-name + * + * @param string $_folderName='' + * @return string utf-7 encoded (done in getMailboxName) + */ + function getUserMailboxString($_username, $_folderName=''); + + /** + * get list of namespaces + * + * @return array with keys 'personal', 'shared' and 'others' and value array with values for keys 'name' and 'delimiter' + */ + function getNameSpaceArray(); + /** + * return the quota for another user + * used by admin connections only + * + * @param string $_username + * @param string $_what - what to retrieve either QMAX, USED or ALL is supported + * @return mixed the quota for specified user (by what) or array with all available Quota Information, or false + */ + function getQuotaByUser($_username, $_what='QMAX'); + + /** + * returns information about a user + * + * Only a stub, as admin connection requires, which is only supported for Cyrus + * + * @param string $_username + * @return array userdata + */ + function getUserData($_username); + + /** + * opens a connection to a imap server + * + * @param bool $_adminConnection create admin connection if true + * @param int $_timeout=null timeout in secs, if none given fmail pref or default of 20 is used + * @throws Exception on error + */ + function openConnection($_adminConnection=false, $_timeout=null); + + /** + * set userdata + * + * @param string $_username username of the user + * @param int $_quota quota in bytes + * @return bool true on success, false on failure + */ + function setUserData($_username, $_quota); + + /** + * check if imap server supports given capability + * + * @param string $_capability the capability to check for + * @return bool true if capability is supported, false if not + */ + function supportsCapability($_capability); + + /** + * Set vacation message for given user + * + * @param int|string $_euser nummeric account_id or imap username + * @param array $_vacation + * @param string $_scriptName=null + * @return boolean + */ + public function setVacationUser($_euser, array $_vacation, $_scriptName=null); + + /** + * Get vacation message for given user + * + * @param int|string $_euser nummeric account_id or imap username + * @param string $_scriptName=null + * @throws Exception on connection error or authentication failure + * @return array + */ + public function getVacationUser($_euser, $_scriptName=null); +} diff --git a/emailadmin/inc/class.defaultsmtp.inc.php b/emailadmin/inc/class.defaultsmtp.inc.php new file mode 100644 index 0000000000..6cc015507b --- /dev/null +++ b/emailadmin/inc/class.defaultsmtp.inc.php @@ -0,0 +1,20 @@ + + * @author Ralf Becker + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License Version 2+ + * @version $Id$ + */ + +/** + * EMailAdmin generic base class for SMTP + * + * @deprecated use emailadmin_smtp + */ +class defaultsmtp extends emailadmin_smtp +{ +} diff --git a/emailadmin/inc/class.emailadmin_account.inc.php b/emailadmin/inc/class.emailadmin_account.inc.php new file mode 100644 index 0000000000..51428c8dc5 --- /dev/null +++ b/emailadmin/inc/class.emailadmin_account.inc.php @@ -0,0 +1,1477 @@ + + * @author Stylite AG + * @copyright (c) 2013-14 by Ralf Becker + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @version $Id$ + */ + +/** + * Mail accounts supports 3 types of accounts: + * + * a) personal mail accounts either created by admin or user themselfs + * b) accounts for multiple users or groups created by admin + * c) configuration to administrate a mail-server + * + * To store the accounts 4 tables are used + * - egw_ea_accounts all data except credentials and identities (incl. signature) + * - egw_ea_valid for which users an account is valid 1:N relation to accounts table + * - egw_ea_credentials username/password for various accounts and types (imap, smtp, admin) + * - egw_ea_identities identities of given account and user incl. standard identity of account + * - egw_ea_notifications folders a user wants to be notified about new mails + * + * Most methods return iterators: use iterator_to_array() to cast them to an array eg. for eTemplate use. + * + * @property-read int $acc_id id + * @property-read string $acc_name description / display name + * @property-read string $acc_imap_host imap hostname + * @property-read int $acc_imap_ssl 0=none, 1=starttls, 2=tls, 3=ssl, &8=validate certificate + * @property-read int $acc_imap_port imap port, default 143 or for ssl 993 + * @property-read string $acc_imap_username + * @property-read string $acc_imap_password + * @property-read boolean $acc_sieve_enabled sieve enabled + * @property-read string $acc_sieve_host sieve host, default imap_host + * @property-read int $acc_sieve_ssl 0=none, 1=starttls, 2=tls, 3=ssl, &8=validate certificate + * @property-read int $acc_sieve_port sieve port, default 4190, old non-ssl port 2000 or ssl 5190 + * @property-read string $acc_folder_sent sent folder + * @property-read string $acc_folder_trash trash folder + * @property-read string $acc_folder_draft draft folder + * @property-read string $acc_folder_template template folder + * @property-read string $acc_folder_junk junk/spam folder + * @property-read string $acc_smtp_host smtp hostname + * @property-read int $acc_smtp_ssl 0=none, 1=starttls, 2=tls, 3=ssl, &8=validate certificate + * @property-read int $acc_smtp_port smtp port + * @property-read string $acc_smtp_username if smtp auth required + * @property-read string $acc_smtp_password + * @property-read string $acc_smtp_type smtp class to use, default emailadmin_smtp + * @property-read string $acc_imap_type imap class to use, default emailadmin_imap + * @property-read string $acc_imap_logintype how to construct login-name standard, vmailmgr, admin, uidNumber + * @property-read string $acc_domain domain name + * @property-read boolean $acc_imap_administration enable administration + * @property-read string $acc_admin_username + * @property-read string $acc_admin_password + * @property-read boolean $acc_further_identities are non-admin users allowed to create further identities + * @property-read boolean $acc_user_editable are non-admin users allowed to edit this account, if it is for them + * @property-read int $acc_modified timestamp of last modification + * @property-read int $acc_modifier account_id of last modifier + * @property-read int $ident_id standard identity + * @property-read string $ident_name name of identity + * @property-read string $ident_realname real name + * @property-read string $ident_email email address + * @property-read string $ident_org organisation + * @property-read string $ident_signature signature text (html) + * @property-read array $params parameters passed to constructor (all above as array) + * @property-read array $account_id account-ids this mail account is valid for, 0=everyone + * @property-read int $user account-id class is instanciated for + * @property-read string $mailLocalAddress mail email address + * @property-read array $mailAlternateAddress further email addresses + * @property-read array $mailForwardingAddress address(es) to forward to + * @property-read string $accountStatus "active", if account is enabled to receive mail + * @property-read string $deliveryMode "forwardOnly", if account only forwards (no imap account!) + * @property-read int $quotaLimit quota limit in MB + * @property-read int $quotaUsed quota usage in MB + * @property-read int $acc_imap_default_quota quota in MB, if no user specific one set + * @property-read int $acc_imap_timeout timeout for imap connection, default 20s + * @property-read array $notif_folders folders user wants to be notified about new mails + * + * @todo remove comments from protected in __construct and db2data, once we require PHP 5.4 (keeping class contect in closures) + */ +class emailadmin_account implements ArrayAccess +{ + const APP = 'emailadmin'; + /** + * Table with mail-accounts + */ + const TABLE = 'egw_ea_accounts'; + /** + * Table holding 1:N relation for which EGroupware accounts a mail-account is valid + */ + const VALID_TABLE = 'egw_ea_valid'; + /** + * Join with egw_ea_valid + */ + const VALID_JOIN = 'JOIN egw_ea_valid ON egw_ea_valid.acc_id=egw_ea_accounts.acc_id '; + /** + * Join with egw_ea_valid + */ + const ALL_VALID_JOIN = 'LEFT JOIN egw_ea_valid all_valid ON all_valid.acc_id=egw_ea_accounts.acc_id '; + /** + * Table with identities and signatures + */ + const IDENTITIES_TABLE = 'egw_ea_identities'; + /** + * Join with standard identity of main-account + */ + const IDENTITY_JOIN = 'JOIN egw_ea_identities ON egw_ea_identities.ident_id=egw_ea_accounts.ident_id'; + /** + * Order for search: first group-profiles, then general profiles, then personal profiles + */ + const DEFAULT_ORDER = 'egw_ea_valid.account_id ASC,ident_org ASC,ident_realname ASC,acc_name ASC'; + + /** + * JOIN to join in admin user, eg. to check if we have admin credentials + */ + const ADMIN_JOIN = 'LEFT JOIN egw_ea_credentials ON egw_ea_credentials.acc_id=egw_ea_accounts.acc_id AND cred_type=8'; + const ADMIN_COL = 'cred_username AS acc_imap_admin_username'; + + /** + * No SSL + */ + const SSL_NONE = 0; + /** + * STARTTLS on regular tcp connection/port + */ + const SSL_STARTTLS = 1; + /** + * SSL (inferior to TLS!) + */ + const SSL_SSL = 3; + /** + * require TLS version 1+, no SSL version 2 or 3 + */ + const SSL_TLS = 2; + /** + * if set, verify certifcate (currently not implemented in Horde_Imap_Client!) + */ + const SSL_VERIFY = 8; + + /** + * Default timeout, if no account specific one is set + */ + const DEFAULT_TIMEOUT = 20; + + /** + * Reference to global db object + * + * @var egw_db + */ + static protected $db; + + /** + * Parameters passed to contructor + * + * @var array + */ + protected $params = array(); + + /** + * Instance of imap server + * + * @var emailadmin_imap + */ + protected $imapServer; + + /** + * Instance of old imap server + * + * @var emailadmin_oldimap + */ + protected $oldImapServer; + + /** + * Instance of smtp server + * + * @var emailadmin_smtp + */ + protected $smtpServer; + + /** + * Instanciated account object by acc_id, read acts as singelton + * + * @var array + */ + protected static $instances = array(); + + /** + * Cache for emailadmin_account::read() to minimize database access + * + * @var array + */ + protected static $cache = array(); + + /** + * Cache for emailadmin_account::search() to minimize database access + */ + protected static $search_cache = array(); + + /** + * account_id class was instanciated for ($called_for parameter of constructor or current user) + * + * @var int + */ + protected $user; + + /** + * Name of certain user-data fields which need to get queried by imap or smtp backends + * + * @var array + */ + static public $user_data = array( + 'mailLocalAddress', 'mailAlternateAddress', 'mailForwardingAddress', + 'accountStatus', 'deliveryMode', 'quotaLimit', 'quotaUsed', + ); + + /** + * Constructor + * + * Should be protected, but php5.3 does NOT keep class context in closures. + * So 'til we require 5.4, it is public BUT SHOULD NOT BE USED! + * + * @param array $params + * @param int $called_for=null if set access to given user (without smtp credentials!), + * default current user AND read username/password from current users session + */ + /*protected*/ function __construct(array $params, $called_for=null) + { + // read credentials from database + $params += emailadmin_credentials::read($params['acc_id'], null, $called_for ? array(0, $called_for) : $called_for); + + if (!isset($params['notify_folders'])) + { + $params += emailadmin_notifications::read($params['acc_id'], $called_for ? array(0, $called_for) : $called_for); + } + if (!empty($params['acc_imap_logintype']) && empty($params['acc_imap_username']) && + $GLOBALS['egw_info']['user']['account_id'] && + (!isset($called_for) || $called_for == $GLOBALS['egw_info']['user']['account_id'])) + { + // get usename/password from current user, let it overwrite credentials for all/no session + $params = emailadmin_credentials::from_session( + (!isset($called_for) ? array() : array('acc_smtp_auth_session' => false)) + $params, !isset($called_for) + ) + $params; + } + $this->params = $params; + + unset($this->imapServer); + unset($this->oldImapServer); + unset($this->smtpServer); + + $this->user = $called_for ? $called_for : $GLOBALS['egw_info']['user']['account_id']; + } + + /** + * Query quota, aliases, forwards, ... from imap and smtp backends and sets them as parameters on current object + * + * @return array with values for keys in self::$user_data + */ + public function getUserData() + { + if ($this->acc_smtp_type != 'emailadmin_smtp' && $this->smtpServer() && + ($smtp_data = $this->smtpServer->getUserData($this->user))) + { + $this->params += $smtp_data; + } + // if we manage the mail-account, include that data too (imap has higher precedence) + try { + if ($this->acc_imap_type != 'emailadmin_imap' && + // do NOT query IMAP server, if we are in forward-only delivery-mode, imap will NOT answer, as switched off for that account! + $this->params['deliveryMode'] != emailadmin_smtp::FORWARD_ONLY && + $this->imapServer() && is_a($this->imapServer, 'emailadmin_imap') && + ($data = $this->imapServer->getUserData($GLOBALS['egw']->accounts->id2name($this->user)))) + { + $this->params = array_merge($this->params, $data); + } + } + catch(Horde_Imap_Client_Exception $e) { + unset($e); + // ignore eg. connection errors + } + catch(InvalidArgumentException $e) { + unset($e); + // ignore eg. missing admin user + } + $this->params += array_fill_keys(self::$user_data, null); // make sure all keys exist now + + return (array)$data + (array)$smtp_data; + } + + /** + * Get new Horde_Imap_Client imap server object + * + * @param bool|int|string $_adminConnection create admin connection if true or account_id or imap username + * @param int $_timeout=null timeout in secs, if none given fmail pref or default of 20 is used + * @return emailadmin_imap + */ + public function imapServer($_adminConnection=false, $_timeout=null) + { + if (!isset($this->imapServer)) + { + // make sure mbstring.func_overload=0 + static $func_overload = null; + if (is_null($func_overload)) $func_overload = extension_loaded('mbstring') ? ini_get('mbstring.func_overload') : 0; + if ($func_overload) throw new egw_exception_assertion_failed('Fatal Error: EGroupware requires mbstring.func_overload=0 set in your php.ini!'); + + $class = self::getIcClass($this->params['acc_imap_type']); + $this->imapServer = new $class($this->params, $_adminConnection, $_timeout); + } + return $this->imapServer; + } + + /** + * Check if account is an imap account + * + * Checks if an imap host, username and for managaged mail-servers accountStatus="active" and NOT deliveryMode="forwardOnly" is set + * + * return boolean + */ + public function is_imap() + { + return !empty($this->acc_imap_host) && !empty($this->acc_imap_username) && !empty($this->acc_imap_password) && + ($this->acc_smtp_type == 'emailadmin_smtp' || + $this->deliveryMode != emailadmin_smtp::FORWARD_ONLY && $this->accountStatus == emailadmin_smtp::MAIL_ENABLED); + } + + /** + * Get name and evtl. autoload incomming server class + * + * @param string $imap_type + * @param boolean $old_ic_server=false true: return emailadmin_oldimap as icServer, false: use new emailadmin_imap + * @return string + */ + public static function getIcClass($imap_type, $old_ic_server=false) + { + static $old2new_icClass = array( + 'defaultimap' => 'emailadmin_imap', + 'cyrusimap' => 'emailadmin_imap_cyrus', + 'emailadmin_dovecot' => 'emailadmin_imap_dovecot', + 'dbmaildbmailuser' => 'emailadmin_imap_dbmail', + 'dbmailqmailuser' => 'emailadmin_imap_dbmail_qmail', + ); + + // convert icClass to new name + $icClass = $imap_type; + if (isset($old2new_icClass[$icClass])) + { + $icClass = $old2new_icClass[$icClass]; + } + // if old Net_IMAP based class requested, always return emailadmin_oldimap + if ($old_ic_server) + { + $icClass = 'emailadmin_oldimap'; + } + + // fetch the IMAP / incomming server data + if (!class_exists($icClass)) + { + if (file_exists($file=EGW_INCLUDE_ROOT.'/emailadmin/inc/class.'.$icClass.'.inc.php')) + { + include_once($file); + } + else // use default imap classes + { + $icClass = $old_ic_server ? 'emailadmin_oldimap' : 'emailadmin_imap'; + } + } + return $icClass; + } + + /** + * Get smtp server object + * + * @return emailadmin_smtp + */ + public function smtpServer() + { + if (!isset($this->smtpServer)) + { + $this->smtpServer = self::_smtp($this->params); + } + return $this->smtpServer; + } + + /** + * Factory method to instanciate smtp server object + * + * @param array $params + * @return emailadmin_smtp + * @throws egw_exception_wrong_parameter + */ + protected static function _smtp(array $params) + { + $class = $params['acc_smtp_type']; + if ($class=='defaultsmtp') $class='emailadmin_smtp'; + $smtp = new $class($params); + $smtp->editForwardingAddress = false; + $smtp->host = $params['acc_smtp_host']; + $smtp->port = $params['acc_smtp_port']; + switch($params['acc_smtp_ssl']) + { + case self::SSL_TLS: // requires modified PHPMailer, or comment next two lines to use just ssl! + $smtp->host = 'tlsv1://'.$smtp->host; + break; + case self::SSL_SSL: + $smtp->host = 'ssl://'.$smtp->host; + break; + case self::SSL_STARTTLS: // PHPMailer uses 'tls' for STARTTLS, not ssl connection with tls version >= 1 and no sslv2/3 + $smtp->host = 'tls://'.$smtp->host; + } + $smtp->smtpAuth = !empty($params['acc_smtp_username']); + $smtp->username = $params['acc_smtp_username']; + $smtp->password = $params['acc_smtp_password']; + $smtp->defaultDomain = $params['acc_domain']; + + return $smtp; + } + + /** + * Get identities of given or current account (for current user!) + * + * Standard identity is always first (as it has account_id=0 and we order account_id ASC). + * + * @param int|array|emailadmin_account $account=null default this account, empty array() to get all identities of current user + * @param boolean $replace_placeholders=false should placeholders like {{n_fn}} be replaced + * @param string $field='name' what to return as value: "ident_(realname|org|email|signature)" or default "name"=result from identity_name + * @return Iterator ident_id => identity_name of identity + */ + public /*static*/ function identities($account=null, $replace_placeholders=true, $field='name') + { + if (is_null($account)) $account = $this; + $acc_id = is_scalar($account) ? $account : $account['acc_id']; + + $cols = array('ident_id', 'ident_name', 'ident_realname', 'ident_org', 'ident_email', 'acc_id', 'acc_imap_username', 'acc_imap_logintype', 'acc_domain'); + if (!in_array($field, array_merge($cols, array('name', 'params')))) + { + $cols[] = $field; + } + $cols[array_search('ident_id', $cols)] = self::IDENTITIES_TABLE.'.ident_id AS ident_id'; + $cols[array_search('acc_id', $cols)] = self::IDENTITIES_TABLE.'.acc_id AS acc_id'; + $cols[array_search('acc_imap_username', $cols)] = emailadmin_credentials::TABLE.'.cred_username AS acc_imap_username'; + + $where[] = self::$db->expression(self::IDENTITIES_TABLE, self::IDENTITIES_TABLE.'.', array('account_id' => self::memberships())); + if ($acc_id) + { + $where[] = self::$db->expression(self::IDENTITIES_TABLE, self::IDENTITIES_TABLE.'.', array('acc_id' => $acc_id)); + } + $rs = self::$db->select(self::IDENTITIES_TABLE, $cols, $where, __LINE__, __FILE__, false, + 'ORDER BY '.self::IDENTITIES_TABLE.'.account_id,ident_realname,ident_org,ident_email', self::APP, null, + ' JOIN '.self::TABLE.' ON '.self::TABLE.'.acc_id='.self::IDENTITIES_TABLE.'.acc_id'. + ' LEFT JOIN '.emailadmin_credentials::TABLE.' ON '.self::TABLE.'.acc_id='.emailadmin_credentials::TABLE.'.acc_id AND '. + emailadmin_credentials::TABLE.'.account_id='.(int)$GLOBALS['egw_info']['user']['account_id'].' AND '. + 'cred_type&'.emailadmin_credentials::IMAP); + //error_log(__METHOD__."(acc_id=$acc_id, replace_placeholders=$replace_placeholders, field='$field') sql=".$rs->sql); + + return new egw_db_callback_iterator($rs, + // process each row + function($row) use ($replace_placeholders, $field) + { + // set email from imap-username (evtl. set from session, if acc_imap_logintype specified) + if (in_array($field, array('name', 'ident_email', 'params')) && + empty($row['ident_email']) && empty($row['acc_imap_username']) && $row['acc_imap_logintype']) + { + $row = array_merge($row, emailadmin_credentials::from_session($row)); + } + if (empty($row['ident_email'])) $row['ident_email'] = $row['acc_imap_username']; + + if ($field != 'name') + { + $data = $replace_placeholders ? array_merge($row, emailadmin_account::replace_placeholders($row)) : $row; + return $field == 'params' ? $data : $data[$field]; + } + return emailadmin_account::identity_name($row, $replace_placeholders); + }, array(), + function($row) { return $row['ident_id'];}); + } + + /** + * Get rfc822 email address from given identity or account + * + * @param array|emailadmin_account $identity + * @return string rfc822 email address from given identity or account + */ + public static function rfc822($identity) + { + $address = $identity['ident_realname']; + if ($identity['ident_org']) + { + $address .= ($address && $identity['ident_org'] ? ' ' : '').$identity['ident_org']; + } + if (strpos($address, ',') !== false) // need to quote comma + { + $address = '"'.str_replace('"', '\\"', $address).'"'; + } + if (!strpos($identity['ident_email'], '@')) + { + $address = null; + } + elseif ($address) + { + $address = $address.' <'.$identity['ident_email'].'>'; + } + else + { + $address = $identity['ident_email']; + } + //error_log(__METHOD__."(acc_id=$identity[acc_id], ident_id=$identity[ident_id], realname=$identity[ident_realname], org=$identity[ident_org], email=$identity[ident_email]) returning ".array2string($address)); + return $address; + } + + /** + * Get list of rfc822 addresses for current user eg. to use as from address selection when sending mail + * + * @param callback $formatter=null function to format identity as rfc822 address, default self::rfc822(), + * @return array acc_id:ident_id:email => rfc822 address pairs, eg. '1:1:rb@stylite.de' => 'Ralf Becker Stylite AG ' + * @todo add aliases for manged mail servers + */ + public static function rfc822_addresses($formatter=null) + { + if (!$formatter || !is_callable($formatter)) + { + $formatter = __CLASS__.'::rfc822'; + } + $addresses = array(); + foreach(self::search(true, false) as $acc_id => $account) + { + $added = false; // make sure each account get's at least added once, even if it uses an identical email address + foreach($account->identities(null, true, 'params') as $identity) + { + if (($address = call_user_func($formatter, $identity)) && (!$added || !in_array($address, $addresses))) + { + $addresses[$acc_id.':'.$identity['ident_id'].':'.$identity['ident_email']] = $address; + $added = true; + } + } + } + // sort caseinsensitiv alphabetical + uasort($addresses, 'strcasecmp'); + //error_log(__METHOD__."() returning ".array2string($addresses)); + return $addresses; + } + + /** + * Return list of identities/signatures for given account ordered by give email on top and then by identity name + * + * @param int|array|emailadmin_account $account=null default this account, empty array() to get all identities of current user + * @param string $order_email_top email address to order top + * @return array ident_id => ident_name pairs + */ + public /*static*/ function identities_ordered($account, $order_email_top) + { + $identities = iterator_to_array(self::identities($account, true, 'params')); + uasort($identities, function($a, $b) use ($order_email_top) + { + $cmp = !strcasecmp($order_email_top, $a['ident_email']) - !strcasecmp($order_email_top, $b['ident_email']); + if (!$cmp) + { + $cmp = strcasecmp($a['ident_name'], $b['ident_name']); + } + return $cmp; + }); + foreach($identities as &$identity) + { + $identity = self::identity_name($identity); + } + //error_log(__METHOD__."(".array2string($account).", '$order_email_top') returning ".array2string($identities)); + return $identities; + } + + /** + * Replace placeholders like {{n_fn}} in an identity + * + * For full list of placeholders see addressbook_merge. + * + * @param array|emailadmin_account $identity=null + * @return array with modified fields + */ + public /*static*/ function replace_placeholders($identity=null) + { + static $fields = array('ident_name','ident_realname','ident_org','ident_email','ident_signature'); + + if (!$identity && isset($this)) $identity = $this; + if (!is_array($identity) && !is_a($identity, 'emailadmin_account')) + { + throw new egw_exception_wrong_parameter(__METHOD__."() requires an identity or account as first parameter!"); + } + $to_replace = array(); + foreach($fields as $name) + { + if (strpos($identity[$name], '{{') !== false || strpos($identity[$name], '$$') !== false) + { + $to_replace[$name] = $identity[$name]; + } + } + if ($to_replace) + { + static $merge=null; + if (!isset($merge)) $merge = new addressbook_merge(); + foreach($to_replace as $name => &$value) + { + $err = null; + $value = $merge->merge_string($value, + (array)accounts::id2name($GLOBALS['egw_info']['user']['account_id'], 'person_id'), + $err, $name == 'ident_signature' ? 'text/html' : 'text/plain'); + } + } + //error_log(__METHOD__."(".array2string($identity).") returning ".array2string($to_replace)); + return $to_replace; + } + + /** + * Read an identity + * + * @param int $ident_id + * @param boolean $replace_placeholders=false should placeholders like {{n_fn}} be replaced + * @return array + * @throws egw_exception_not_found + */ + public static function read_identity($ident_id, $replace_placeholders=false) + { + if (!($data = self::$db->select(self::IDENTITIES_TABLE, '*', array( + 'ident_id' => $ident_id, + 'account_id' => self::memberships(), + ), __LINE__, __FILE__, false, '', self::APP)->fetch())) + { + throw new egw_exception_not_found(); + } + if ($replace_placeholders) + { + $data = array_merge($data, self::replace_placeholders($data)); + + // set empty email&realname from session / account + if (empty($data['ident_email']) || empty($data['ident_realname'])) + { + if (($account = self::read($data['acc_id']))) + { + if (empty($data['ident_email'])) $data['ident_email'] = $account->ident_email; + if (empty($data['ident_realname'])) $data['ident_realname'] = $account->ident_realname; + } + } + if (empty($data['ident_name'])) + { + $data['ident_name'] = self::identity_name($data); + } + } + return $data; + } + + /** + * Store an identity in database + * + * Can be called static, if identity is given as parameter + * + * @param array|emailadmin_account $identity=null default standard identity of current account + * @return int ident_id of new/updated identity + */ + public /*static*/ function save_identity($identity=null) + { + if (!$identity && isset($this)) $identity = $this; + if (!is_array($identity) && !is_a($identity, 'emailadmin_account')) + { + throw new egw_exception_wrong_parameter(__METHOD__."() requires an identity or account as first parameter!"); + } + if (!($identity['acc_id'] > 0)) + { + throw new egw_exception_wrong_parameter(__METHOD__."() no account / acc_id specified in identity!"); + } + $data = array( + 'acc_id' => $identity['acc_id'], + 'ident_name' => $identity['ident_name'] ? $identity['ident_name'] : null, + 'ident_realname' => $identity['ident_realname'], + 'ident_org' => $identity['ident_org'], + 'ident_email' => $identity['ident_email'], + 'ident_signature' => $identity['ident_signature'], + 'account_id' => self::is_multiple($identity) ? 0 : + (is_array($identity['account_id']) ? $identity['account_id'][0] : $identity['account_id']), + ); + if ($identity['ident_id'] > 0) + { + self::$db->update(self::IDENTITIES_TABLE, $data, array( + 'ident_id' => $identity['ident_id'], + ), __LINE__, __FILE__, self::APP); + + return $identity['ident_id']; + } + self::$db->insert(self::IDENTITIES_TABLE, $data, false, __LINE__, __FILE__, self::APP); + + return self::$db->get_last_insert_id(self::IDENTITIES_TABLE, 'ident_id'); + } + + /** + * Delete given identity + * + * @param int $ident_id + * @return int number off affected rows + * @throws egw_exception_wrong_parameter if identity is standard identity of existing account + */ + public static function delete_identity($ident_id) + { + if (($acc_id = self::$db->select(self::TABLE, 'acc_id', array('ident_id' => $ident_id), + __LINE__, __FILE__, 0, '', self::APP, 1)->fetchColumn())) + { + throw new egw_exception_wrong_parameter("Can not delete identity #$ident_id used as standard identity in account #$acc_id!"); + } + self::$db->delete(self::IDENTITIES_TABLE, array('ident_id' => $ident_id), __LINE__, __FILE__, self::APP); + + return self::$db->affected_rows(); + } + + /** + * Give read access to protected parameters in $this->params + * + * To get $this->params you need to call getUserData before! It is never automatically loaded. + * + * @param type $name + * @return mixed + */ + public function __get($name) + { + switch($name) + { + case 'acc_imap_administration': // no longer stored in database + return !empty($this->params['acc_imap_admin_username']); + + case 'params': // does NOT return user-data, unless $this->getUserData was called before! + return $this->params; + } + // if user-data is requested, check if it is already loaded and load it if not + if (in_array($name, self::$user_data) && !array_key_exists($name, $this->params)) + { + $this->getUserData(); + } + return $this->params[$name]; + } + + /** + * Give read access to protected parameters in $this->params + * + * @param type $name + * @return mixed + */ + public function __isset($name) + { + switch($name) + { + case 'acc_imap_administration': // no longer stored in database + return true; + + case 'params': + return isset($this->params); + } + // if user-data is requested, check if it is already loaded and load it if not + if (in_array($name, self::$user_data) && !array_key_exists($name, $this->params)) + { + $this->getUserData(); + } + return isset($this->params[$name]); + } + + /** + * ArrayAccess to emailadmin_account + * + * @param string $offset + * @return mixed + */ + public function offsetGet($offset) + { + return $this->__get($offset); + } + + /** + * ArrayAccess to emailadmin_account + * + * @param string $offset + * @return boolean + */ + public function offsetExists($offset) + { + return $this->__isset($offset); + } + + /** + * ArrayAccess requires it but we dont want to give public write access + * + * Protected access has to use protected attributes! + * + * @param string $offset + * @param mixed $value + * @throws egw_exception_wrong_parameter + */ + public function offsetSet($offset, $value) + { + throw new egw_exception_wrong_parameter(__METHOD__."($offset, $value) No write access through ArrayAccess interface of emailadmin_account!"); + } + + /** + * ArrayAccess requires it but we dont want to give public write access + * + * Protected access has to use protected attributes! + * + * @param string $offset + * @throws egw_exception_wrong_parameter + */ + public function offsetUnset($offset) + { + throw new egw_exception_wrong_parameter(__METHOD__."($offset) No write access through ArrayAccess interface of emailadmin_account!"); + } + + /** + * Check which rights current user has on mail-account + * + * @param int $rights EGW_ACL_(READ|EDIT|DELETE) + * @param array|emailadmin_account $account=null default use this + * @return boolean + */ + public /*static*/ function check_access($rights, $account=null) + { + if (!isset($account)) $account = $this; + + if (!is_array($account) && !is_a($account, 'emailadmin_account')) + { + throw new egw_exception_wrong_parameter('$account must be either an array or an emailadmin_account object!'); + } + + $access = false; + // emailadmin has all rights + if (isset($GLOBALS['egw_info']['user']['apps']['emailadmin'])) + { + $access = true; + $reason = 'user is EMailAdmin'; + } + else + { + // check if account is for current user, if not deny access + $memberships = self::memberships(); + $memberships[] = ''; // edit uses '' for everyone + + if (array_intersect((array)$account['account_id'], $memberships)) + { + switch($rights) + { + case EGW_ACL_READ: + $access = true; + break; + + case EGW_ACL_EDIT: + case EGW_ACL_DELETE: + // users have only edit/delete rights on accounts marked as user-editable AND belonging to them personally + if (!$account['acc_user_editable']) + { + $access = false; + $reason = 'account not user editable'; + } + elseif (!in_array($GLOBALS['egw_info']['user']['account_id'], (array)$account['account_id'])) + { + $access = false; + $reason = 'no edit/delete for public (not personal) account'; + } + else + { + $access = true; + $reason = 'user editable personal account'; + } + break; + } + } + else + { + $reason = 'account not valid for current user'.array2string($account['account_id']); + } + } + //error_log(__METHOD__."($rights, $account[acc_id]: $account[acc_name]) returning ".array2string($access).' '.$reason); + return $access; + } + + /** + * Read/return account object for given $acc_id + * + * @param int $acc_id + * @param int $called_for=null if set admin access to given user, default current user + * AND read username/password from current users session, 0: find accounts from all users + * @return email_account + * @throws egw_exception_not_found if account was not found (or not valid for current user) + */ + public static function read($acc_id, $called_for=null) + { + //error_log(__METHOD__."($acc_id, ".array2string($called_for).")"); + // some caching, but only for regular usage/users + if (!isset($called_for)) + { + // act as singleton: if we already have an instance, return it + if (isset(self::$instances[$acc_id])) + { + //error_log(__METHOD__."($acc_id) returned existing instance"); + return self::$instances[$acc_id]; + } + // not yet an instance, create one + if (isset(self::$cache[$acc_id]) && is_array(self::$cache[$acc_id])) + { + //error_log(__METHOD__."($acc_id) created instance from cached data"); + return self::$instances[$acc_id] = new emailadmin_account(self::$cache[$acc_id]); + } + $data =& self::$cache[$acc_id]; + } + $where = array(self::TABLE.'.acc_id='.(int)$acc_id); + if (!isset($called_for) || $called_for !== '0') + { + $where[] = self::$db->expression(self::VALID_TABLE, self::VALID_TABLE.'.', array('account_id' => self::memberships($called_for))); + } + $cols = array(self::TABLE.'.*', self::IDENTITIES_TABLE.'.*'); + $group_by = 'GROUP BY '.self::TABLE.'.acc_id,'.self::IDENTITIES_TABLE.'.ident_id'; + $join = self::IDENTITY_JOIN.' '.self::VALID_JOIN; + if (($valid_account_id_sql = self::$db->group_concat('all_valid.account_id'))) + { + $cols[] = $valid_account_id_sql.' AS account_id'; + $join .= ' '.self::ALL_VALID_JOIN; + } + if (!($data = self::$db->select(self::TABLE, $cols, $where, __LINE__, __FILE__, + false, so_sql::fix_group_by_columns($group_by, $cols, self::TABLE, 'acc_id'), + self::APP, 0, $join)->fetch())) + { + throw new egw_exception_not_found(lang('Account not found!').' (acc_id='.array2string($acc_id).')'); + } + if (!$valid_account_id_sql) + { + $data['account_id'] = array(); + foreach(self::$db->select(self::VALID_TABLE, 'account_id', array('acc_id' => $acc_id), + __LINE__, __FILE__, false, '', self::APP) as $row) + { + $data['account_id'][] = $row['account_id']; + } + } + $data = self::db2data($data); + //error_log(__METHOD__."($acc_id, $only_current_user) returning ".array2string($data)); + + if (!isset($called_for)) + { + //error_log(__METHOD__."($acc_id) creating instance and caching data read from db"); + $ret =& self::$instances[$acc_id]; + } + return $ret = new emailadmin_account($data, $called_for); + } + + /** + * Transform data returned from database (currently only fixing bool values) + * + * @param array $data + * @return array + */ + protected static function db2data(array $data) + { + foreach(array('acc_sieve_enabled','acc_further_identities','acc_user_editable','acc_smtp_auth_session') as $name) + { + if (isset($data[$name])) + { + $data[$name] = self::$db->from_bool($data[$name]); + } + } + if (isset($data['account_id']) && !is_array($data['account_id'])) + { + $data['account_id'] = explode(',', $data['account_id']); + } + return $data; + } + + /** + * Save account data to db + * + * @param array $data + * @param int $user=null account-id to store account-infos of managed mail-server + * @return array $data plus added values for keys acc_id, ident_id from insert + * @throws egw_exception_wrong_parameter if called static without data-array + * @throws egw_exception_db + */ + public static function write(array $data, $user=null) + { + //error_log(__METHOD__."(".array2string($data).")"); + $data['acc_modifier'] = $GLOBALS['egw_info']['user']['account_id']; + $data['acc_modified'] = time(); + + // store account data + if (!($data['acc_id'] > 0)) unset($data['acc_id']); + $where = $data['acc_id'] > 0 ? array('acc_id' => $data['acc_id']) : false; + self::$db->insert(self::TABLE, $data, $where, __LINE__, __FILE__, self::APP); + if (!($data['acc_id'] > 0)) + { + $data['acc_id'] = self::$db->get_last_insert_id(self::TABLE, 'acc_id'); + } + // store identity + $new_ident_id = self::save_identity($data); + if (!($data['ident_id'] > 0)) + { + $data['ident_id'] = $new_ident_id; + self::$db->update(self::TABLE, array( + 'ident_id' => $data['ident_id'], + ), array( + 'acc_id' => $data['acc_id'], + ), __LINE__, __FILE__, self::APP); + } + // make account valid for given owner + if (!isset($data['account_id'])) + { + $data['account_id'] = $GLOBALS['egw_info']['user']['account_id']; + } + $old_account_ids = array(); + if ($where) + { + foreach(self::$db->select(self::VALID_TABLE, 'account_id', $where, + __LINE__, __FILE__, false, '', self::APP) as $row) + { + $old_account_ids[] = $row['account_id']; + } + if (($ids_to_remove = array_diff($old_account_ids, (array)$data['account_id']))) + { + self::$db->delete(self::VALID_TABLE, $where+array( + 'account_id' => $ids_to_remove, + ), __LINE__, __FILE__, self::APP); + } + } + foreach((array)$data['account_id'] as $account_id) + { + if (!in_array($account_id, $old_account_ids)) + { + self::$db->insert(self::VALID_TABLE, array( + 'acc_id' => $data['acc_id'], + 'account_id' => $account_id, + ), false, __LINE__, __FILE__, self::APP); + } + } + // check for whom we have to store credentials + $valid_for = self::credentials_valid_for($data); + // add imap credentials + $cred_type = $data['acc_imap_username'] == $data['acc_smtp_username'] && + $data['acc_imap_password'] == $data['acc_smtp_password'] ? 3 : 1; + emailadmin_credentials::write($data['acc_id'], $data['acc_imap_username'], $data['acc_imap_password'], + $cred_type, $valid_for, $data['acc_imap_cred_id']); + // add smtp credentials if necessary and different from imap + if ($data['acc_smtp_username'] && $cred_type != 3) + { + emailadmin_credentials::write($data['acc_id'], $data['acc_smtp_username'], $data['acc_smtp_password'], + 2, $valid_for, $data['acc_smtp_cred_id'] != $data['acc_imap_cred_id'] ? + $data['acc_smtp_cred_id'] : null); + } + // store or delete admin credentials + if ($data['acc_imap_admin_username'] && $data['acc_imap_admin_password']) + { + emailadmin_credentials::write($data['acc_id'], $data['acc_imap_admin_username'], + $data['acc_imap_admin_password'], emailadmin_credentials::ADMIN, 0, + $data['acc_imap_admin_cred_id']); + } + else + { + emailadmin_credentials::delete($data['acc_id'], 0, emailadmin_credentials::ADMIN); + } + + // store notification folders + emailadmin_notifications::write($data['acc_id'], (int)$data['notify_account_id'], (array)$data['notify_folders']); + + // store account-information of managed mail server + if ($user > 0 && $data['acc_smtp_type'] && $data['acc_smtp_type'] != 'emailadmin_smtp') + { + $smtp = self::_smtp($data); + $smtp->setUserData($user, $data['mailAlternateAddress'], $data['mailForwardingAddress'], + $data['deliveryMode'], $data['accountStatus'], $data['mailLocalAddress'], $data['quotaLimit']); + } + if ($user > 0 && $data['acc_imap_type'] && $data['acc_imap_type'] != 'emailadmin_imap') + { + $class = self::getIcClass($data['acc_imap_type']); + $imap = new $class($data, true); + $imap->setUserData($GLOBALS['egw']->accounts->id2name($user), $data['quotaLimit']); + } + + // store domain of an account for all user like before as "mail_suffix" config + if ($data['acc_domain'] && (!$data['account_id'] || $data['account_id'] == array(0))) + { + config::save_value('mail_suffix', $data['acc_domain'], 'phpgwapi', true); + } + + self::cache_invalidate($data['acc_id']); + //error_log(__METHOD__."() returning ".array2string($data)); + return $data; + } + + /** + * Check for whom given credentials are to be stored + * + * @param array|emailadmin_account $account + * @param int $account_id=null + * @return boolean + */ + protected static function credentials_valid_for($account, $account_id=null) + { + if (!isset($account_id)) $account_id = $GLOBALS['egw_info']['user']['account_id']; + + // if account valid for multiple users + if (self::is_multiple($account)) + { + // if imap login-name get constructed --> store credentials only for current user + if ($account['acc_imap_logintype']) + { + return $account_id; + } + // store credentials for all users + return 0; + } + // account is valid for a single user + return is_array($account['account_id']) ? $account['account_id'][0] : $account['account_id']; + } + + /** + * Delete accounts or account related data belonging to given mail or user account + * + * @param int|array $acc_id mail account + * @param int $account_id=null user or group + * @return int number of deleted mail accounts or null if only user-data was deleted and no full mail accounts + */ + public static function delete($acc_id, $account_id=null) + { + if (is_array($acc_id) || $acc_id > 0) + { + self::$db->delete(self::VALID_TABLE, array('acc_id' => $acc_id), __LINE__, __FILE__, self::APP); + self::$db->delete(self::IDENTITIES_TABLE, array('acc_id' => $acc_id), __LINE__, __FILE__, self::APP); + emailadmin_credentials::delete($acc_id); + emailadmin_notifications::delete($acc_id); + self::$db->delete(self::TABLE, array('acc_id' => $acc_id), __LINE__, __FILE__, self::APP); + + // invalidate caches + foreach((array)$acc_id as $acc_id) + { + self::cache_invalidate($acc_id); + } + return self::$db->affected_rows(); + } + if (!$account_id) + { + throw new egw_exception_wrong_parameter(__METHOD__."() no acc_id AND no account_id parameter!"); + } + // delete all credentials belonging to given account(s) + emailadmin_credentials::delete(0, $account_id); + emailadmin_notifications::delete(0, $account_id); + // delete all pointers to mail accounts belonging to given user accounts + self::$db->delete(self::VALID_TABLE, array('account_id' => $account_id), __LINE__, __FILE__, self::APP); + // delete all identities belonging to given user accounts + self::$db->delete(self::IDENTITIES_TABLE, array('account_id' => $account_id), __LINE__, __FILE__, self::APP); + // find profiles not belonging to anyone else and delete them + $acc_ids = array(); + foreach(self::$db->select(self::TABLE, self::TABLE.'.acc_id', 'account_id IS NULL', __LINE__, __FILE__, + false, 'GROUP BY '.self::TABLE.'.acc_id', self::APP, 0, 'LEFT '.self::VALID_JOIN) as $row) + { + $acc_ids[] = $row['acc_id']; + } + if ($acc_ids) + { + return self::delete($acc_ids); + } + return null; + } + + /** + * Return array with acc_id => acc_name or account-object pairs + * + * @param boolean|int $only_current_user=true return only accounts for current user or integer account_id of a user + * @param boolean|string $just_name=true true: return self::identity_name, false: return emailadmin_account objects, + * string with attribute-name: return that attribute, eg. acc_imap_host or 'params' to return all attributes as array + * @param string $order_by='acc_name ASC' + * @param int|boolean $offset=false offset or false to return all + * @param int $num_rows=0 number of rows to return, 0=default from prefs (if $offset !== false) + * @param boolean $replace_placeholders=true should placeholders like {{n_fn}} be replaced + * @return Iterator with acc_id => acc_name or emailadmin_account objects + */ + public static function search($only_current_user=true, $just_name=true, $order_by=null, $offset=false, $num_rows=0, $replace_placeholders=true) + { + //error_log(__METHOD__."($only_current_user, $just_name, '$order_by', $offset, $num_rows)"); + $where = array(); + if ($only_current_user) + { + $account_id = $only_current_user === true ? $GLOBALS['egw_info']['user']['account_id'] : $only_current_user; + if (!is_numeric($account_id)) + { + throw new egw_exception_wrong_parameter(__METHOD__."(".array2string($only_current_user).") is NO valid account_id"); + } + $where[] = self::$db->expression(self::VALID_TABLE, self::VALID_TABLE.'.', array('account_id' => self::memberships($account_id))); + } + if (empty($order_by) || !preg_match('/^[a-z_]+ (ASC|DESC)$/i', $order_by)) + { + // for current user prefer account with ident_email matching user email or domain + // (this also helps notifications to account allowing to send with from address of current user / account_email) + if ($only_current_user && $GLOBALS['egw_info']['user']['account_email']) + { + list(,$domain) = $account_email = $GLOBALS['egw_info']['user']['account_email']; + // empty ident_email will be replaced with account_email! + $order_by = "(ident_email='' OR ident_email=".self::$db->quote($account_email). + ') DESC,ident_email LIKE '.self::$db->quote('%@'.$domain).' DESC,'; + } + $order_by .= self::DEFAULT_ORDER; + } + $cache_key = json_encode($where).$order_by; + + if (!$only_current_user || !isset(self::$search_cache[$cache_key])) + { + $cols = array(self::TABLE.'.*', self::IDENTITIES_TABLE.'.*'); + $group_by = 'GROUP BY '.self::TABLE.'.acc_id,'.self::IDENTITIES_TABLE.'.ident_id,'.self::VALID_TABLE.'.account_id'; + $join = self::IDENTITY_JOIN.' '.self::VALID_JOIN; + if (($valid_account_id_sql = self::$db->group_concat('all_valid.account_id'))) + { + $cols[] = $valid_account_id_sql.' AS account_id'; + $join .= ' '.self::ALL_VALID_JOIN; + } + if ($just_name == 'params') // join in acc_imap_admin_username + { + $cols[] = self::ADMIN_COL; + $join .= ' '.self::ADMIN_JOIN; + } + $rs = self::$db->select(self::TABLE, $cols, $where, __LINE__, __FILE__, + $offset, so_sql::fix_group_by_columns($group_by, $cols, self::TABLE, 'acc_id').' ORDER BY '.$order_by, + self::APP, $num_rows, $join); + + $ids = array(); + foreach($rs as $row) + { + $row = self::db2data($row); + + if ($only_current_user) + { + //error_log(__METHOD__."(TRUE, $just_name) caching data for acc_id=$row[acc_id]"); + self::$search_cache[$cache_key][$row['acc_id']] =& self::$cache[$row['acc_id']]; + self::$cache[$row['acc_id']] = $row; + } + else + { + self::$search_cache[$cache_key][$row['acc_id']] = $row; + } + $ids[] = $row['acc_id']; + } + // fetch valid_id, if not yet fetched + if (!$valid_account_id_sql && $ids) + { + foreach(self::$db->select(self::VALID_TABLE, 'account_id', array('acc_id' => $ids), + __LINE__, __FILE__, false, '', self::APP) as $row) + { + self::$cache[$row['acc_id']]['account_id'][] = $row['account_id']; + } + } + } + if (is_null(self::$search_cache[$cache_key])) self::$search_cache[$cache_key]=array(); + return new egw_db_callback_iterator(new ArrayIterator(self::$search_cache[$cache_key]), + // process each row + function($row) use ($just_name, $replace_placeholders) + { + if ($replace_placeholders) + { + $row = array_merge($row, emailadmin_account::replace_placeholders($row)); + } + if (is_string($just_name)) + { + return $just_name == 'params' ? $row : $row[$just_name]; + } + elseif ($just_name) + { + return emailadmin_account::identity_name($row, false); + } + return new emailadmin_account($row); + }, array(), + // return acc_id as key + function($row) + { + return $row['acc_id']; + }); + } + + /** + * Get ID of default mail account for either IMAP or SMTP + * + * @param boolean $smtp=false false: usable for IMAP, true: usable for SMTP + * @return int + */ + static function get_default_acc_id($smtp=false) + { + try + { + foreach(emailadmin_account::search(true, 'params') as $acc_id => $params) + { + if ($smtp) + { + if (!$params['acc_smtp_host'] || !$params['acc_smtp_port']) continue; + // check requirement of session, which is not available in async service! + if (isset($GLOBALS['egw_info']['flags']['async-service']) && $params['acc_smtp_auth_session']) continue; + } + else + { + if (!$params['acc_imap_host'] || !$params['acc_imap_port']) continue; + $account = new emailadmin_account($params); + //error_log(__METHOD__.__LINE__.' '.$acc_id.':'.array2string($account)); + // continue if we have either no imap username or password + if (!$account->is_imap()) continue; + } + return $acc_id; + } + } + catch (Exception $e) + { + error_log(__METHOD__.__LINE__.' Error no Default available.'.$e->getMessage()); + } + return null; + } + + /** + * build an identity name + * + * @param array|emailadmin_account $account object or values for keys 'ident_(realname|org|email)', 'acc_(id|name|imap_username)' + * @param boolean $replace_placeholders=true should placeholders like {{n_fn}} be replaced + * @return string with htmlencoded angle brackets + */ + public static function identity_name($account, $replace_placeholders=true) + { + if ($replace_placeholders) + { + $data = array( + 'ident_name' => $account['ident_name'], + 'ident_realname' => $account['ident_realname'], + 'ident_org' => $account['ident_org'], + 'ident_email' => $account['ident_email'], + 'acc_name' => $account['acc_name'], + 'acc_imap_username' => $account['acc_imap_username'], + 'acc_imap_logintype' => $account['acc_imap_logintype'], + 'acc_domain' => $account['acc_domain'], + 'acc_id' => $account['acc_id'], + ); + unset($account); + //$start = microtime(true); + $account = array_merge($data, self::replace_placeholders($data)); + //error_log(__METHOD__."() account=".array2string($account).' took '.number_format(microtime(true)-$start,3)); + } + // user specified an own name --> use just it + if (!empty($account['ident_name'])) + { + $name = $account['ident_name']; + } + else + { + if (empty($account['ident_email'])) + { + try { + if (is_array($account) && empty($account['acc_imap_username']) && $account['acc_id']) + { + if (!isset($account['acc_imap_username'])) + { + $account += emailadmin_credentials::read($account['acc_id']); + } + if (empty($account['acc_imap_username']) && $account['acc_imap_logintype']) + { + $account = array_merge($account, emailadmin_credentials::from_session($account)); + } + } + if (empty($account['ident_email']) && !empty($account['acc_imap_username'])) + { + $account['ident_email'] = $account['acc_imap_username']; + } + } + catch(Exception $e) { + _egw_log_exception($e); + } + + } + if (strlen(trim($account['ident_realname'].$account['ident_org']))) + { + $name = $account['ident_realname'].' '.$account['ident_org']; + } + else + { + $name = $account['acc_name']; + } + if ($account['ident_email']) + { + $name .= ' <'.$account['ident_email'].'>'; + } + if (stripos($name, $account['acc_name']) === false) + { + $name .= ' '.$account['acc_name']; + } + } + //error_log(__METHOD__."(".array2string($account).", $replace_placeholders) returning ".array2string($name)); + return $name; + } + + /** + * Check if account is for multiple users + * + * account_id == 0 == everyone, is multiple too! + * + * @param array|emailadmin_account $account value for key account_id (can be an array too!) + * @return boolean + */ + public static function is_multiple($account) + { + $is_multiple = !is_array($account['account_id']) ? $account['account_id'] <= 0 : + (count($account['account_id']) > 1 || $account['account_id'][0] <= 0); + //error_log(__METHOD__."(account_id=".array2string($account['account_id']).") returning ".array2string($is_multiple)); + return $is_multiple; + } + + /** + * Magic method to convert account to a string: identity_name + * + * @return string + */ + public function __toString() + { + return self::identity_name($this); + } + + /** + * Get memberships of current or given user incl. our 0=Everyone + * + * @param type $user + * @return array + */ + protected static function memberships($user=null) + { + if (!$user) $user = $GLOBALS['egw_info']['user']['account_id']; + + $memberships = $GLOBALS['egw']->accounts->memberships($user, true); + $memberships[] = $user; + $memberships[] = 0; // marks accounts valid for everyone + + return $memberships; + } + + /** + * Invalidate various caches + * + * @param int $acc_id + */ + protected static function cache_invalidate($acc_id) + { + //error_log(__METHOD__."($acc_id) invalidating cache"); + unset(self::$cache[$acc_id]); + unset(self::$instances[$acc_id]); + self::$search_cache = array(); + } + + /** + * Init our static properties + */ + static public function init_static() + { + self::$db = $GLOBALS['egw']->db; + } +} + +// some testcode, if this file is called via it's URL (you need to uncomment!) +/*if (isset($_SERVER['SCRIPT_FILENAME']) && $_SERVER['SCRIPT_FILENAME'] == __FILE__) +{ + $GLOBALS['egw_info'] = array( + 'flags' => array( + 'currentapp' => 'home', + 'nonavbar' => true, + ), + ); + include_once '../../header.inc.php'; + + emailadmin_account::init_static(); + + foreach(emailadmin_account::search(true, false) as $acc_id => $account) + { + echo "

$acc_id: $account

\n"; + } + if (isset($_GET['acc_id']) && (int)$_GET['acc_id'] > 0) + { + $account = emailadmin_account::read((int)$_GET['acc_id']); + _debug_array($account); + } +}*/ + +// need to be after test-code! +emailadmin_account::init_static(); diff --git a/emailadmin/inc/class.emailadmin_base.inc.php b/emailadmin/inc/class.emailadmin_base.inc.php new file mode 100644 index 0000000000..3d10ff34fe --- /dev/null +++ b/emailadmin/inc/class.emailadmin_base.inc.php @@ -0,0 +1,77 @@ + + * @copyright (c) 2014 by Ralf Becker + * @author Stylite AG + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @version $Id$ + */ + +class emailadmin_base +{ + /** + * Get a list of supported SMTP servers + * + * Calls hook "smtp_server_types" to allow applications to supply own server-types + * + * @return array classname => label pairs + */ + static public function getSMTPServerTypes($extended=true) + { + $retData = array(); + foreach($GLOBALS['egw']->hooks->process(array( + 'location' => 'smtp_server_types', + 'extended' => $extended, + ), array('managementserver', 'emailadmin'), true) as $data) + { + if ($data) $retData += $data; + } + uksort($retData, function($a, $b) { + static $prio = array( // not explicitly mentioned get 0 + 'emailadmin_smtp' => 9, + 'emailadmin_smtp_sql' => 8, + 'smtpplesk' => -1, + ); + return (int)$prio[$b] - (int)$prio[$a]; + }); + return $retData; + } + + /** + * Get a list of supported IMAP servers + * + * Calls hook "imap_server_types" to allow applications to supply own server-types + * + * @param boolean $extended=true + * @return array classname => label pairs + */ + static public function getIMAPServerTypes($extended=true) + { + $retData = array(); + foreach($GLOBALS['egw']->hooks->process(array( + 'location' => 'imap_server_types', + 'extended' => $extended, + ), array('managementserver', 'emailadmin'), true) as $data) + { + if ($data) $retData += $data; + } + uksort($retData, function($a, $b) { + static $prio = array( // not explicitly mentioned get 0 + 'emailadmin_imap' => 9, + 'emailadmin_oldimap' => 9, + 'managementserver_imap' => 8, + 'emailadmin_dovecot' => 7, + 'emailadmin_imap_dovecot' => 7, + 'cyrusimap' => 6, + 'emailadmin_imap_cyrus' => 6, + 'pleskimap' => -1, + ); + return (int)$prio[$b] - (int)$prio[$a]; + }); + return $retData; + } +} \ No newline at end of file diff --git a/emailadmin/inc/class.emailadmin_bo.inc.php b/emailadmin/inc/class.emailadmin_bo.inc.php new file mode 100644 index 0000000000..08b133477f --- /dev/null +++ b/emailadmin/inc/class.emailadmin_bo.inc.php @@ -0,0 +1,242 @@ + + * @author Klaus Leithoff + * @author Lars Kneschke + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @version $Id$ + */ + +/** + * Business logic + */ +class emailadmin_bo +{ + /** + * Name of app the table is registered + */ + const APP = 'emailadmin'; + + static $sessionData = array(); + #var $userSessionData; + var $LDAPData; + + //var $SMTPServerType = array(); // holds a list of config options + static $SMTPServerType = array( + 'emailadmin_smtp' => array( + 'description' => 'standard SMTP-Server', + 'classname' => 'emailadmin_smtp' + ), + ); + //var $IMAPServerType = array(); // holds a list of config options + static $IMAPServerType = array( + 'defaultimap' => array( + 'description' => 'standard IMAP server', + 'protocol' => 'imap', + 'classname' => 'defaultimap' + ) + ); + + var $imapClass; // holds the imap/pop3 class + var $smtpClass; // holds the smtp class + var $tracking; // holds the tracking object + + /** + * @var emailadmin_so + */ + var $soemailadmin; + + function __construct($_profileID=false,$_restoreSesssion=true) + { + parent::__construct(self::APP,self::TABLE,null,'',true); + //error_log(__METHOD__.function_backtrace()); + if (!is_object($GLOBALS['emailadmin_bo'])) + { + $GLOBALS['emailadmin_bo'] = $this; + } + //init with all servertypes and translate the standard entry description + self::$SMTPServerType = self::getSMTPServerTypes(); + self::$IMAPServerType = self::getIMAPServerTypes(); + self::$SMTPServerType['emailadmin_smtp']['description'] = lang('standard SMTP-Server'); + self::$IMAPServerType['defaultimap']['description'] = lang('standard IMAP Server'); + if ($_restoreSesssion) // && !(is_array(self::$sessionData) && (count(self::$sessionData)>0)) ) + { + $this->restoreSessionData(); + } + if ($_restoreSesssion===false) // && (is_array(self::$sessionData) && (count(self::$sessionData)>0)) ) + { + // make sure session data will be created new + self::$sessionData = array(); + self::saveSessionData(); + } + #_debug_array(self::$sessionData); + } + + function getAccountEmailAddress($_accountName, $_profileID) + { + $profileData = $this->getProfile($_profileID); + + #$smtpClass = self::$SMTPServerType[$profileData['smtpType']]['classname']; + if ($profileData['smtpType']=='defaultsmtp') $profileData['smtpType']='emailadmin_smtp'; + $smtpClass = CreateObject('emailadmin.'.self::$SMTPServerType[$profileData['smtpType']]['classname']); + + #return empty($smtpClass) ? False : ExecMethod("emailadmin.$smtpClass.getAccountEmailAddress",$_accountName,3,$profileData); + return is_object($smtpClass) ? $smtpClass->getAccountEmailAddress($_accountName) : False; + } + + function getMailboxString($_folderName) + { + if (is_object($this->imapClass)) + { + return ExecMethod("emailadmin.".$this->imapClass.".getMailboxString",$_folderName,3,$this->profileData); + return $this->imapClass->getMailboxString($_folderName); + } + else + { + return false; + } + } + + /** + * Get a list of supported SMTP servers + * + * Calls hook "smtp_server_types" to allow applications to supply own server-types + * + * @return array classname => label pairs + * @deprecated use emailadmin_base::getSMTPServerTypes() + */ + static public function getSMTPServerTypes($extended=true) + { + return emailadmin_base::getSMTPServerTypes($extended); + } + + /** + * Get a list of supported IMAP servers + * + * Calls hook "imap_server_types" to allow applications to supply own server-types + * + * @param boolean $extended=true + * @return array classname => label pairs + * @deprecated use emailadmin_base::getIMAPServerTypes() + */ + static public function getIMAPServerTypes($extended=true) + { + return emailadmin_base::getIMAPServerTypes($extended); + } + + /** + * Query user data from incomming (IMAP) and outgoing (SMTP) mail-server + * + * @param int $_accountID + * @return array + */ + function getUserData($_accountID) + { + return false; + } + + function restoreSessionData() + { + $GLOBALS['egw_info']['flags']['autoload'] = array(__CLASS__,'autoload'); + + //echo function_backtrace()."
"; + //unserializing the sessiondata, since they are serialized for objects sake + self::$sessionData = (array) unserialize($GLOBALS['egw']->session->appsession('session_data','emailadmin')); + } + + /** + * Autoload classes from emailadmin, 'til they get autoloading conform names + * + * @param string $class + */ + static function autoload($class) + { + if (strlen($class)<100) + { + if (file_exists($file=EGW_INCLUDE_ROOT.'/emailadmin/inc/class.'.$class.'.inc.php')) + { + include_once($file); + } + elseif (strpos($class,'activesync')===0) + { + //temporary solution/hack to fix the false loading of activesync stuff, even as we may not need it for ui + //but trying to load it blocks the mail app + //error_log(__METHOD__.__LINE__.' '.$class); + include_once(EGW_INCLUDE_ROOT.'/activesync/backend/egw.php'); + } + + } + } + + function saveSMTPForwarding($_accountID, $_forwardingAddress, $_keepLocalCopy) + { + if (is_object($this->smtpClass)) + { + #$smtpClass = CreateObject('emailadmin.'.$this->smtpClass,$this->profileID); + #$smtpClass->saveSMTPForwarding($_accountID, $_forwardingAddress, $_keepLocalCopy); + $this->smtpClass->saveSMTPForwarding($_accountID, $_forwardingAddress, $_keepLocalCopy); + } + + } + + /** + * called by the validation hook in setup + * + * @param array $settings following keys: mail_server, mail_server_type {IMAP|IMAPS|POP-3|POP-3S}, + * mail_login_type {standard|vmailmgr}, mail_suffix (domain), smtp_server, smtp_port, smtp_auth_user, smtp_auth_passwd + */ + function setDefaultProfile($settings) + { + return false; + } + + function saveSessionData() + { + // serializing the session data, for the sake of objects + if (is_object($GLOBALS['egw']->session)) // otherwise setup(-cli) fails + { + $GLOBALS['egw']->session->appsession('session_data','emailadmin',serialize(self::$sessionData)); + } + #$GLOBALS['egw']->session->appsession('user_session_data','',$this->userSessionData); + } + + function updateAccount($_hookValues) { + if (is_object($this->imapClass)) { + #ExecMethod("emailadmin.".$this->imapClass.".updateAccount",$_hookValues,3,$this->profileData); + $this->imapClass->updateAccount($_hookValues); + } + + if (is_object($this->smtpClass)) { + #ExecMethod("emailadmin.".$this->smtpClass.".updateAccount",$_hookValues,3,$this->profileData); + $this->smtpClass->updateAccount($_hookValues); + } + self::$sessionData = array(); + $this->saveSessionData(); + } + + /** + * Get ID of default new account profile + * + * @return int + * @deprecated use emailadmin_account::get_default_acc_id() + */ + static function getDefaultAccID() + { + return emailadmin_account::get_default_acc_id(); + } + + /** + * Get ID of User specific default new account profile + * + * @return int + * @deprecated use emailadmin_account::get_default_acc_id() + */ + static function getUserDefaultAccID() + { + return emailadmin_account::get_default_acc_id(); + } +} diff --git a/emailadmin/inc/class.emailadmin_credentials.inc.php b/emailadmin/inc/class.emailadmin_credentials.inc.php new file mode 100644 index 0000000000..7f4fec90f3 --- /dev/null +++ b/emailadmin/inc/class.emailadmin_credentials.inc.php @@ -0,0 +1,460 @@ + + * @copyright (c) 2013-14 by Ralf Becker + * @author Stylite AG + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @version $Id$ + */ + +/** + * Mail account credentials are stored in egw_ea_credentials for given + * acocunt-id, users and types (imap, smtp and optional admin connection). + * + * Passwords in credentials are encrypted with either user password from session + * or the database password. + */ +class emailadmin_credentials +{ + const APP = 'emailadmin'; + const TABLE = 'egw_ea_credentials'; + const USER_EDITABLE_JOIN = 'JOIN egw_ea_accounts ON egw_ea_accounts.acc_id=egw_ea_credentials.acc_id AND acc_user_editable=1'; + + /** + * Credentials for type IMAP + */ + const IMAP = 1; + /** + * Credentials for type SMTP + */ + const SMTP = 2; + /** + * Credentials for admin connection + */ + const ADMIN = 8; + /** + * All credentials IMAP|SMTP|ADMIN + */ + const ALL = 11; + + /** + * Password in cleartext + */ + const CLEARTEXT = 0; + /** + * Password encrypted with user password + */ + const USER = 1; + /** + * Password encrypted with system secret + */ + const SYSTEM = 2; + + /** + * Translate type to prefix + * + * @var array + */ + protected static $type2prefix = array( + self::IMAP => 'acc_imap_', + self::SMTP => 'acc_smtp_', + self::ADMIN => 'acc_imap_admin_', + ); + + /** + * Reference to global db object + * + * @var egw_db + */ + static protected $db; + + /** + * Mcrypt instance initialised with system specific key + * + * @var ressource + */ + static protected $system_mcrypt; + + /** + * Mcrypt instance initialised with user password from session + * + * @var ressource + */ + static protected $user_mcrypt; + + /** + * Cache for credentials to minimize database access + * + * @var array + */ + protected static $cache = array(); + + /** + * Read credentials for a given mail account + * + * @param int $acc_id + * @param int $type=null default return all credentials + * @param int|array $account_id=null default use current user or all (in that order) + * @return array with values for (imap|smtp|admin)_(username|password|cred_id) + */ + public static function read($acc_id, $type=null, $account_id=null) + { + if (is_null($type)) $type = self::ALL; + if (is_null($account_id)) + { + $account_id = array(0, $GLOBALS['egw_info']['user']['account_id']); + } + + // check cache, if nothing found, query database + // check assumes always same accounts (eg. 0=all plus own account_id) are asked + if (!isset(self::$cache[$acc_id]) || + !($rows = array_intersect_key(self::$cache[$acc_id], array_flip((array)$account_id)))) + { + $rows = self::$db->select(self::TABLE, '*', array( + 'acc_id' => $acc_id, + 'account_id' => $account_id, + '(cred_type & '.(int)$type.') > 0', // postgreSQL require > 0, or gives error as it expects boolean + ), __LINE__, __FILE__, false, + // account_id DESC ensures 0=all allways overwrite (old user-specific credentials) + 'ORDER BY account_id ASC', self::APP); + //error_log(__METHOD__."($acc_id, $type, ".array2string($account_id).") nothing in cache"); + } + else + { + ksort($rows); // ORDER BY account_id ASC + + // flatten account_id => cred_type => row array again, to have format like from database + $rows = call_user_func_array('array_merge', $rows); + //error_log(__METHOD__."($acc_id, $type, ".array2string($account_id).") read from cache ".array2string($rows)); + } + $results = array(); + foreach($rows as $row) + { + // update cache (only if we have database-iterator and all credentials asked!) + if (!is_array($rows) && $type == self::ALL) + { + self::$cache[$acc_id][$row['account_id']][$row['cred_type']] = $row; + //error_log(__METHOD__."($acc_id, $type, ".array2string($account_id).") stored to cache ".array2string($row)); + } + $password = self::decrypt($row); + + foreach(self::$type2prefix as $pattern => $prefix) + { + if ($row['cred_type'] & $pattern) + { + $results[$prefix.'username'] = $row['cred_username']; + $results[$prefix.'password'] = $password; + $results[$prefix.'cred_id'] = $row['cred_id']; + $results[$prefix.'account_id'] = $row['account_id']; + } + } + } + return $results; + } + + /** + * Generate username according to acc_imap_logintype and fetch password from session + * + * @param array $data values for acc_imap_logintype and acc_domain + * @param boolean $set_identity=true true: also set identity values realname&email, if not yet set + * @return array with values for keys 'acc_(imap|smtp)_(username|password|cred_id)' + */ + public static function from_session(array $data, $set_identity=true) + { + switch($data['acc_imap_logintype']) + { + case 'standard': + $username = $GLOBALS['egw_info']['user']['account_lid']; + break; + + case 'vmailmgr': + $username = $GLOBALS['egw_info']['user']['account_lid'].'@'.$data['acc_domain']; + break; + + case 'email': + $username = $GLOBALS['egw_info']['user']['account_email']; + break; + + case 'uidNumber': + $username = 'u'.$GLOBALS['egw_info']['user']['account_id'].'@'.$data['acc_domain']; + break; + + case 'admin': + // data should have been stored in credentials table + throw new egw_exception_assertion_failed('data[acc_imap_logintype]=admin and no stored username/password for data[acc_id]='.$data['acc_id'].'!'); + + default: + throw new egw_exception_wrong_parameter("Unknown data[acc_imap_logintype]=".array2string($data['acc_imap_logintype']).'!'); + } + $password = base64_decode(egw_cache::getSession('phpgwapi', 'password')); + $realname = !$set_identity || $data['ident_realname'] ? $data['ident_realname'] : + $GLOBALS['egw_info']['user']['account_fullname']; + $email = !$set_identity || $data['ident_email'] ? $data['ident_email'] : + $GLOBALS['egw_info']['user']['account_email']; + + return array( + 'ident_realname' => $realname, + 'ident_email' => $email, + 'acc_imap_username' => $username, + 'acc_imap_password' => $password, + 'acc_imap_cred_id' => $data['acc_imap_logintype'], // to NOT store it + 'acc_imap_account_id' => 'c', + ) + ($data['acc_smtp_auth_session'] ? array( + // only set smtp + 'acc_smtp_username' => $username, + 'acc_smtp_password' => $password, + 'acc_smtp_cred_id' => $data['acc_imap_logintype'], // to NOT store it + 'acc_smtp_account_id' => 'c', + ) : array()); + } + + /** + * Write and encrypt credentials + * + * @param int $acc_id id of account + * @param string $username + * @param string $password cleartext password to write + * @param int $type self::IMAP, self::SMTP or self::ADMIN + * @param int $account_id if of user-account for whom credentials are + * @param int $cred_id=null id of existing credentials to update + * @param ressource $mcrypt=null mcrypt ressource for user, default calling self::init_crypt(true) + * @return int cred_id + */ + public static function write($acc_id, $username, $password, $type, $account_id=0, $cred_id=null, $mcrypt=null) + { + //error_log(__METHOD__."(acc_id=$acc_id, '$username', \$password, type=$type, account_id=$account_id, cred_id=$cred_id)"); + if (!empty($cred_id) && !is_numeric($cred_id) || !is_numeric($account_id)) + { + error_log(__METHOD__."($acc_id, '$username', \$password, $type, $account_id, ".array2string($cred_id).") not storing session credentials!"); + return; // do NOT store credentials from session of current user! + } + $pw_enc = self::CLEARTEXT; + $data = array( + 'acc_id' => $acc_id, + 'account_id' => $account_id, + 'cred_username' => $username, + 'cred_password' => self::encrypt($password, $account_id, $pw_enc, $mcrypt), + 'cred_type' => $type, + 'cred_pw_enc' => $pw_enc, + ); + //error_log(__METHOD__."($acc_id, '$username', '$password', $type, $account_id, $cred_id, $mcrypt) storing ".array2string($data).' '.function_backtrace()); + if ($cred_id > 0) + { + self::$db->update(self::TABLE, $data, array('cred_id' => $cred_id), __LINE__, __FILE__, self::APP); + } + else + { + self::$db->insert(self::TABLE, $data, array( + 'acc_id' => $acc_id, + 'account_id' => $account_id, + 'cred_type' => $type, + ), __LINE__, __FILE__, self::APP); + $cred_id = self::$db->get_last_insert_id(self::TABLE, 'cred_id'); + } + // invalidate cache + unset(self::$cache[$acc_id][$account_id]); + + //error_log(__METHOD__."($acc_id, '$username', \$password, $type, $account_id) returning $cred_id"); + return $cred_id; + } + + /** + * Delete credentials from database + * + * @param int $acc_id + * @param int|array $account_id=null + * @param int $type=self::ALL self::IMAP, self::SMTP or self::ADMIN + * @return int number of rows deleted + */ + public static function delete($acc_id, $account_id=null, $type=self::ALL) + { + if (!($acc_id > 0) && !isset($account_id)) + { + throw new egw_exception_wrong_parameter(__METHOD__."() no acc_id AND no account_id parameter!"); + } + $where = array(); + if ($acc_id > 0) $where['acc_id'] = $acc_id; + if (isset($account_id)) $where['account_id'] = $account_id; + if ($type != self::ALL) $where[] = '(cred_type & '.(int)$type.') > 0'; // postgreSQL require > 0, or gives error as it expects boolean + + self::$db->delete(self::TABLE, $where, __LINE__, __FILE__, self::APP); + + // invalidate cache: we allways unset everything about an account to simplify cache handling + foreach($acc_id > 0 ? (array)$acc_id : array_keys(self::$cache) as $acc_id) + { + unset(self::$cache[$acc_id]); + } + $ret = self::$db->affected_rows(); + //error_log(__METHOD__."($acc_id, ".array2string($account_id).", $type) affected $ret rows"); + return $ret; + } + + /** + * Encrypt password for storing in database + * + * @param string $password cleartext password + * @param int $account_id user-account password is for + * @param int &$pw_enc on return encryption used + * @param ressource $mcrypt=null mcrypt ressource for user, default calling self::init_crypt(true) + * @return string encrypted password + */ + protected static function encrypt($password, $account_id, &$pw_enc, $mcrypt=null) + { + if ($account_id > 0 && $account_id == $GLOBALS['egw_info']['user']['account_id'] && + ($mcrypt || ($mcrypt = self::init_crypt(true)))) + { + $pw_enc = self::USER; + $password = mcrypt_generic($mcrypt, $password); + } + elseif (($mcrypt = self::init_crypt(false))) + { + $pw_enc = self::SYSTEM; + $password = mcrypt_generic($mcrypt, $password); + } + else + { + $pw_enc = self::CLEARTEXT; + } + //error_log(__METHOD__."(, $account_id, , $mcrypt) pw_enc=$pw_enc returning ".array2string(base64_encode($password))); + return base64_encode($password); + } + + /** + * Decrypt password from database + * + * @param array $row database row + * @param ressource $mcrypt=null mcrypt ressource for user, default calling self::init_crypt(true) + */ + protected static function decrypt(array $row, $mcrypt=null) + { + switch ($row['cred_pw_enc']) + { + case self::CLEARTEXT: + return base64_decode($row['cred_password']); + + case self::USER: + case self::SYSTEM: + if (($row['cred_pw_enc'] != self::USER || !$mcrypt) && + !($mcrypt = self::init_crypt($row['cred_pw_enc'] == self::USER))) + { + throw new egw_exception_wrong_parameter("Password encryption type $row[cred_pw_enc] NOT available!"); + } + return (!empty($row['cred_password'])?trim(mdecrypt_generic($mcrypt, base64_decode($row['cred_password']))):''); + } + throw new egw_exception_wrong_parameter("Unknow password encryption type $row[cred_pw_enc]!"); + } + + /** + * Hook called when user changes his password, to re-encode his credentials with his new password + * + * It also changes all user credentials encoded with system password! + * + * It only changes credentials from user-editable accounts, as user probably + * does NOT know password set by admin! + * + * @param array $data values for keys 'old_passwd', 'new_passwd', 'account_id' + */ + static public function changepassword(array $data) + { + if (empty($data['old_passwd'])) return; + + $old_mcrypt = null; + foreach(self::$db->select(self::TABLE, self::TABLE.'.*', array( + 'account_id' => $data['account_id'] + ),__LINE__, __FILE__, false, '', 'emailadmin', 0, self::USER_EDITABLE_JOIN) as $row) + { + if (!isset($old_mcrypt)) + { + $old_mcrypt = self::init_crypt($data['old_passwd']); + $new_mcrypt = self::init_crypt($data['new_passwd']); + if (!$old_mcrypt && !$new_mcrypt) return; + } + $password = self::decrypt($row, $old_mcrypt); + + self::write($row['acc_id'], $row['cred_username'], $password, $row['cred_type'], + $row['account_id'], $row['cred_id'], $new_mcrypt); + } + } + + /** + * Check if session encryption is configured, possible and initialise it + * + * @param boolean|string $user=false true: use user-password from session, + * false: database password or string with password to use + * @param string $algo='tripledes' + * @param string $mode='ecb' + * @return ressource|boolean mcrypt ressource to use or false if not available + */ + static public function init_crypt($user=false, $algo='tripledes',$mode='ecb') + { + if (is_string($user)) + { + // do NOT use/set/change static object + } + elseif ($user) + { + $mcrypt =& self::$user_mcrypt; + } + else + { + $mcrypt =& self::$system_mcrypt; + } + if (!isset($mcrypt)) + { + if (is_string($user)) + { + $key = $user; + } + elseif ($user) + { + $session_key = egw_cache::getSession('phpgwapi', 'password'); + if (empty($session_key)) return false; + $key = base64_decode($session_key); + } + else + { + $key = self::$db->Password; + } + if (!check_load_extension('mcrypt')) + { + error_log(__METHOD__."() required PHP extension mcrypt not loaded and can not be loaded, passwords can be NOT encrypted!"); + $mcrypt = false; + } + elseif (!($mcrypt = mcrypt_module_open($algo, '', $mode, ''))) + { + error_log(__METHOD__."() could not mcrypt_module_open(algo='$algo','',mode='$mode',''), passwords can be NOT encrypted!"); + $mcrypt = false; + } + else + { + $iv_size = mcrypt_enc_get_iv_size($mcrypt); + $iv = !isset($GLOBALS['egw_info']['server']['mcrypt_iv']) || strlen($GLOBALS['egw_info']['server']['mcrypt_iv']) < $iv_size ? + mcrypt_create_iv ($iv_size, MCRYPT_RAND) : substr($GLOBALS['egw_info']['server']['mcrypt_iv'],0,$iv_size); + + $key_size = mcrypt_enc_get_key_size($mcrypt); + if (bytes($key) > $key_size) $key = cut_bytes($key,0,$key_size-1); + + if (mcrypt_generic_init($mcrypt, $key, $iv) < 0) + { + error_log(__METHOD__."() could not initialise mcrypt, passwords can be NOT encrypted!"); + $mcrypt = false; + } + } + } + //error_log(__METHOD__."(".array2string($user).") key=".array2string($key)." returning ".array2string($mcrypt)); + return $mcrypt; + } + + /** + * Init our static properties + */ + static public function init_static() + { + self::$db = isset($GLOBALS['egw_setup']) ? $GLOBALS['egw_setup']->db : $GLOBALS['egw']->db; + } +} +emailadmin_credentials::init_static(); diff --git a/emailadmin/inc/class.emailadmin_hooks.inc.php b/emailadmin/inc/class.emailadmin_hooks.inc.php new file mode 100644 index 0000000000..59a62292c0 --- /dev/null +++ b/emailadmin/inc/class.emailadmin_hooks.inc.php @@ -0,0 +1,167 @@ + + * @author Ralf Becker + * @copyright (c) 2008-14 by leithoff-At-stylite.de + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @version $Id$ + */ + +/** + * diverse static emailadmin hooks + */ +class emailadmin_hooks +{ + /** + * Hook called to add action to user + * + * @param array $data + * @param int $data['account_id'] numerical id + */ + static function edit_user($data) + { + unset($data); // not used + + $actions = array(); + + if ($GLOBALS['egw_info']['user']['apps']['emailadmin']) + { + $actions[] = array( + 'id' => 'mail_account', + 'caption' => 'mail account', + 'url' => 'menuaction=emailadmin.emailadmin_wizard.edit&account_id=$id', + 'popup' => '720x530', + 'icon' => 'emailadmin/navbar', + ); + } + return $actions; + } + + /** + * Password changed hook --> unset cached objects, as password might be used for email connection + * + * @param array $hook_data + */ + public static function changepassword($hook_data) + { + if (!empty($hook_data['old_passwd'])) + { + emailadmin_credentials::changepassword($hook_data); + } + } + + /** + * Hook called before an account get deleted + * + * @param array $data + * @param int $data['account_id'] numerical id + * @param string $data['account_lid'] account-name + * @param int $data['new_owner'] account-id of new owner, or false if data should get deleted + */ + static function deleteaccount(array $data) + { + // as mail accounts contain credentials, we do NOT assign them to user users + emailadmin_account::delete(0, $data['account_id']); + } + + /** + * Hook called before a group get deleted + * + * @param array $data + * @param int $data['account_id'] numerical id + * @param string $data['account_name'] account-name + */ + static function deletegroup(array $data) + { + emailadmin_account::delete(0, $data['account_id']); + } + + /** + * Hook called when an account get added or edited + * + * @param array $data + * @param int $data['account_id'] numerical id + * @param string $data['account_lid'] account-name + * @param string $data['account_email'] email + */ + static function addaccount(array $data) + { + $method = $data['location'] == 'addaccount' ? 'addAccount' : 'updateAccount'; + + foreach(emailadmin_account::search((int)$data['account_id'], 'params') as $params) + { + if (!emailadmin_account::is_multiple($params)) continue; // no need to waste time on personal accounts + + try { + $account = new emailadmin_account($params); + if ($account->acc_imap_type != 'emailadmin_imap' && ($imap = $account->imapServer(true)) && + is_a($imap, 'emailadmin_imap') && get_class($imap) != 'emailadmin_imap') + { + $imap->$method($data); + } + if ($account->acc_smtp_type != 'emailadmin_smtp' && ($smtp = $account->smtpServer(true)) && + is_a($smtp, 'emailadmin_smtp') && get_class($smtp) != 'emailadmin_smtp') + { + $smtp->$method($data); + } + } + catch(Exception $e) { + _egw_log_exception($e); + // ignore exception, without stalling other hooks + } + } + } + + /** + * Detect imap and smtp server plugins from EMailAdmin's inc directory + * + * @param string|array $data location string or array with key 'location' and other params + * @return array + */ + public static function server_types($data) + { + $location = is_array($data) ? $data['location'] : $data; + $extended = is_array($data) ? $data['extended'] : false; + + $types = array(); + foreach(scandir($dir=EGW_INCLUDE_ROOT.'/emailadmin/inc') as $file) + { + $matches = null; + if (!preg_match('/^class\.([^.]*(smtp|imap|postfix|dovecot|dbmail)[^.*]*)\.inc\.php$/', $file, $matches)) continue; + $class_name = $matches[1]; + include_once($dir.'/'.$file); + if (!class_exists($class_name)) continue; + + $is_imap = $class_name == 'emailadmin_imap' || is_subclass_of($class_name, 'emailadmin_imap'); + $is_smtp = $class_name == 'emailadmin_smtp' || is_subclass_of($class_name, 'emailadmin_smtp') && $class_name != 'defaultsmtp'; + + if ($is_smtp && $location == 'smtp_server_types' || $is_imap && $location == 'imap_server_types') + { + // only register new imap-class-names + if ($is_imap && $class_name == emailadmin_account::getIcClass ($class_name, true)) continue; + + $type = array( + 'classname' => $class_name, + 'description' => is_callable($function=$class_name.'::description') ? call_user_func($function) : $class_name, + ); + + if ($is_imap) $type['protocol'] = 'imap'; + + $types[$class_name] = $type; + } + } + if (!$extended) + { + foreach($types as $class_name => &$type) + { + $type = $type['description']; + } + } + //error_log(__METHOD__."(".array2string($data).") returning ".array2string($types)); + return $types; + } +} diff --git a/emailadmin/inc/class.emailadmin_horde_cache.inc.php b/emailadmin/inc/class.emailadmin_horde_cache.inc.php new file mode 100644 index 0000000000..6cf5e534be --- /dev/null +++ b/emailadmin/inc/class.emailadmin_horde_cache.inc.php @@ -0,0 +1,91 @@ + + * @copyright (c) 2013 by Ralf Becker + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @version $Id$ + */ + +/** + * Horde_Cache compatible class using egw_cache + */ +class emailadmin_horde_cache +{ + /** + * App to use + */ + const APP = 'mail'; + /** + * How to cache: instance-specific + */ + const LEVEL = egw_cache::INSTANCE; + + /** + * Retrieve cached data. + * + * @param string $key Object ID to query. + * @param integer $lifetime Lifetime of the object in seconds. + * + * @return mixed Cached data, or false if none was found. + */ + public function get($key, $lifetime = 0) + { + $ret = egw_cache::getCache(self::LEVEL, 'mail', $key); + + return !is_null($ret) ? $ret : false; + } + + /** + * Store an object in the cache. + * + * @param string $key Object ID used as the caching key. + * @param mixed $data Data to store in the cache. + * @param integer $lifetime Object lifetime - i.e. the time before the + * data becomes available for garbage + * collection. If 0 will not be GC'd. + */ + public function set($key, $data, $lifetime = 0) + { + egw_cache::setCache(self::LEVEL, 'mail', $key, $data, $lifetime); + } + + /** + * Checks if a given key exists in the cache, valid for the given + * lifetime. + * + * @param string $key Cache key to check. + * @param integer $lifetime Lifetime of the key in seconds. + * + * @return boolean Existence. + */ + public function exists($key, $lifetime = 0) + { + return !is_null(egw_cache::getCache(self::LEVEL, 'mail', $key)); + } + + /** + * Expire any existing data for the given key. + * + * @param string $key Cache key to expire. + * + * @return boolean Success or failure. + */ + public function expire($key) + { + egw_cache::unsetCache(self::LEVEL, 'mail', $key); + } + + /** + * Clears all data from the cache. + * + * @throws Horde_Cache_Exception + */ + public function clear() + { + egw_cache::flush(self::LEVEL, self::APP); + } +} diff --git a/emailadmin/inc/class.emailadmin_imap.inc.php b/emailadmin/inc/class.emailadmin_imap.inc.php new file mode 100644 index 0000000000..fa423a7203 --- /dev/null +++ b/emailadmin/inc/class.emailadmin_imap.inc.php @@ -0,0 +1,1188 @@ + + * @author Stylite AG + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @version $Id$ + */ + +require_once EGW_INCLUDE_ROOT.'/emailadmin/inc/class.defaultimap.inc.php'; + +/** + * This class holds all information about the imap connection. + * This is the base class for all other imap classes. + * + * Also proxies Sieve calls to emailadmin_sieve (eg. it behaves like the former felamimail bosieve), + * to allow IMAP plugins to also manage Sieve connection. + * + * @property-read integer $ImapServerId acc_id of mail account (alias for acc_id) + * @property-read boolean $enableSieve sieve enabled (alias for acc_sieve_enabled) + * @property-read int $acc_id id + * @property-read string $acc_name description / display name + * @property-read string $acc_imap_host imap hostname + * @property-read int $acc_imap_ssl 0=none, 1=starttls, 2=tls, 3=ssl, &8=validate certificate + * @property-read int $acc_imap_port imap port, default 143 or for ssl 993 + * @property-read string $acc_imap_username + * @property-read string $acc_imap_password + * @property-read boolean $acc_sieve_enabled sieve enabled + * @property-read string $acc_sieve_host possible sieve hostname, default imap_host + * @property-read int $acc_sieve_ssl 0=none, 1=starttls, 2=tls, 3=ssl, &8=validate certificate + * @property-read int $acc_sieve_port sieve port, default 4190, old non-ssl port 2000 or ssl 5190 + * @property-read string $acc_folder_sent sent folder + * @property-read string $acc_folder_trash trash folder + * @property-read string $acc_folder_draft draft folder + * @property-read string $acc_folder_template template folder + * @property-read string $acc_folder_junk junk/spam folder + * @property-read string $acc_imap_type imap class to use, default emailadmin_imap + * @property-read string $acc_imap_logintype how to construct login-name standard, vmailmgr, admin, uidNumber + * @property-read string $acc_domain domain name + * @property-read boolean $acc_imap_administration enable administration + * @property-read string $acc_imap_admin_username + * @property-read string $acc_imap_admin_password + * @property-read boolean $acc_further_identities are non-admin users allowed to create further identities + * @property-read boolean $acc_user_editable are non-admin users allowed to edit this account, if it is for them + * @property-read array $params parameters passed to constructor (all above as array) + */ +class emailadmin_imap extends Horde_Imap_Client_Socket implements defaultimap +{ + /** + * Label shown in EMailAdmin + */ + const DESCRIPTION = 'standard IMAP server'; + + /** + * Capabilities of this class (pipe-separated): default, sieve, admin, logintypeemail + */ + const CAPABILITIES = 'default|sieve'; + + /** + * does the server with the serverID support keywords + * this information is filled/provided by examineMailbox + * + * @var array of boolean for each known serverID + */ + static $supports_keywords; + + /** + * is the mbstring extension available + * + * @var boolean + */ + protected $mbAvailable; + + /** + * Login type: 'uid', 'vmailmgr', 'uidNumber', 'email' + * + * @var string + */ + protected $imapLoginType; + + /** + * a debug switch + */ + public $debug = false; + + /** + * Sieve available + * + * @var boolean + */ + protected $enableSieve = false; + + /** + * True if connection is an admin connection + * + * @var boolean + */ + protected $isAdminConnection = false; + + /** + * Domain name + * + * @var string + */ + protected $domainName; + + /** + * Parameters passed to constructor from emailadmin_account + * + * @var array + */ + protected $params = array(); + + /** + * Construtor + * + * @param array + * @param bool|int|string $_adminConnection create admin connection if true or account_id or imap username + * @param int $_timeout=null timeout in secs, if none given fmail pref or default of 20 is used + * @return void + */ + function __construct(array $params, $_adminConnection=false, $_timeout=null) + { + if (function_exists('mb_convert_encoding')) + { + $this->mbAvailable = true; + } + $this->params = $params; + $this->isAdminConnection = $_adminConnection; + $this->enableSieve = (boolean)$this->params['acc_sieve_enabled']; + $this->loginType = $this->params['acc_imap_logintype']; + $this->domainName = $this->params['acc_domain']; + + if (is_null($_timeout)) $_timeout = self::getTimeOut (); + + switch($this->params['acc_imap_ssl'] & ~emailadmin_account::SSL_VERIFY) + { + case emailadmin_account::SSL_STARTTLS: + $secure = 'tls'; // Horde uses 'tls' for STARTTLS, not ssl connection with tls version >= 1 and no sslv2/3 + break; + case emailadmin_account::SSL_SSL: + $secure = 'ssl'; + break; + case emailadmin_account::SSL_TLS: + $secure = 'tlsv1'; // since Horde_Imap_Client-1.16.0 requiring Horde_Socket_Client-1.1.0 + break; + } + // Horde use locale for translation of error messages + common::setlocale(LC_MESSAGES); + + // some plugins need extra measures to switch to an admin connection (eg. Dovecot constructs a special admin user name) + $username = $_adminConnection; + if (!is_bool($username) && is_numeric($username)) + { + $username = $this->getMailBoxUserName($username); + } + if ($_adminConnection) $this->adminConnection($username); + + parent::__construct(array( + 'username' => $this->params[$_adminConnection ? 'acc_imap_admin_username' : 'acc_imap_username'], + 'password' => $this->params[$_adminConnection ? 'acc_imap_admin_password' : 'acc_imap_password'], + 'hostspec' => $this->params['acc_imap_host'], + 'port' => $this->params['acc_imap_port'], + 'secure' => $secure, + 'timeout' => $_timeout, + //'debug_literal' => true, + //'debug' => '/tmp/imap.log', + 'cache' => array( + 'backend' => new Horde_Imap_Client_Cache_Backend_Cache(array( + 'cacheob' => new emailadmin_horde_cache(), + )), + ), + )); + } + + /** + * Ensure we use an admin connection + * + * Plugins can overwrite it to eg. construct a special admin user name + * + * @param string $_username=null create an admin connection for given user or $this->acc_imap_username + */ + function adminConnection($_username=true) + { + if ($this->isAdminConnection !== $_username) + { + $this->logout(); + + $this->__construct($this->params, $_username); + } + } + + /** + * Check admin credientials and connection (if supported) + * + * @param string $username=null create an admin connection for given user or $this->acc_imap_username + * @throws Horde_IMAP_Client_Exception + */ + public function checkAdminConnection($_username=true) + { + if ($this->acc_imap_administration) + { + $this->adminConnection($_username); + $this->login(); + } + } + + /** + * Allow read access to former public attributes + * + * @param type $name + */ + public function __get($name) + { + switch($name) + { + case 'acc_imap_administration': + return !empty($this->params['acc_imap_admin_username']); + + case 'ImapServerId': + return $this->params['acc_id']; + + case 'enableSieve': + return (boolean)$this->params['acc_sieve_enabled']; + + default: + // allow readonly access to all class attributes + if (isset($this->$name)) + { + return $this->$name; + } + if (array_key_exists($name,$this->params)) + { + return $this->params[$name]; + } + if ($this->getParam($name)) + { + return $this->getParam($name); + } + throw new egw_exception_wrong_parameter("Tried to access unknown attribute '$name'!"); + } + } + + /** + * opens a connection to a imap server + * + * @param bool $_adminConnection create admin connection if true + * @param int $_timeout=null timeout in secs, if none given fmail pref or default of 20 is used + * @deprecated allready called by constructor automatic, parameters must be passed to constructor! + * @return boolean|PEAR_Error true on success, PEAR_Error of false on failure + */ + function openConnection($_adminConnection=false, $_timeout=null) + { + unset($_timeout); // not used + if ($_adminConnection !== $this->params['adminConnection']) + { + throw new egw_exception_wrong_parameter('need to set parameters on calling emailadmin_account->imapServer()!'); + } + } + + /** + * getTimeOut + * + * @param string _use decide if the use is for IMAP or SIEVE, by now only the default differs + * @return int - timeout (either set or default 20/10) + */ + static function getTimeOut($_use='IMAP') + { + $timeout = $GLOBALS['egw_info']['user']['preferences']['mail']['connectionTimeout']; + if (empty($timeout) || !($timeout > 0)) $timeout = $_use == 'SIEVE' ? 10 : 20; // this is the default value + return $timeout; + } + + /** + * Return description for EMailAdmin + * + * @return string + */ + public static function description() + { + return static::DESCRIPTION; + } + + /** + * adds a account on the imap server + * + * @param array $_hookValues + * @return bool true on success, false on failure + */ + function addAccount($_hookValues) + { + unset($_hookValues); // not used + return true; + } + + /** + * updates a account on the imap server + * + * @param array $_hookValues + * @return bool true on success, false on failure + */ + function updateAccount($_hookValues) + { + unset($_hookValues); // not used + return true; + } + + /** + * deletes a account on the imap server + * + * @param array $_hookValues + * @return bool true on success, false on failure + */ + function deleteAccount($_hookValues) + { + unset($_hookValues); // not used + return true; + } + + function disconnect() + { + + } + + /** + * converts a foldername from current system charset to UTF7 + * + * @param string $_folderName + * @return string the encoded foldername + */ + function encodeFolderName($_folderName) + { + if($this->mbAvailable) { + return mb_convert_encoding($_folderName, "UTF7-IMAP", translation::charset()); + } + + // if not + // we can encode only from ISO 8859-1 + return imap_utf7_encode($_folderName); + } + + /** + * getMailbox + * + * @param string $mailbox + * @return mixed mailbox object/string (string if not found by listMailboxes but existing) + */ + function getMailbox($mailbox) + { + $mailboxes = $this->listMailboxes($mailbox,Horde_Imap_Client::MBOX_ALL); + if (empty($mailboxes)) $mailboxes = $this->listMailboxes($mailbox,Horde_Imap_Client::MBOX_UNSUBSCRIBED); + //error_log(__METHOD__.__LINE__.'->'.$mailbox.'/'.array2string($mailboxes)); + $mboxes = new Horde_Imap_Client_Mailbox_List($mailboxes); + //_debug_array($mboxes->count()); + foreach ($mboxes->getIterator() as $k =>$box) + { + //error_log(__METHOD__.__LINE__.'->'.$k); + if ($k!='user' && $k != '' && $k==$mailbox) return $box['mailbox']; //_debug_array(array($k => $client->status($k))); + } + return ($this->mailboxExist($mailbox)?$mailbox:false); + } + + /** + * mailboxExists + * + * @param string $mailbox + * @return boolean + */ + function mailboxExist($mailbox) + { + try + { + //error_log(__METHOD__.__LINE__.':'.$mailbox); + $currentMailbox = $this->currentMailbox(); + } + catch(Exception $e) + { + //error_log(__METHOD__.__LINE__.' failed detecting currentMailbox:'.$currentMailbox.':'.$e->getMessage()); + $currentMailbox=null; + unset($e); + } + try + { + //error_log(__METHOD__.__LINE__.':'.$mailbox); + $this->openMailbox($mailbox); + $returnvalue=true; + } + catch(Exception $e) + { + //error_log(__METHOD__.__LINE__.' failed opening:'.$mailbox.':'.$e->getMessage().' Called by:'.function_backtrace()); + unset($e); + $returnvalue=false; + } + if (!empty($currentMailbox) && $currentMailbox['mailbox'] != $mailbox) + { + try + { + //error_log(__METHOD__.__LINE__.':'.$currentMailbox .'<->'.$mailbox); + $this->openMailbox($currentMailbox['mailbox']); + } + catch(Exception $e) + { + //error_log(__METHOD__.__LINE__.' failed reopening:'.$currentMailbox.':'.$e->getMessage()); + unset($e); + } + } + return $returnvalue; + } + + /** + * getSpecialUseFolders + * + * @return current mailbox, or if none check on INBOX, and return upon existance + */ + function getCurrentMailbox() + { + $mailbox = $this->currentMailbox(); + if (!empty($mailbox)) return $mailbox['mailbox']; + if (empty($mailbox) && $this->mailboxExist('INBOX')) return 'INBOX'; + return null; + } + + /** + * getSpecialUseFolders + * + * @return array with special use folders + */ + function getSpecialUseFolders() + { + $mailboxes = $this->getMailboxes('',0,true); + $suF = array(); + foreach ($mailboxes as $box) + { + if ($box['MAILBOX']!='user' && $box['MAILBOX'] != '') + { + //error_log(__METHOD__.__LINE__.$k.'->'.array2string($box)); + if (isset($box['ATTRIBUTES'])&&!empty($box['ATTRIBUTES'])&& + stripos(strtolower(array2string($box['ATTRIBUTES'])),'\noselect')=== false&& + stripos(strtolower(array2string($box['ATTRIBUTES'])),'\nonexistent')=== false) + { + $suF[$box['MAILBOX']] = $box; + } + } + } + return $suF; + } + + /** + * getStatus + * + * @param string $mailbox + * @return array with counters + */ + function getStatus($mailbox) + { + $mailboxes = $this->listMailboxes($mailbox,Horde_Imap_Client::MBOX_ALL,array( + 'attributes'=>true, + 'children'=>true, //child info + 'delimiter'=>true, + 'special_use'=>true, + )); + + $mboxes = new Horde_Imap_Client_Mailbox_List($mailboxes); + //error_log(__METHOD__.__LINE__.array2string($mboxes->count())); + foreach ($mboxes->getIterator() as $k =>$box) + { + if ($k!='user' && $k != '' && $k==$mailbox) + { + if (stripos(array2string($box['attributes']),'\noselect')=== false) + { + $status = $this->status($k); + foreach ($status as $key => $v) + { + $_status[strtoupper($key)]=$v; + } + $_status['HIERACHY_DELIMITER'] = $_status['delimiter'] = $box['delimiter'];//$this->getDelimiter('user'); + $_status['ATTRIBUTES'] = $box['attributes']; + //error_log(__METHOD__.__LINE__.$k.'->'.array2string($_status)); + return $_status; + } + else + { + return false; + } + } + } + return false; + } + + /** + * Returns an array containing the names of the selected mailboxes + * + * @param string $reference base mailbox to start the search (default is current mailbox) + * @param string $restriction_search false or 0 means return all mailboxes + * true or 1 return only the mailbox that contains that exact name + * 2 return all mailboxes in that hierarchy level + * @param string $returnAttributes true means return an assoc array containing mailbox names and mailbox attributes + * false - the default - means return an array of mailboxes with only selected attributes like delimiter + * + * @return mixed array of mailboxes + */ + function getMailboxes($reference = '' , $restriction_search = 0, $returnAttributes = false) + { + if ( is_bool($restriction_search) ){ + $restriction_search = (int) $restriction_search; + } + $mailbox = ''; + if ( is_int( $restriction_search ) ){ + switch ( $restriction_search ) { + case 0: + $searchstring = $reference."*"; + break; + case 1: + $mailbox = $searchstring = $reference; + //$reference = '%'; + break; + case 2: + $searchstring = $reference."%"; + break; + } + }else{ + if ( is_string( $restriction_search ) ){ + $mailbox = $searchstring = $restriction_search; + } + } + //error_log(__METHOD__.__LINE__.array2string($mailbox)); + //if (is_array($mailbox))error_log(__METHOD__.__LINE__.function_backtrace()); + $options = array( + 'attributes'=>true, + 'children'=>true, //child info + 'delimiter'=>true, + 'special_use'=>true, + 'sort'=>true, + ); + if ($returnAttributes==false) + { + unset($options['attributes']); + unset($options['children']); + unset($options['special_use']); + } + $mailboxes = $this->listMailboxes($searchstring,Horde_Imap_Client::MBOX_ALL, $options); + //$mboxes = new Horde_Imap_Client_Mailbox_List($mailboxes); + //_debug_array($mboxes->count()); + foreach ((array)$mailboxes as $k =>$box) + { + //error_log(__METHOD__.__LINE__.' Box:'.$k.'->'.array2string($box)); + $ret[$k]=array('MAILBOX'=>$k,'ATTRIBUTES'=>$box['attributes'],'delimiter'=>$box['delimiter'],'SUBSCRIBED'=>true); + } + // for unknown reasons on ALL, UNSUBSCRIBED are not returned + //always fetch unsubscribed, think about only fetching it when $options['attributes'] is set + //but then allMailboxes are not all, .... + //if (!empty($mailbox) && !isset($ret[$mailbox])) + { + $mailboxes = $this->listMailboxes($searchstring,Horde_Imap_Client::MBOX_UNSUBSCRIBED, $options); + //$mboxes = new Horde_Imap_Client_Mailbox_List($mailboxes); + //_debug_array($mboxes->count()); + //error_log(__METHOD__.__LINE__.' '.$mailbox.':'.count((array)$mailboxes).'->'.function_backtrace()); + foreach ((array)$mailboxes as $k =>$box) + { + //error_log(__METHOD__.__LINE__.' Box:'.$k.' already In?'.array_key_exists($k,$boxexists).'->'.array2string($box)); + if(!array_key_exists($k,$ret)) + { + $ret[$k]=array('MAILBOX'=>$k,'ATTRIBUTES'=>$box['attributes'],'delimiter'=>$box['delimiter'],'SUBSCRIBED'=>false); + } + else + { + $ret[$k]['SUBSCRIBED'] = false; + } + } + } + return $ret; + } + + /** + * Returns an array containing the names of the subscribed selected mailboxes + * + * @param string $reference base mailbox to start the search + * @param string $restriction_search false or 0 means return all mailboxes + * true or 1 return only the mailbox that contains that exact name + * 2 return all mailboxes in that hierarchy level + * @param string $returnAttributes true means return an assoc array containing mailbox names and mailbox attributes + * false - the default - means return an array of mailboxes with only selected attributes like delimiter + * + * @return mixed array of mailboxes + */ + function listSubscribedMailboxes($reference = '' , $restriction_search = 0, $returnAttributes = false) + { + if ( is_bool($restriction_search) ){ + $restriction_search = (int) $restriction_search; + } + $mailbox = ''; + if ( is_int( $restriction_search ) ){ + switch ( $restriction_search ) { + case 0: + $searchstring = $reference."*"; + break; + case 1: + $mailbox = $searchstring = $reference; + //$reference = '%'; + break; + case 2: + $searchstring = $reference."%"; + break; + } + }else{ + if ( is_string( $restriction_search ) ){ + $mailbox = $searchstring = $restriction_search; + } + } + //error_log(__METHOD__.__LINE__.$mailbox); + $options = array( + 'attributes'=>true, + 'children'=>true, //child info + 'delimiter'=>true, + 'special_use'=>true, + 'sort'=>true, + ); + if ($returnAttributes==false) + { + unset($options['attributes']); + unset($options['children']); + unset($options['special_use']); + } + $mailboxes = $this->listMailboxes($searchstring,Horde_Imap_Client::MBOX_SUBSCRIBED_EXISTS, $options); + //$mboxes = new Horde_Imap_Client_Mailbox_List($mailboxes); + //_debug_array($mboxes->count()); + foreach ((array)$mailboxes as $k =>$box) + { + //error_log(__METHOD__.__LINE__.' Searched for:'.$mailbox.' got Box:'.$k.'->'.array2string($box).function_backtrace()); + if ($returnAttributes==false) + { + $ret[]=$k; + } + else + { + $ret[$k]=array('MAILBOX'=>$k,'ATTRIBUTES'=>$box['attributes'],'delimiter'=>$box['delimiter'],'SUBSCRIBED'=>true); + } + } + return $ret; + } + + /** + * Returns an array containing the names of the selected unsubscribed mailboxes + * + * @param string $reference base mailbox to start the search + * @param string $restriction_search false or 0 means return all mailboxes + * true or 1 return only the mailbox that contains that exact name + * 2 return all mailboxes in that hierarchy level + * + * @return mixed array of mailboxes + */ + function listUnSubscribedMailboxes($reference = '' , $restriction_search = 0) + { + if ( is_bool($restriction_search) ){ + $restriction_search = (int) $restriction_search; + } + + if ( is_int( $restriction_search ) ){ + switch ( $restriction_search ) { + case 0: + $mailbox = $reference."*"; + break; + case 1: + $mailbox = $reference; + $reference = '%'; + break; + case 2: + $mailbox = "%"; + break; + } + }else{ + if ( is_string( $restriction_search ) ){ + $mailbox = $restriction_search; + } + } + //error_log(__METHOD__.__LINE__.$mailbox); + $options = array( + 'sort'=>true, + //'flat'=>true, + ); + $mailboxes = $this->listMailboxes($mailbox,Horde_Imap_Client::MBOX_SUBSCRIBED_EXISTS, $options); + foreach ($mailboxes as $box) + { + //error_log(__METHOD__.__LINE__.' Box:'.$k.'->'.array2string($box['mailbox']->utf8)); + $sret[]=$box['mailbox']->utf8; + } + $unsubscribed = $this->listMailboxes($mailbox,Horde_Imap_Client::MBOX_UNSUBSCRIBED, $options); + foreach ($unsubscribed as $box) + { + //error_log(__METHOD__.__LINE__.' Box:'.$k.'->'.array2string($box['mailbox']->utf8)); + if (!in_array($box['mailbox']->utf8,$sret) && $box['mailbox']->utf8!='INBOX') $ret[]=$box['mailbox']->utf8; + } + return $ret; + } + + /** + * examineMailbox + * + * @param string $mailbox + * @return array of counters for mailbox + */ + function examineMailbox($mailbox) + { + if ($mailbox=='') return false; + $mailboxes = $this->listMailboxes($mailbox); + + $mboxes = new Horde_Imap_Client_Mailbox_List($mailboxes); + //_debug_array($mboxes->count()); + foreach ($mboxes->getIterator() as $k => $box) + { + //error_log(__METHOD__.__LINE__.array2string($box)); + unset($box); + if ($k!='user' && $k != '' && $k==$mailbox) + { + $status = $this->status($k, Horde_Imap_Client::STATUS_ALL | Horde_Imap_Client::STATUS_FLAGS | Horde_Imap_Client::STATUS_PERMFLAGS); + //error_log(__METHOD__.__LINE__.array2string($status)); + foreach ($status as $key => $v) + { + $_status[strtoupper($key)]=$v; + } + self::$supports_keywords[$this->ImapServerId] = stripos(array2string($_status['FLAGS']),'$label')!==false; + return $_status; + } + } + return false; + } + + /** + * returns the supported capabilities of the imap server + * return false if the imap server does not support capabilities + * + * @deprecated use capability() + * @return array the supported capabilites + */ + function getCapabilities() + { + $cap = $this->capability(); + foreach ($cap as $c => $v) + { + if (is_array($v)) + { + foreach ($v as $v) + { + $cap[$c.'='.$v] = true; + } + } + } + return $cap; + } + + /** + * Query a single capability + * + * @deprecated use queryCapability($capability) + * @param string $capability + * @return boolean + */ + function hasCapability($capability) + { + //return $this->queryCapability($capability); + if ($capability=='SUPPORTS_KEYWORDS') + { + //error_log(__METHOD__.__LINE__.' '.$capability.'->'.array2string(self::$supports_keywords)); + return self::$supports_keywords[$this->ImapServerId]; + } + try + { + $cap = $this->capability(); + } + catch (Exception $e) + { + if ($this->debug) error_log(__METHOD__.__LINE__.' error querying for capability:'.$capability.' ->'.$e->getMessage()); + return false; + } + if (!is_array($cap)) + { + error_log(__METHOD__.__LINE__.' error querying for capability:'.$capability.' Expected array but got->'.array2string($cap)); + return false; + } + foreach ($cap as $c => $v) + { + if (is_array($v)) + { + foreach ($v as $v) + { + $cap[$c.'='.$v] = true; + } + } + } + //error_log(__METHOD__.__LINE__.$capability.'->'.array2string($cap)); + if (isset($cap[$capability]) && $cap[$capability]) + { + return true; + } + else + { + return false; + } + } + + /** + * return the delimiter used by the current imap server + * @param mixed _type (1=personal, 2=user/other, 3=shared) + * @return string the delimimiter + */ + function getDelimiter($_type=1) + { + switch ($_type) + { + case 'user': + case 'other': + case 2: + $type=2; + break; + case 'shared': + case '': + case 3: + $type=3; + break; + case 'personal': + case 1: + default: + $type=1; + } + $namespaces = $this->getNamespaces(); + foreach ($namespaces as $nsp) + { + if ($nsp['type']==$type) return $nsp['delimiter']; + } + return "/"; + } + + /** + * Check if IMAP server supports group ACL, can be overwritten in extending classes + * + * If group ACL is supported getMailBoxUserName and getMailBoxAccountId should be + * modified too, to return correct values for groups. + * + * @return boolean true if group ACL is supported, false if not + */ + function supportsGroupAcl() + { + return false; + } + + /** + * get the effective Username for the Mailbox, as it is depending on the loginType + * + * @param string|int $_username account_id or account_lid + * @return string the effective username to be used to access the Mailbox + */ + function getMailBoxUserName($_username) + { + if (is_numeric($_username)) + { + $_username = $GLOBALS['egw']->accounts->id2name($_username); + } + else + { + $accountID = $GLOBALS['egw']->accounts->name2id($_username); + } + switch ($this->loginType) + { + case 'email': + $accountemail = $GLOBALS['egw']->accounts->id2name($accountID,'account_email'); + //$accountemail = $GLOBALS['egw']->accounts->read($GLOBALS['egw']->accounts->name2id($_username,'account_email')); + if (!empty($accountemail)) + { + list($lusername,$domain) = explode('@',$accountemail,2); + if (strtolower($domain) == strtolower($this->domainName) && !empty($lusername)) + { + $_username = $lusername; + } + } + break; + + case 'vmailmgr': + $_username .= '@'.$this->domainName; + break; + + case 'uidNumber': + $_username = 'u'.$accountID; + break; + default: + if (empty($this->loginType)) + { + // try to figure out by params['acc_imap_username'] + list($lusername,$domain) = explode('@',$this->params['acc_imap_username'],2); + if (strpos($_username,'@')===false && !empty($domain) && !empty($lusername)) + { + $_username = $_username.'@'.$domain; + } + } + } + return strtolower($_username); + } + + /** + * Get account_id from a mailbox username + * + * @param string $_username + * @return int|boolean account_id of user or false if no matching user found + */ + function getMailBoxAccountId($_username) + { + switch ($this->loginType) + { + case 'email': + $account_id = $GLOBALS['egw']->accounts->name2id($_username, 'account_email'); + if (!$account_id) + { + list($uid) = explode('@', $_username); + $account_id = $GLOBALS['egw']->accounts->name2id($uid, 'account_lid'); + } + break; + + case 'uidNumber': + $account_id = (int)substr($_username, 1); + break; + + default: + $account_id = $GLOBALS['egw']->accounts->name2id($_username, 'account_lid'); + } + return $account_id; + } + + /** + * Create mailbox string from given mailbox-name and user-name + * + * @param string $_folderName='' + * @return string utf-7 encoded (done in getMailboxName) + */ + function getUserMailboxString($_username, $_folderName='') + { + $nameSpaces = $this->getNameSpaceArray(); + + if(!isset($nameSpaces['others'])) { + return false; + } + + $_username = $this->getMailBoxUserName($_username); + if($this->loginType == 'vmailmgr' || $this->loginType == 'email' || $this->loginType == 'uidNumber') { + $_username .= '@'. $this->domainName; + } + + $mailboxString = $nameSpaces['others'][0]['name'] . $_username . (!empty($_folderName) ? $nameSpaces['others'][0]['delimiter'] . $_folderName : ''); + + return $mailboxString; + } + + /** + * get list of namespaces + * Note: a IMAPServer may present several namespaces under each key + * @return array with keys 'personal', 'shared' and 'others' and value array with values for keys 'name' and 'delimiter' + */ + function getNameSpaceArray() + { + static $types = array( + Horde_Imap_Client::NS_PERSONAL => 'personal', + Horde_Imap_Client::NS_OTHER => 'others', + Horde_Imap_Client::NS_SHARED => 'shared' + ); + //error_log(__METHOD__.__LINE__.array2string($types)); + $result = array(); + foreach($this->getNamespaces() as $data) + { + //error_log(__METHOD__.__LINE__.array2string($data)); + if (isset($types[$data['type']])) + { + $result[$types[$data['type']]][] = array( + 'type' => $types[$data['type']], + 'name' => $data['name'], + 'prefix' => $data['name'], + 'prefix_present' => !empty($data['name']), + 'delimiter' => $data['delimiter'], + ); + } + } + //error_log(__METHOD__.__LINE__.array2string($result)); + return $result; + } + + /** + * return the quota for the current user + * + * @param string $mailboxName + * @return mixed the quota for the current user -> array with all available Quota Information, or false + */ + function getStorageQuotaRoot($mailboxName) + { + $storageQuota = $this->getQuotaRoot($mailboxName); + foreach ($storageQuota as $qInfo) + { + if ($qInfo['storage']) + { + return array('USED'=>$qInfo['storage']['usage'],'QMAX'=>$qInfo['storage']['limit']); + } + } + return false; + } + + /** + * return the quota for another user + * used by admin connections only + * + * @param string $_username + * @param string $_what - what to retrieve either limit/QMAX, usage/USED or ALL is supported + * @return int|array|boolean the quota for specified user (by what) or array with values for "limit" and "usage", or false + */ + function getQuotaByUser($_username, $_what='QMAX') + { + $mailboxName = $this->getUserMailboxString($_username); + $storageQuota = $this->getQuotaRoot($mailboxName); + //error_log(__METHOD__.' Username:'.$_username.' Mailbox:'.$mailboxName.' getQuotaRoot('.$_what.'):'.array2string($storageQuota)); + + if (is_array($storageQuota) && isset($storageQuota[$mailboxName]) && is_array($storageQuota[$mailboxName]) && + isset($storageQuota[$mailboxName]['storage']) && is_array($storageQuota[$mailboxName]['storage'])) + { + switch($_what) + { + case 'QMAX': + $_what = 'limit'; + break; + case 'USED': + $_what = 'usage'; + case 'ALL': + return $storageQuota[$mailboxName]['storage']; + } + return isset($storageQuota[$mailboxName]['storage'][$_what]) ? (int)$storageQuota[$mailboxName]['storage'][$_what] : false; + } + + return false; + } + + /** + * returns information about a user + * + * Only a stub, as admin connection requires, which is only supported for Cyrus + * + * @param string $_username + * @return array userdata + */ + function getUserData($_username) + { + unset($_username); // not used + return array(); + } + + /** + * set userdata + * + * @param string $_username username of the user + * @param int $_quota quota in bytes + * @return bool true on success, false on failure + */ + function setUserData($_username, $_quota) + { + unset($_username, $_quota); // not used + return true; + } + + /** + * check if imap server supports given capability + * + * @param string $_capability the capability to check for + * @return bool true if capability is supported, false if not + */ + function supportsCapability($_capability) + { + return $this->hasCapability($_capability); + } + + /** + * Instance of emailadmin_sieve + * + * @var emailadmin_sieve + */ + private $sieve; + + public $scriptName; + public $error; + + //public $error; + + /** + * Proxy former felamimail bosieve methods to internal emailadmin_sieve instance + * + * @param string $name + * @param array $params + * @throws Exception + */ + public function __call($name,array $params=null) + { + if ($this->debug) error_log(__METHOD__.'->'.$name.' with params:'.array2string($params)); + switch($name) + { + case 'installScript': + case 'getScript': + case 'setActive': + case 'setEmailNotification': + case 'getEmailNotification': + case 'setRules': + case 'getRules': + case 'retrieveRules': + case 'getVacation': + case 'setVacation': + if (is_null($this->sieve)) + { + PEAR::setErrorHandling(PEAR_ERROR_EXCEPTION); + $this->sieve = new emailadmin_sieve($this); + $this->error =& $this->sieve->error; + } + $ret = call_user_func_array(array($this->sieve,$name),$params); + //error_log(__CLASS__.'->'.$name.'('.array2string($params).') returns '.array2string($ret)); + return $ret; + } + throw new egw_exception_wrong_parameter("No method '$name' implemented!"); + } + + /** + * Set vacation message for given user + * + * @param int|string $_euser nummeric account_id or imap username + * @param array $_vacation + * @param string $_scriptName=null + * @return boolean + */ + public function setVacationUser($_euser, array $_vacation, $_scriptName=null) + { + if ($this->debug) error_log(__METHOD__.' User:'.array2string($_euser).' Scriptname:'.array2string($_scriptName).' VacationMessage:'.array2string($_vacation)); + + if (is_numeric($_euser)) + { + $_euser = $this->getMailBoxUserName($_euser); + } + if (is_null($this->sieve) || $this->isAdminConnection !== $_euser) + { + $this->adminConnection($_euser); + PEAR::setErrorHandling(PEAR_ERROR_EXCEPTION); + $this->sieve = new emailadmin_sieve($this, $_euser, $_scriptName); + $this->scriptName =& $this->sieve->scriptName; + $this->error =& $this->sieve->error; + } + $ret = $this->setVacation($_vacation, $_scriptName); + + return $ret; + } + + /** + * Get vacation message for given user + * + * @param int|string $_euser nummeric account_id or imap username + * @param string $_scriptName=null + * @throws Exception on connection error or authentication failure + * @return array + */ + public function getVacationUser($_euser, $_scriptName=null) + { + if ($this->debug) error_log(__METHOD__.' User:'.array2string($_euser)); + + if (is_numeric($_euser)) + { + $_euser = $this->getMailBoxUserName($_euser); + } + if (is_null($this->sieve) || $this->isAdminConnection !== $_euser) + { + $this->adminConnection($_euser); + PEAR::setErrorHandling(PEAR_ERROR_EXCEPTION); + $this->sieve = new emailadmin_sieve($this, $_euser, $_scriptName); + $this->error =& $this->sieve->error; + $this->scriptName =& $this->sieve->scriptName; + } + return $this->sieve->getVacation(); + } + + /** + * Return fields or tabs which should be readonly in UI for given imap implementation + * + * @return array fieldname => true pairs or 'tabs' => array(tabname => true) + */ + public static function getUIreadonlys() + { + return array(); + } +} diff --git a/emailadmin/inc/class.emailadmin_imap_cyrus.inc.php b/emailadmin/inc/class.emailadmin_imap_cyrus.inc.php new file mode 100644 index 0000000000..240a3b3544 --- /dev/null +++ b/emailadmin/inc/class.emailadmin_imap_cyrus.inc.php @@ -0,0 +1,190 @@ + + * @author Klaus Leithoff + * @author Lars Kneschke + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @version $Id$ + */ + +/** + * Manages connection to Cyrus IMAP server + */ +class emailadmin_imap_cyrus extends emailadmin_imap +{ + /** + * Label shown in EMailAdmin + */ + const DESCRIPTION = 'Cyrus'; + + /** + * Capabilities of this class (pipe-separated): default, sieve, admin, logintypeemail + */ + const CAPABILITIES = 'default|sieve|timedsieve|admin|logintypeemail'; + + /** + * prefix for groupnames, when using groups in ACL Management + */ + const ACL_GROUP_PREFIX = 'group:'; + + // mailbox delimiter + var $mailboxDelimiter = '.'; + + // mailbox prefix + var $mailboxPrefix = ''; + + /** + * Updates an account + * + * @param array $_hookValues only value for key 'account_lid' and 'new_passwd' is used + */ + function addAccount($_hookValues) + { + return $this->updateAccount($_hookValues); + } + + /** + * Delete an account + * + * @param array $_hookValues only value for key 'account_lid' is used + */ + function deleteAccount($_hookValues) + { + // some precausion to really delete just _one_ account + if (strpos($_hookValues['account_lid'],'%') !== false || + strpos($_hookValues['account_lid'],'*') !== false) + { + return false; + } + return !!$this->deleteUsers($_hookValues['account_lid']); + } + + /** + * Delete multiple (user-)mailboxes via a wildcard, eg. '%' for whole domain + * + * Domain is the configured domain and it uses the Cyrus admin user + * + * @return string $username='%' username containing wildcards, default '%' for all users of a domain + * @return int|boolean number of deleted mailboxes on success or false on error + */ + function deleteUsers($username='%') + { + if(!$this->acc_imap_administration || empty($username)) + { + return false; + } + + // we need a admin connection + $this->adminConnection(); + + $mailboxName = $this->getUserMailboxString($username); + list($reference,$restriction) = explode($username,$mailboxName,2); + + try { + $mboxes = $this->getMailboxes($reference,$username.$restriction); + //error_log(__METHOD__."('$username') getMailboxes('$reference','$username$restriction') = ".array2string($mboxes)); + + foreach($mboxes as $mbox) + { + // give the admin account the rights to delete this mailbox + $this->setACL($mbox, $this->adminUsername, 'lrswipcda'); + $this->deleteMailbox($mbox); + } + } + catch(Horde_Imap_Client_Exception $e) { + _egw_log_exception($e); + $this->disconnect(); + return false; + } + $this->disconnect(); + + return count($mboxes); + } + + /** + * returns information about a user + * currently only supported information is the current quota + * + * @param string $_username + * @return array userdata + */ + function getUserData($_username) + { + $this->adminConnection(); + $userData = array(); + + if(($quota = $this->getQuotaByUser($_username,'ALL'))) + { + $userData['quotaLimit'] = (int)($quota['limit'] / 1024); + $userData['quotaUsed'] = (int)($quota['usage'] / 1024); + } + //error_log(__LINE__.': '.__METHOD__."('$_username') quota=".array2string($quota).' returning '.array2string($userData)); + + $this->disconnect(); + + return $userData; + } + + /** + * Set information about a user + * currently only supported information is the current quota + * + * @param string $_username + * @param int $_quota + */ + function setUserData($_username, $_quota) + { + if(!$this->acc_imap_administration) + { + return false; + } + + // create a admin connection + $this->adminConnection(); + + $mailboxName = $this->getUserMailboxString($_username); + + $this->setQuota($mailboxName, array('STORAGE' => (int)$_quota > 0 ? (int)$_quota*1024 : -1)); + + $this->disconnect(); + + return true; + } + + /** + * Updates an account + * + * @param array $_hookValues only value for key 'account_lid' and 'new_passwd' is used + */ + function updateAccount($_hookValues) + { + if(!$this->acc_imap_administration) + { + return false; + } + + // we need a admin connection + $this->adminConnection(); + + // create the mailbox, with the account_lid, as it is passed from the hook values (gets transformed there if needed) + $mailboxName = $this->getUserMailboxString($_hookValues['account_lid'], $mailboxName); + // make sure we use the correct username here. + $username = $this->getMailBoxUserName($_hookValues['account_lid']); + $folderInfo = $this->getMailboxes('', $mailboxName, true); + if(empty($folderInfo)) + { + try { + $this->createMailbox($mailboxName); + $this->setACL($mailboxName, $username, "lrswipcda"); + } + catch(Horde_Imap_Client_Exception $e) { + _egw_log_exception($e); + } + } + $this->disconnect(); + } +} diff --git a/emailadmin/inc/class.emailadmin_imap_dovecot.inc.php b/emailadmin/inc/class.emailadmin_imap_dovecot.inc.php new file mode 100644 index 0000000000..f988b67fad --- /dev/null +++ b/emailadmin/inc/class.emailadmin_imap_dovecot.inc.php @@ -0,0 +1,257 @@ + + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @version $Id$ + */ + +/** + * Manages connection to Dovecot IMAP server + * + * Basic differences to cyrusimap: + * - no real admin user, but master user, whos password can be used to connect instead of real user + * - mailboxes have to be deleted in filesystem (no IMAP command for that) + * --> require by webserver writable user_home to be configured, otherwise deleting get ignored like with defaultimap + * - quota can be read, but not set + */ +class emailadmin_imap_dovecot extends emailadmin_imap +{ + /** + * Label shown in EMailAdmin + */ + const DESCRIPTION = 'Dovecot'; + /** + * Capabilities of this class (pipe-separated): default, sieve, admin, logintypeemail + */ + const CAPABILITIES = 'default|sieve|timedsieve|admin|logintypeemail'; + + /** + * prefix for groupnames, when using groups in ACL Management + */ + const ACL_GROUP_PREFIX = '$'; + + // mailbox delimiter + var $mailboxDelimiter = '.'; + + // mailbox prefix + var $mailboxPrefix = ''; + + /** + * To enable deleting of a mailbox user_home has to be set and be writable by webserver + * + * Supported placeholders are: + * - %d domain + * - %u username part of email + * - %s email address + * + * @var string + */ + var $user_home; // = '/var/dovecot/imap/%d/%u'; + + /** + * Ensure we use an admin connection + * + * Prefixes adminUsername with real username (separated by an asterisk) + * + * @param string $_username=null create an admin connection for given user or $this->acc_imap_username + */ + function adminConnection($_username=null) + { + // generate admin user name of $username + if (($pos = strpos($this->acc_imap_admin_username, '*')) !== false) // remove evtl. set username + { + $this->params['acc_imap_admin_username'] = substr($this->acc_imap_admin_username, $pos+1); + } + $this->params['acc_imap_admin_username'] = (is_string($_username) ? $_username : $this->acc_imap_username). + '*'.$this->acc_imap_admin_username; + + parent::adminConnection($_username); + } + + /** + * Updates an account + * + * @param array $_hookValues only value for key 'account_lid' and 'new_passwd' is used + */ + function addAccount($_hookValues) + { + return $this->updateAccount($_hookValues); + } + + /** + * Delete an account + * + * @param array $_hookValues only value for key 'account_lid' is used + */ + function deleteAccount($_hookValues) + { + // some precausion to really delete just _one_ account + if (strpos($_hookValues['account_lid'],'%') !== false || + strpos($_hookValues['account_lid'],'*') !== false) + { + return false; + } + return !!$this->deleteUsers($_hookValues['account_lid']); + } + + /** + * Delete multiple (user-)mailboxes via a wildcard, eg. '%' for whole domain + * + * Domain is the configured domain and it uses the Cyrus admin user + * + * @return string $username='%' username containing wildcards, default '%' for all users of a domain + * @return int|boolean number of deleted mailboxes on success or false on error + */ + function deleteUsers($username='%') + { + if(!$this->acc_imap_administration || empty($username)) + { + return false; + } + + // dovecot can not delete mailbox, they need to be physically deleted in filesystem (webserver needs write-rights to do so!) + if (empty($this->user_home)) + { + return false; + } + $replace = array('%d' => $this->domainName, '%u' => $username, '%s' => $username.'@'.$this->domainName); + + if ($username == '%') + { + if (($pos = strpos($this->user_home, '%d')) === false) + { + throw new egw_exception_assertion_failed("user_home='$this->user_home' contains no domain-part '%d'!"); + } + $home = strtr(substr($this->user_home, 0, $pos+2), $replace); + + $ret = count(scandir($home))-2; + } + else + { + $home = strtr($this->user_home, $replace); + + $ret = 1; + } + if (!is_writable(dirname($home)) || !self::_rm_recursive($home)) + { + error_log(__METHOD__."('$username') Failed to delete $home!"); + return false; + } + return $ret; + } + + /** + * Recursively delete a directory (or file) + * + * @param string $path + * @return boolean true on success, false on failure + */ + private function _rm_recursive($path) + { + if (is_dir($path)) + { + foreach(scandir($path) as $file) + { + if ($file == '.' || $file == '..') continue; + + if (is_dir($path)) + { + self::_rm_recursive($path.'/'.$file); + } + elseif (!unlink($path.'/'.$file)) + { + return false; + } + } + if (!rmdir($path)) + { + return false; + } + } + elseif(!unlink($path)) + { + return false; + } + return true; + } + + /** + * returns information about a user + * currently only supported information is the current quota + * + * @param string $_username + * @return array userdata + */ + function getUserData($_username) + { + if (isset($this->username)) $bufferUsername = $this->username; + if (isset($this->loginName)) $bufferLoginName = $this->loginName; + $this->username = $_username; + $nameSpaces = $this->getNameSpaces(); + $mailBoxName = $this->getUserMailboxString($this->username); + $this->loginName = str_replace((is_array($nameSpaces)?$nameSpaces['others'][0]['name']:'user/'),'',$mailBoxName); // we need to strip the namespacepart + + // now disconnect to be able to reestablish the connection with the targetUser while we go on + try + { + $this->adminConnection(); + } + catch (Exception $e) + { + // error_log(__METHOD__.__LINE__." Could not establish admin Connection!".$e->getMessage()); + return array(); + } + + $userData = array(); + // we are authenticated with master but for current user + if(($quota = $this->getStorageQuotaRoot('INBOX'))) + { + $userData['quotaLimit'] = (int) ($quota['limit'] / 1024); + $userData['quotaUsed'] = (int) ($quota['usage'] / 1024); + } + $this->username = $bufferUsername; + $this->loginName = $bufferLoginName; + $this->disconnect(); + + //error_log(__METHOD__."('$_username') getStorageQuotaRoot('INBOX')=".array2string($quota).' returning '.array2string($userData)); + return $userData; + } + + /** + * Set information about a user + * currently only supported information is the current quota + * + * Dovecot get's quota from it's user-db, but cant set it --> ignored + * + * @param string $_username + * @param int $_quota + * @return boolean + */ + function setUserData($_username, $_quota) + { + unset($_username); unset($_quota); // not used, but required by function signature + + return true; + } + + /** + * Updates an account + * + * @param array $_hookValues only value for key 'account_lid' and 'new_passwd' is used + */ + function updateAccount($_hookValues) + { + unset($_hookValues); // not used, but required by function signature + + if(!$this->acc_imap_administration) + { + return false; + } + // mailbox get's automatic created with full rights for user + return true; + } +} diff --git a/emailadmin/inc/class.emailadmin_imapbase.inc.php b/emailadmin/inc/class.emailadmin_imapbase.inc.php new file mode 100644 index 0000000000..b9fa65f95d --- /dev/null +++ b/emailadmin/inc/class.emailadmin_imapbase.inc.php @@ -0,0 +1,6528 @@ + + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @version $Id$ + */ + +/** + * Mail worker class + * -provides backend functionality for all classes in Mail + * -provides classes that may be used by other apps too + * + * @link https://github.com/horde/horde/blob/master/imp/lib/Contents.php + */ +class emailadmin_imapbase +{ + /** + * the current selected user profile + * @var int + */ + var $profileID = 0; + + /** + * the current display char set + * @var string + */ + static $displayCharset; + static $activeFolderCache; + static $folderStatusCache; + static $supportsORinQuery; + + /** + * Active preferences + * + * @var array + */ + var $mailPreferences; + + /** + * active html Options + * + * @var array + */ + var $htmlOptions; + + /** + * Active mimeType + * + * @var string + */ + var $activeMimeType; + + /** + * Active incomming (IMAP) Server Object + * + * @var emailadmin_imap + */ + var $icServer; + + /** + * Active outgoing (smtp) Server Object + * + * @var emailadmin_smtp + */ + var $ogServer; + + /** + * errorMessage + * + * @var string $errorMessage + */ + var $errorMessage; + + /** + * switch to enable debug; sometimes debuging is quite handy, to see things. check with the error log to see results + * @var boolean + */ + static $debug = false; //true; + static $debugTimes = false; //true; + + /** + * static used to hold the mail Config values + * @array + */ + static $mailConfig; + + /** + * static used to configure tidy - if tidy is loadable, this config is used with tidy to straighten out html, instead of using purifiers tidy mode + * + * @array + */ + static $tidy_config = array('clean'=>true,'output-html'=>true,'join-classes'=>true,'join-styles'=>true,'show-body-only'=>"auto",'word-2000'=>true,'wrap'=>0); + + /** + * static used to configure htmLawed, for use with emails + * + * @array + */ + static $htmLawed_config = array('comment'=>1, //remove comments + 'make_tag_strict' => 3, // 3 is a new own config value, to indicate that transformation is to be performed, but don't transform font as size transformation of numeric sizes to keywords alters the intended result too much + 'keep_bad'=>2, //remove tags but keep element content (4 and 6 keep element content only if text (pcdata) is valid in parent element as per specs, this may lead to textloss if balance is switched on) + 'balance'=>1,//turn off tag-balancing (config['balance']=>0). That will not introduce any security risk; only standards-compliant tag nesting check/filtering will be turned off (basic tag-balance will remain; i.e., there won't be any unclosed tag, etc., after filtering) + 'direct_list_nest' => 1, + 'allow_for_inline' => array('table','div','li','p'),//block elements allowed for nesting when only inline is allowed; Example span does not allow block elements as table; table is the only element tested so far + // tidy eats away even some wanted whitespace, so we switch it off; + // we used it for its compacting and beautifying capabilities, which resulted in better html for further processing + 'tidy'=>0, + 'elements' => "* -script", + 'deny_attribute' => 'on*', + 'schemes'=>'href: file, ftp, http, https, mailto; src: cid, data, file, ftp, http, https; *:file, http, https, cid, src', + 'hook_tag' =>"hl_email_tag_transform", + ); + + /** + * static used define abbrevations for common access rights + * + * @array + */ + static $aclShortCuts = array('' => array('label'=>'none','title'=>'The user has no rights whatsoever.'), + 'lrs' => array('label'=>'readable','title'=>'Allows a user to read the contents of the mailbox.'), + 'lprs' => array('label'=>'post','title'=>'Allows a user to read the mailbox and post to it through the delivery system by sending mail to the submission address of the mailbox.'), + 'ilprs' => array('label'=>'append','title'=>'Allows a user to read the mailbox and append messages to it, either via IMAP or through the delivery system.'), + 'cdilprsw' => array('label'=>'write','title'=>'Allows a user to read the maibox, post to it, append messages to it, and delete messages or the mailbox itself. The only right not given is the right to change the ACL of the mailbox.'), + 'acdilprsw' => array('label'=>'all','title'=>'The user has all possible rights on the mailbox. This is usually granted to users only on the mailboxes they own.'), + 'custom' => array('label'=>'custom','title'=>'User defined combination of rights for the ACL'), + ); + + /** + * Folders that get automatic created AND get translated to the users language + * their creation is also controlled by users mailpreferences. if set to none / dont use folder + * the folder will not be automatically created. This is controlled in emailadmin_imapbase->getFolderObjects + * so changing names here, must include a change of keywords there as well. Since these + * foldernames are subject to translation, keep that in mind too, if you change names here. + * lang('Drafts'), lang('Templates'), lang('Sent'), lang('Trash'), lang('Junk'), lang('Outbox') + * ActiveSync: + * Outbox is needed by Nokia Clients to be able to send Mails + * @var array + */ + static $autoFolders = array('Drafts', 'Templates', 'Sent', 'Trash', 'Junk', 'Outbox'); + + /** + * Array to cache the specialUseFolders, if existing + * @var array + */ + static $specialUseFolders; + + /** + * IDNA2 instance + * + * @var egw_idna + */ + static $idna2; + + /** + * Hold instances by profileID for getInstance() singleton + * + * @var array + */ + private static $instances = array(); + + /** + * Singleton for emailadmin_imapbase + * + * @param boolean $_restoreSession=true + * @param int $_profileID=0 + * @param boolean $_validate=true - flag wether the profileid should be validated or not, if validation is true, you may receive a profile + * not matching the input profileID, if we can not find a profile matching the given ID + * @param mixed boolean/object $_icServerObject - if object, return instance with object set as icServer + * immediately, if boolean === true use oldImapServer in constructor + * @param boolean $_reuseCache=null if null it is set to the value of $_restoreSession + * @return emailadmin_imapbase + */ + public static function getInstance($_restoreSession=true, &$_profileID=0, $_validate=true, $_oldImapServerObject=false, $_reuseCache=null) + { + //$_restoreSession=false; + if (is_null($_reuseCache)) $_reuseCache = $_restoreSession; + //error_log(__METHOD__.' ('.__LINE__.') '.' RestoreSession:'.$_restoreSession.' ProfileId:'.$_profileID.'/'.emailadmin_bo::getUserDefaultAccID().' for user:'.$GLOBALS['egw_info']['user']['account_lid'].' called from:'.function_backtrace()); + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($_oldImapServerObject)); + if ($_oldImapServerObject instanceof emailadmin_imap) + { + if (!is_object(self::$instances[$_profileID])) + { + self::$instances[$_profileID] = new emailadmin_imapbase('utf-8',false,$_profileID,false,$_reuseCache); + } + self::$instances[$_profileID]->icServer = $_oldImapServerObject; + self::$instances[$_profileID]->accountid= $_oldImapServerObject->ImapServerId; + self::$instances[$_profileID]->profileID= $_oldImapServerObject->ImapServerId; + self::$instances[$_profileID]->mailPreferences = $GLOBALS['egw_info']['user']['preferences']['mail']; + self::$instances[$_profileID]->htmlOptions = self::$instances[$_profileID]->mailPreferences['htmlOptions']; + return self::$instances[$_profileID]; + } + if ($_profileID == 0) + { + if (isset($GLOBALS['egw_info']['user']['preferences']['mail']['ActiveProfileID']) && !empty($GLOBALS['egw_info']['user']['preferences']['mail']['ActiveProfileID'])) + { + $profileID = (int)$GLOBALS['egw_info']['user']['preferences']['mail']['ActiveProfileID']; + } + else + { + $profileID = emailadmin_bo::getUserDefaultAccID(); + } + if ($profileID!=$_profileID) $_restoreSession==false; + $_profileID=$profileID; + if (self::$debug) error_log(__METHOD__.' ('.__LINE__.') '.' called with profileID==0 using '.$profileID.' instead->'.function_backtrace()); + } + // no validation or restoreSession for old ImapServer Object, just fetch it and return it + if ($_oldImapServerObject===true) + { + return new emailadmin_imapbase('utf-8',false,$_profileID,true,$_reuseCache); + } + if ($_profileID != 0 && $_validate) + { + $profileID = self::validateProfileID($_profileID); + if ($profileID != $_profileID) + { + if (self::$debug) + { + error_log(__METHOD__.' ('.__LINE__.') '.' Validation of profile with ID:'.$_profileID.' failed. Using '.$profileID.' instead.'); + error_log(__METHOD__.' ('.__LINE__.') '.' # Instance='.$GLOBALS['egw_info']['user']['domain'].', User='.$GLOBALS['egw_info']['user']['account_lid']); + } + $_profileID = $profileID; + //$GLOBALS['egw']->preferences->add('mail','ActiveProfileID',$_profileID,'user'); + // save prefs + //$GLOBALS['egw']->preferences->save_repository(true); + } + //egw_cache::setSession('mail','activeProfileID',$_profileID); + } + //error_log(__METHOD__.' ('.__LINE__.') '.' RestoreSession:'.$_restoreSession.' ProfileId:'.$_profileID.' called from:'.function_backtrace()); + if ($_profileID && (!isset(self::$instances[$_profileID]) || $_restoreSession===false)) + { + self::$instances[$_profileID] = new emailadmin_imapbase('utf-8',$_restoreSession,$_profileID,false,$_reuseCache); + } + else + { + //refresh objects + try + { + self::$instances[$_profileID]->icServer = emailadmin_account::read($_profileID)->imapServer(); + self::$instances[$_profileID]->ogServer = emailadmin_account::read($_profileID)->smtpServer(); + // TODO: merge mailprefs into userprefs, for easy treatment + self::$instances[$_profileID]->mailPreferences = $GLOBALS['egw_info']['user']['preferences']['mail']; + self::$instances[$_profileID]->htmlOptions = self::$instances[$_profileID]->mailPreferences['htmlOptions']; + } catch (egw_exception $e) + { + $newprofileID = emailadmin_bo::getUserDefaultAccID(); + // try loading the default profile for the user + error_log(__METHOD__.' ('.__LINE__.') '." Loading the Profile for ProfileID ".$_profileID.' failed for icServer; '.$e->getMessage().' Trigger new instance for Default-Profile '.$newprofileID.'. called from:'.function_backtrace()); + if ($newprofileID) + { + self::$instances[$newprofileID] = new emailadmin_imapbase('utf-8',false,$newprofileID,false,$_reuseCache); + $_profileID = $newprofileID; + } + else + { + throw $e; + } + } + self::storeActiveProfileIDToPref(self::$instances[$_profileID]->icServer, $_profileID, $_validate ); + } + self::$instances[$_profileID]->profileID = $_profileID; + if (!isset(self::$instances[$_profileID]->idna2)) self::$instances[$_profileID]->idna2 = new egw_idna; + //if ($_profileID==0); error_log(__METHOD__.' ('.__LINE__.') '.' RestoreSession:'.$_restoreSession.' ProfileId:'.$_profileID); + if (is_null(self::$mailConfig)) self::$mailConfig = config::read('mail'); + return self::$instances[$_profileID]; + } + + /** + * store given ProfileID to Session and pref + * + * @param int $_profileID=0 + * @param boolean $_testConnection=0 + * @return mixed $_profileID or false on failed ConnectionTest + */ + public static function storeActiveProfileIDToPref($_icServerObject, $_profileID=0, $_testConnection=true) + { + if (isset($GLOBALS['egw_info']['user']['preferences']['mail']['ActiveProfileID']) && !empty($GLOBALS['egw_info']['user']['preferences']['mail']['ActiveProfileID'])) + { + $oldProfileID = (int)$GLOBALS['egw_info']['user']['preferences']['mail']['ActiveProfileID']; + } + else + { + $oldProfileID = emailadmin_bo::getUserDefaultAccID(); + } + if ($_testConnection) + { + try + { + $mailbox = $_icServerObject->getCurrentMailbox(); + } + catch (Exception $e) + { + if ($_profileID != emailadmin_bo::getUserDefaultAccID()) $_profileID = emailadmin_bo::getUserDefaultAccID(); + error_log(__METHOD__.__LINE__.' '.$e->getMessage()); + return false; + } + } + if ($oldProfileID != $_profileID) + { + if ($oldProfileID && $_profileID==0) $_profileID = $oldProfileID; + $GLOBALS['egw']->preferences->add('mail','ActiveProfileID',$_profileID,'user'); + // save prefs + $GLOBALS['egw']->preferences->save_repository(true); + $GLOBALS['egw_info']['user']['preferences']['mail']['ActiveProfileID'] = $_profileID; + egw_cache::setSession('mail','activeProfileID',$_profileID); + } + return $_profileID; + } + + /** + * Validate given account acc_id to make sure account is valid for current user + * + * Validation checks: + * - non-empty imap-host + * - non-empty imap-username + * + * @param int $_acc_id=0 + * @return int validated acc_id -> either acc_id given, or first valid one + */ + public static function validateProfileID($_acc_id=0) + { + if ($_acc_id) + { + try { + $account = emailadmin_account::read($_acc_id); + if ($account->is_imap()) + { + return $_acc_id; + } + if (self::$debug) error_log(__METHOD__."($_acc_id) account NOT valid, no imap-host!"); + } + catch (Exception $e) { + unset($e); + if (self::$debug) error_log(__METHOD__."($_acc_id) account NOT found!"); + } + } + // no account specified or specified account not found or not valid + // --> search existing account for first valid one and return that + foreach(emailadmin_account::search($only_current_user=true, 'acc_imap_host') as $acc_id => $imap_host) + { + if (!empty($imap_host) && ($account = emailadmin_account::read($acc_id)) && $account->is_imap()) + { + if (self::$debug && $_acc_id) error_log(__METHOD__."($_acc_id) using $acc_id instead"); + return $acc_id; + } + } + if (self::$debug) error_log(__METHOD__."($_acc_id) NO valid account found!"); + return 0; + } + + + /** + * Private constructor, use emailadmin_imapbase::getInstance() instead + * + * @param string $_displayCharset='utf-8' + * @param boolean $_restoreSession=true + * @param int $_profileID=0 if not nummeric, we assume we only want an empty class object + * @param boolean $_oldImapServerObject=false + * @param boolean $_reuseCache=null if null it is set to the value of $_restoreSession + */ + private function __construct($_displayCharset='utf-8',$_restoreSession=true, $_profileID=0, $_oldImapServerObject=false, $_reuseCache=null) + { + if (is_null($_reuseCache)) $_reuseCache = $_restoreSession; + if (!empty($_displayCharset)) self::$displayCharset = $_displayCharset; + // not nummeric, we assume we only want an empty class object + if (!is_numeric($_profileID)) return true; + if ($_restoreSession) + { + //error_log(__METHOD__." Session restore ".function_backtrace()); + $this->restoreSessionData(); + $lv_mailbox = $this->sessionData['mailbox']; + $firstMessage = $this->sessionData['previewMessage']; + } + else + { + $this->restoreSessionData(); + $lv_mailbox = $this->sessionData['mailbox']; + $firstMessage = $this->sessionData['previewMessage']; + $this->sessionData = array(); + } + if (!$_reuseCache) $this->forcePrefReload(); + try + { + $this->profileID = self::validateProfileID($_profileID); + $this->accountid = $GLOBALS['egw_info']['user']['account_id']; + + //error_log(__METHOD__.' ('.__LINE__.') '." ProfileID ".$this->profileID.' called from:'.function_backtrace()); + $acc = emailadmin_account::read($this->profileID); + } + catch (Exception $e) + { + throw new egw_exception(__METHOD__." failed to instanciate emailadmin_imapbase for $_profileID / ".$this->profileID." with error:".$e->getMessage());; + } + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($acc->imapServer())); + $this->icServer = ($_oldImapServerObject?$acc->oldImapServer():$acc->imapServer()); + $this->ogServer = $acc->smtpServer(); + // TODO: merge mailprefs into userprefs, for easy treatment + $this->mailPreferences = $GLOBALS['egw_info']['user']['preferences']['mail']; + $this->htmlOptions = $this->mailPreferences['htmlOptions']; + if (isset($this->icServer->ImapServerId) && !empty($this->icServer->ImapServerId)) + { + $_profileID = $this->profileID = $GLOBALS['egw_info']['user']['preferences']['mail']['ActiveProfileID'] = $this->icServer->ImapServerId; + } + + if (is_null(self::$mailConfig)) self::$mailConfig = config::read('mail'); + if (!isset(self::$idna2)) self::$idna2 = new egw_idna; + } + + /** + * forceEAProfileLoad + * used to force the load of a specific emailadmin profile; we assume administrative use only (as of now) + * @param int $_profile_id must be a value lower than 0 (emailadmin profile) + * @return object instance of emailadmin_imapbase (by reference) + */ + public static function &forceEAProfileLoad($_profile_id) + { + self::unsetCachedObjects($_profile_id); + $mail = emailadmin_imapbase::getInstance(false, $_profile_id,false); + //_debug_array( $_profile_id); + $mail->icServer = emailadmin_account::read($_profile_id)->imapServer(); + $mail->ogServer = emailadmin_account::read($_profile_id)->smtpServer(); + return $mail; + } + + /** + * trigger the force of the reload of the SessionData by resetting the session to an empty array + */ + public static function forcePrefReload() + { + // unset the mail_preferences session object, to force the reload/rebuild + $GLOBALS['egw']->session->appsession('mail_preferences','mail',serialize(array())); + $GLOBALS['egw']->session->appsession('session_data','emailadmin',serialize(array())); + emailadmin_imapbase::resetFolderObjectCache(); + } + + /** + * restore the SessionData + */ + function restoreSessionData() + { + $this->sessionData = array();//egw_cache::getCache(egw_cache::SESSION,'mail','session_data',$callback=null,$callback_params=array(),$expiration=60*60*1); + self::$activeFolderCache = egw_cache::getCache(egw_cache::INSTANCE,'email','activeMailbox'.trim($GLOBALS['egw_info']['user']['account_id']),$callback=null,$callback_params=array(),$expiration=60*60*10); + if (!empty(self::$activeFolderCache[$this->profileID])) $this->sessionData['mailbox'] = self::$activeFolderCache[$this->profileID]; + } + + /** + * saveSessionData saves session data + */ + function saveSessionData() + { + //error_log(__METHOD__.' ('.__LINE__.') '.array2string(array_keys($this->sessionData))); + if (!empty($this->sessionData['mailbox'])) self::$activeFolderCache[$this->profileID]=$this->sessionData['mailbox']; + if (isset(self::$activeFolderCache) && is_array(self::$activeFolderCache)) + { + egw_cache::setCache(egw_cache::INSTANCE,'email','activeMailbox'.trim($GLOBALS['egw_info']['user']['account_id']),self::$activeFolderCache, $expiration=60*60*10); + } + } + + /** + * unset certain CachedObjects for the given profile id, unsets the profile for default ID=0 as well + * + * 1) icServerIMAP_connectionError + * 2) icServerSIEVE_connectionError + * 3) defaultimap_nameSpace + * 4) StructureCache (emailStructure Objects) + * 5) SummaryCache (emailSummary Objects) + * 6) INSTANCE OF MAIL_BO + * + * @param int $_profileID=null default profile of user as returned by getUserDefaultProfileID + * @return void + */ + static function unsetCachedObjects($_profileID=null) + { + if (is_null($_profileID)) $_profileID = emailadmin_account::get_default_acc_id(); + //error_log(__METHOD__.__LINE__.' called with ProfileID:'.$_profileID.' from '.function_backtrace()); + if (!is_array($_profileID) && (is_numeric($_profileID) || !(stripos($_profileID,'tracker_')===false))) + { + self::resetConnectionErrorCache($_profileID); + $structure = egw_cache::getCache(egw_cache::INSTANCE,'email','structureCache'.trim($GLOBALS['egw_info']['user']['account_id']),$callback=null,$callback_params=array(),$expiration=60*60*1); + if (isset($structure[$_profileID])) + { + unset($structure[$_profileID]); + egw_cache::setCache(egw_cache::INSTANCE,'email','structureCache'.trim($GLOBALS['egw_info']['user']['account_id']),$structure, $expiration=60*60*1); + } + $summary = egw_cache::getCache(egw_cache::INSTANCE,'email','summaryCache'.trim($GLOBALS['egw_info']['user']['account_id']),$callback=null,$callback_params=array(),$expiration=60*60*1); + if (isset($summary[$_profileID])) + { + unset($summary[$_profileID]); + egw_cache::setCache(egw_cache::INSTANCE,'email','summaryCache'.trim($GLOBALS['egw_info']['user']['account_id']),$summary, $expiration=60*60*1); + } + $rawHeadersCache = egw_cache::getCache(egw_cache::INSTANCE,'email','rawHeadersCache'.trim($GLOBALS['egw_info']['user']['account_id']),$callback=null,$callback_params=array(),$expiration=60*60*1); + if (isset($rawHeadersCache[$_profileID])) + { + unset($rawHeadersCache[$_profileID]); + egw_cache::setCache(egw_cache::INSTANCE,'email','rawHeadersCache'.trim($GLOBALS['egw_info']['user']['account_id']),$rawHeadersCache, $expiration=60*60*1); + } + //reset folderObject cache, to trigger reload + self::resetFolderObjectCache($_profileID); + //reset counter of deleted messages per folder + $eMailListContainsDeletedMessages = egw_cache::getCache(egw_cache::INSTANCE,'email','eMailListContainsDeletedMessages'.trim($GLOBALS['egw_info']['user']['account_id']),$callback=null,$callback_params=array(),$expiration=60*60*1); + if (isset($eMailListContainsDeletedMessages[$_profileID])) + { + unset($eMailListContainsDeletedMessages[$_profileID]); + egw_cache::setCache(egw_cache::INSTANCE,'email','eMailListContainsDeletedMessages'.trim($GLOBALS['egw_info']['user']['account_id']),$eMailListContainsDeletedMessages, $expiration=60*60*1); + } + + $nameSpace = egw_cache::getSession('email','defaultimap_nameSpace'); + if (isset($nameSpace[$_profileID])) + { + unset($nameSpace[$_profileID]); + egw_cache::setSession('email','defaultimap_nameSpace',$nameSpace); + } + if (isset(self::$instances[$_profileID])) unset(self::$instances[$_profileID]); + } + } + + /** + * resets the various cache objects where connection error Objects may be cached + * + * @param int $_ImapServerId the profileID to look for + */ + static function resetConnectionErrorCache($_ImapServerId=null) + { + //error_log(__METHOD__.' ('.__LINE__.') '.' for Profile:'.array2string($_ImapServerId) .' for user:'.trim($GLOBALS['egw_info']['user']['account_id'])); + $account_id = $GLOBALS['egw_info']['user']['account_id']; + if (is_array($_ImapServerId)) + { + // called via hook + $account_id = $_ImapServerId['account_id']; + unset($_ImapServerId); + $_ImapServerId = null; + } + if (is_null($_ImapServerId)) + { + $buff = array(); + $isConError = array(); + $waitOnFailure = array(); + } + else + { + $isConError = egw_cache::getCache(egw_cache::INSTANCE,'email','icServerSIEVE_connectionError'.trim($account_id)); + if (isset($isConError[$_ImapServerId])) + { + unset($isConError[$_ImapServerId]); + } + $waitOnFailure = egw_cache::getCache(egw_cache::INSTANCE,'email','ActiveSyncWaitOnFailure'.trim($GLOBALS['egw_info']['user']['account_id']),$callback=null,$callback_params=array(),$expiration=60*60*2); + if (isset($waitOnFailure[$_ImapServerId])) + { + unset($waitOnFailure[$_ImapServerId]); + } + } + egw_cache::setCache(egw_cache::INSTANCE,'email','icServerSIEVE_connectionError'.trim($account_id),$isConError,$expiration=60*15); + egw_cache::setCache(egw_cache::INSTANCE,'email','ActiveSyncWaitOnFailure'.trim($GLOBALS['egw_info']['user']['account_id']),$waitOnFailure,$expiration=60*60*2); + } + + /** + * resets the various cache objects where Folder Objects may be cached + * + * @param int $_ImapServerId the profileID to look for + */ + static function resetFolderObjectCache($_ImapServerId=null) + { + //error_log(__METHOD__.' ('.__LINE__.') '.' called for Profile:'.$_ImapServerId.'->'.function_backtrace()); + if (is_null($_ImapServerId)) + { + $folders2return = array(); + $folderInfo = array(); + } + else + { + $folders2return = egw_cache::getCache(egw_cache::INSTANCE,'email','folderObjects'.trim($GLOBALS['egw_info']['user']['account_id']),$callback=null,$callback_params=array(),$expiration=60*60*1); + if (isset($folders2return[$_ImapServerId])) + { + unset($folders2return[$_ImapServerId]); + } + $folderInfo = egw_cache::getCache(egw_cache::INSTANCE,'email','icServerFolderExistsInfo'.trim($GLOBALS['egw_info']['user']['account_id']),null,array(),$expiration=60*5); + if (isset($folderInfo[$_ImapServerId])) + { + unset($folderInfo[$_ImapServerId]); + } + /* + $lastFolderUsedForMove = egw_cache::getCache(egw_cache::INSTANCE,'email','lastFolderUsedForMove'.trim($GLOBALS['egw_info']['user']['account_id']),null,array(),$expiration=60*60*1); + if (isset($lastFolderUsedForMove[$_ImapServerId])) + { + unset($lastFolderUsedForMove[$_ImapServerId]); + } + */ + $folderBasicInfo = egw_cache::getCache(egw_cache::INSTANCE,'email','folderBasicInfo'.trim($GLOBALS['egw_info']['user']['account_id']),null,array(),$expiration=60*60*1); + if (isset($folderBasicInfo[$_ImapServerId])) + { + unset($folderBasicInfo[$_ImapServerId]); + } + $_specialUseFolders = egw_cache::getCache(egw_cache::INSTANCE,'email','specialUseFolders'.trim($GLOBALS['egw_info']['user']['account_id']),null,array(),$expiration=60*60*12); + if (isset($_specialUseFolders[$_ImapServerId])) + { + unset($_specialUseFolders[$_ImapServerId]); + self::$specialUseFolders=null; + } + } + egw_cache::setCache(egw_cache::INSTANCE,'email','folderObjects'.trim($GLOBALS['egw_info']['user']['account_id']),$folders2return, $expiration=60*60*1); + egw_cache::setCache(egw_cache::INSTANCE,'email','icServerFolderExistsInfo'.trim($GLOBALS['egw_info']['user']['account_id']),$folderInfo,$expiration=60*5); + //egw_cache::setCache(egw_cache::INSTANCE,'email','lastFolderUsedForMove'.trim($GLOBALS['egw_info']['user']['account_id']),$lastFolderUsedForMove,$expiration=60*60*1); + egw_cache::setCache(egw_cache::INSTANCE,'email','folderBasicInfo'.trim($GLOBALS['egw_info']['user']['account_id']),$folderBasicInfo,$expiration=60*60*1); + egw_cache::setCache(egw_cache::INSTANCE,'email','specialUseFolders'.trim($GLOBALS['egw_info']['user']['account_id']),$_specialUseFolders,$expiration=60*60*12); + } + + /** + * checks if the imap server supports a given capability + * + * @param string $_capability the name of the capability to check for + * @return bool + */ + function hasCapability($_capability) + { + $rv = $this->icServer->hasCapability(strtoupper($_capability)); + //error_log(__METHOD__.' ('.__LINE__.') '." $_capability:".array2string($rv)); + return $rv; + } + + /** + * getUserEMailAddresses - function to gather the emailadresses connected to the current mail-account + * @param string $_profileID the ID of the mailaccount to check for identities, if null current mail-account is used + * @return array - array(email=>realname) + */ + function getUserEMailAddresses($_profileID=null) { + $acc = emailadmin_account::read((!empty($_profileID)?$_profileID:$this->profileID)); + //error_log(__METHOD__.' ('.__LINE__.') '.':'.array2string($acc)); + $identities = $acc->identities(); + + $userEMailAdresses = array($acc['ident_email']=>$acc['ident_realname']); + + foreach($identities as $ik => $ident) { + //error_log(__METHOD__.' ('.__LINE__.') '.':'.$ik.'->'.array2string($ident)); + $identity = emailadmin_account::read_identity($ik); + if (!empty($identity['ident_email']) && !isset($userEMailAdresses[$identity['ident_email']])) $userEMailAdresses[$identity['ident_email']] = $identity['ident_realname']; + } + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($userEMailAdresses)); + return $userEMailAdresses; + } + + /** + * getAllIdentities - function to gather the identities connected to the current user + * @return array - array(email=>realname) + */ + static function getAllIdentities() { + $userEMailAdresses = array(); + foreach(emailadmin_account::search($only_current_user=true, $just_name=true) as $acc_id => $identity_name) + { + $acc = emailadmin_account::read($acc_id); + $userEMailAdresses[$acc['ident_id']] = array('acc_id'=>$acc_id,'ident_id'=>$acc['ident_id'],'ident_email'=>$acc['ident_email'],'ident_org'=>$acc['ident_org'],'ident_realname'=>$acc['ident_realname'],'ident_signature'=>$acc['ident_signature'],'ident_name'=>$acc['ident_name']); + $identities = $acc->identities($acc_id); + + foreach($identities as $ik => $ident) { + //error_log(__METHOD__.' ('.__LINE__.') '.':'.$ik.'->'.array2string($ident)); + $identity = emailadmin_account::read_identity($ik); + //error_log(__METHOD__.' ('.__LINE__.') '.':'.$ik.'->'.array2string($identity)); + if (!isset($userEMailAdresses[$identity['ident_id']])) $userEMailAdresses[$identity['ident_id']] = array('acc_id'=>$acc_id,'ident_id'=>$identity['ident_id'],'ident_email'=>$identity['ident_email'],'ident_org'=>$identity['ident_org'],'ident_realname'=>$identity['ident_realname'],'ident_signature'=>$identity['ident_signature'],'ident_name'=>$identity['ident_name']); + } + } + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($userEMailAdresses)); + return $userEMailAdresses; + } + + /** + * getAccountIdentities - function to gather the identities connected to the current mailaccount + * @param int $acc_id to pass all conected identities back + * @return array - array(email=>realname) + */ + function getAccountIdentities($acc_id) { + $userEMailAdresses = array(); + $acc = emailadmin_account::read($acc_id); + $userEMailAdresses[$acc['ident_id']] = array('acc_id'=>$acc_id,'ident_id'=>$acc['ident_id'],'ident_email'=>$acc['ident_email'],'ident_org'=>$acc['ident_org'],'ident_realname'=>$acc['ident_realname'],'ident_signature'=>$acc['ident_signature'],'ident_name'=>$acc['ident_name']); + $identities = $acc->identities(); + + foreach($identities as $ik => $ident) { + //error_log(__METHOD__.' ('.__LINE__.') '.':'.$ik.'->'.array2string($ident)); + $identity = emailadmin_account::read_identity($ik); + //error_log(__METHOD__.' ('.__LINE__.') '.':'.$ik.'->'.array2string($identity)); + if (!isset($userEMailAdresses[$identity['ident_id']])) $userEMailAdresses[$identity['ident_id']] = array('ident_id'=>$identity['ident_id'],'ident_email'=>$identity['ident_email'],'ident_org'=>$identity['ident_org'],'ident_realname'=>$identity['ident_realname'],'ident_signature'=>$identity['ident_signature'],'ident_name'=>$identity['ident_name']); + } + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($userEMailAdresses)); + return $userEMailAdresses; + } + + /** + * getDefaultIdentity - function to gather the default identitiy connected to the current mailaccount + * @return int - id of the identity + */ + function getDefaultIdentity() { + // retrieve the signature accociated with the identity + $id = $this->getIdentitiesWithAccounts($_accountData); + $acc = emailadmin_account::read($this->profileID); + $accountDataIT = ($_accountData[$this->profileID]?$acc->identities($this->profileID,false,'ident_id'):$acc->identities($_accountData[$id],false,'ident_id')); + foreach($accountDataIT as $it => $accountData) + { + return $accountData; + } + } + + /** + * getIdentitiesWithAccounts + * + * @param array reference to pass all identities back + * @return the default Identity (active) or 0 + */ + function getIdentitiesWithAccounts(&$identities) + { + // account select box + $selectedID = $this->profileID; + $allAccountData = emailadmin_account::search($only_current_user=true, $just_name=false, $order_by=null); + if ($allAccountData) { + $rememberFirst=$selectedFound=null; + foreach ($allAccountData as $tmpkey => $icServers) + { + if (is_null($rememberFirst)) $rememberFirst = $tmpkey; + if ($tmpkey == $selectedID) $selectedFound=true; + //error_log(__METHOD__.' ('.__LINE__.') '.' Key:'.$tmpkey.'->'.array2string($icServers->acc_imap_host)); + $host = $icServers->acc_imap_host; + if (empty($host)) continue; + $identities[$icServers->acc_id] = $icServers['ident_realname'].' '.$icServers['ident_org'].' <'.$icServers['ident_email'].'>'; + //error_log(__METHOD__.' ('.__LINE__.') '.' Key:'.$tmpkey.'->'.array2string($identities[$icServers->acc_id])); + } + } + return ($selectedFound?$selectedID:$rememberFirst); + } + + /** + * generateIdentityString + * construct the string representing an Identity passed by $identity + * @var array/object $identity, identity object that holds realname, organization, emailaddress and signatureid + * @var boolean $fullString full or false=NamePart only is returned + * @return string - constructed of identity object data as defined in mailConfig + */ + static function generateIdentityString($identity, $fullString=true) + { + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($identity)); + if (is_null(self::$mailConfig)) self::$mailConfig = config::read('mail'); + // not set? -> use default, means full display of all available data + if (!isset(self::$mailConfig['how2displayIdentities'])) self::$mailConfig['how2displayIdentities']=''; + switch (self::$mailConfig['how2displayIdentities']) + { + case 'email'; + //$retData = str_replace('@',' ',$identity->emailAddress).($fullString===true?' <'.$identity->emailAddress.'>':''); + $retData = $identity['ident_email'].($fullString===true?' <'.$identity['ident_email'].'>':''); + break; + case 'nameNemail'; + $retData = (!empty($identity['ident_realname'])?$identity['ident_realname']:substr_replace($identity['ident_email'],'',strpos($identity['ident_email'],'@'))).($fullString===true?' <'.$identity['ident_email'].'>':''); + break; + case 'orgNemail'; + $retData = (!empty($identity['ident_org'])?$identity['ident_org']:substr_replace($identity['ident_email'],'',0,strpos($identity['ident_email'],'@')+1)).($fullString===true?' <'.$identity['ident_email'].'>':''); + break; + default: + $retData = $identity['ident_realname'].(!empty($identity['ident_org'])?' '.$identity['ident_org']:'').($fullString===true?' <'.$identity['ident_email'].'>':''); + } + return $retData; + } + + /** + * closes a connection on the active Server ($this->icServer) + * + * @return void + */ + function closeConnection() { + //if ($icServer->_connected) error_log(__METHOD__.' ('.__LINE__.') '.' disconnect from Server'); + //error_log(__METHOD__."() ".function_backtrace()); + $this->icServer->disconnect(); + } + + /** + * reopens a connection for the active Server ($this->icServer), and selects the folder given + * + * @param string $_foldername, folder to open/select + * @return void + */ + function reopen($_foldername) + { + if (self::$debugTimes) $starttime = microtime (true); + + //error_log(__METHOD__.' ('.__LINE__.') '."('$_foldername') ".function_backtrace()); + // TODO: trying to reduce traffic to the IMAP Server here, introduces problems with fetching the bodies of + // eMails when not in "current-Folder" (folder that is selected by UI) + static $folderOpened; + //if (empty($folderOpened) || $folderOpened!=$_foldername) + //{ + //error_log( __METHOD__.' ('.__LINE__.') '." $_foldername ".function_backtrace()); + //error_log(__METHOD__.' ('.__LINE__.') '.' Connected with icServer for Profile:'.$this->profileID.'?'.print_r($this->icServer->_connected,true)); + if ($this->folderIsSelectable($_foldername)) { + $tretval = $this->icServer->openMailbox($_foldername); + } + $folderOpened = $_foldername; + //} + if (self::$debugTimes) self::logRunTimes($starttime,null,'Folder:'.$_foldername,__METHOD__.' ('.__LINE__.') '); + } + + + /** + * openConnection + * + * @param int $_icServerID + * @param boolean $_adminConnection + * @throws Horde_Imap_Client_Exception on connection error or authentication failure + * @throws InvalidArgumentException on missing credentials + */ + function openConnection($_icServerID=0, $_adminConnection=false) + { + //error_log( "-------------------------->open connection ".function_backtrace()); + //error_log(__METHOD__.' ('.__LINE__.') '.' ->'.array2string($this->icServer)); + if (self::$debugTimes) $starttime = microtime (true); + $mailbox=null; + try + { + if($this->folderExists($this->sessionData['mailbox'])) $mailbox = $this->sessionData['mailbox']; + if (empty($mailbox)) $mailbox = $this->icServer->getCurrentMailbox(); +/* + if (isset(emailadmin_imap::$supports_keywords[$_icServerID])) + { + $this->icServer->openMailbox($mailbox); + } + else + { + $this->icServer->examineMailbox($mailbox); + } +*/ + // the above should detect if there is a known information about supporting KEYWORDS + // but does not work as expected :-( + $this->icServer->examineMailbox($mailbox); + //error_log(__METHOD__." using existing Connection ProfileID:".$_icServerID.' Status:'.print_r($this->icServer->_connected,true)); + //error_log(__METHOD__.' ('.__LINE__.') '."->open connection for Server with profileID:".$_icServerID.function_backtrace()); + + //make sure we are working with the correct hierarchyDelimiter on the current connection, calling getHierarchyDelimiter with false to reset the cache + $hD = $this->getHierarchyDelimiter(false); + self::$specialUseFolders = $this->getSpecialUseFolders(); + } + catch (Exception $e) + { + error_log(__METHOD__.' ('.__LINE__.') '."->open connection for Server with profileID:".$_icServerID." failed!".$e->getMessage()); + throw new egw_exception(__METHOD__." failed to ".__METHOD__." on Profile to $_icServerID :".$e->getMessage()); + } + if (self::$debugTimes) self::logRunTimes($starttime,null,'ProfileID:'.$_icServerID,__METHOD__.' ('.__LINE__.') '); + } + + /** + * getQuotaRoot + * return the qouta of the users INBOX + * + * @return mixed array/boolean + */ + function getQuotaRoot() + { + static $quota; + if (isset($quota)) return $quota; + if(!$this->icServer->hasCapability('QUOTA')) { + $quota = false; + return false; + } + $quota = $this->icServer->getStorageQuotaRoot('INBOX'); + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($quota)); + if(is_array($quota)) { + $quota = array( + 'usage' => $quota['USED'], + 'limit' => $quota['QMAX'], + ); + } else { + $quota = false; + } + return $quota; + } + + /** + * getTimeOut + * + * @param string _use decide if the use is for IMAP or SIEVE, by now only the default differs + * + * @return int - timeout (either set or default 20/10) + */ + static function getTimeOut($_use='IMAP') + { + $timeout = $GLOBALS['egw_info']['user']['preferences']['mail']['connectionTimeout']; + if (empty($timeout)) $timeout = ($_use=='SIEVE'?10:20); // this is the default value + return $timeout; + } + + /** + * _getNameSpaces, fetch the namespace from icServer + * Note: a IMAPServer may present several namespaces under each key; + * so we return an array of namespacearrays for our needs + * @return array array(prefix_present=>mixed (bool/string) ,prefix=>string,delimiter=>string,type=>string (personal|others|shared)) + */ + function _getNameSpaces() + { + static $nameSpace; + $foldersNameSpace = array(); + $delimiter = $this->getHierarchyDelimiter(); + // TODO: cache by $this->icServer->ImapServerId + if (is_null($nameSpace)) $nameSpace = $this->icServer->getNameSpaceArray(); + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($nameSpace)); + if (is_array($nameSpace)) { + foreach($nameSpace as $type => $singleNameSpaceArray) + { + foreach ($singleNameSpaceArray as $k => $singleNameSpace) + { + $prefix_present = false; + $_foldersNameSpace = array(); + if($type == 'personal' && $singleNameSpace['name'] == '#mh/' && ($this->folderExists('Mail')||$this->folderExists('INBOX'))) + { + $_foldersNameSpace['prefix_present'] = 'forced'; + // uw-imap server with mailbox prefix or dovecot maybe + $_foldersNameSpace['prefix'] = ($this->folderExists('Mail')?'Mail':(!empty($singleNameSpace['name'])?$singleNameSpace['name']:'')); + } + elseif($type == 'personal' && ($singleNameSpace['name'] == '#mh/') && $this->folderExists('mail')) + { + $_foldersNameSpace['prefix_present'] = 'forced'; + // uw-imap server with mailbox prefix or dovecot maybe + $_foldersNameSpace['prefix'] = 'mail'; + } else { + $_foldersNameSpace['prefix_present'] = !empty($singleNameSpace['name']); + $_foldersNameSpace['prefix'] = $singleNameSpace['name']; + } + $_foldersNameSpace['delimiter'] = ($singleNameSpace['delimiter']?$singleNameSpace['delimiter']:$delimiter); + $_foldersNameSpace['type'] = $type; + $foldersNameSpace[] =$_foldersNameSpace; + } + //echo "############## $type->".print_r($foldersNameSpace[$type],true)." ###################
"; + } + } + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($foldersNameSpace)); + return $foldersNameSpace; + } + + /** + * getFolderPrefixFromNamespace, wrapper to extract the folder prefix from folder compared to given namespace array + * @var array $nameSpace + * @var string $_folderName + * @return string the prefix (may be an empty string) + */ + function getFolderPrefixFromNamespace($nameSpace, $folderName) + { + foreach($nameSpace as $k => $singleNameSpace) + { + //if (substr($singleNameSpace['prefix'],0,strlen($folderName))==$folderName) return $singleNameSpace['prefix']; + if (substr($folderName,0,strlen($singleNameSpace['prefix']))==$singleNameSpace['prefix']) return $singleNameSpace['prefix']; + } + return ""; + } + + /** + * getHierarchyDelimiter + * @var boolean $_useCache + * @return string the hierarchyDelimiter + */ + function getHierarchyDelimiter($_useCache=true) + { + static $HierarchyDelimiter; + if (is_null($HierarchyDelimiter)) $HierarchyDelimiter =& egw_cache::getSession('mail','HierarchyDelimiter'); + if ($_useCache===false) unset($HierarchyDelimiter[$this->icServer->ImapServerId]); + if (isset($HierarchyDelimiter[$this->icServer->ImapServerId])&&!empty($HierarchyDelimiter[$this->icServer->ImapServerId])) + { + return $HierarchyDelimiter[$this->icServer->ImapServerId]; + } + $HierarchyDelimiter[$this->icServer->ImapServerId] = '/'; + try + { + $HierarchyDelimiter[$this->icServer->ImapServerId] = $this->icServer->getDelimiter(); + } + catch(Exception $e) + { + $HierarchyDelimiter[$this->icServer->ImapServerId] = '/'; + } + return $HierarchyDelimiter[$this->icServer->ImapServerId]; + } + + /** + * getSpecialUseFolders + * @ToDo: could as well be static, when icServer is passed + * @return mixed null/array + */ + function getSpecialUseFolders() + { + //error_log(__METHOD__.' ('.__LINE__.') '.':'.$this->icServer->ImapServerId.' Connected:'.$this->icServer->_connected); + static $_specialUseFolders; + if (is_null($_specialUseFolders)||empty($_specialUseFolders)) $_specialUseFolders = egw_cache::getCache(egw_cache::INSTANCE,'email','specialUseFolders'.trim($GLOBALS['egw_info']['user']['account_id']),$callback=null,$callback_params=array(),$expiration=60*60*24*5); + if (isset($_specialUseFolders[$this->icServer->ImapServerId]) &&!empty($_specialUseFolders[$this->icServer->ImapServerId])) + { + if(($this->icServer instanceof defaultimap)) + { + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($specialUseFolders[$this->icServer->ImapServerId])); + // array('Drafts', 'Templates', 'Sent', 'Trash', 'Junk', 'Outbox'); + if (empty($this->icServer->acc_folder_trash) && ($f = array_search('Trash',(array)$_specialUseFolders[$this->icServer->ImapServerId]))) $this->icServer->acc_folder_trash = $f; + if (empty($this->icServer->acc_folder_draft) && ($f = array_search('Drafts',(array)$_specialUseFolders[$this->icServer->ImapServerId]))) $this->icServer->acc_folder_draft = $f; + if (empty($this->icServer->acc_folder_sent) && ($f = array_search('Sent',(array)$_specialUseFolders[$this->icServer->ImapServerId]))) $this->icServer->acc_folder_sent = $f; + if (empty($this->icServer->acc_folder_template) && ($f = array_search('Templates',(array)$_specialUseFolders[$this->icServer->ImapServerId]))) $this->icServer->acc_folder_template = $f; + } + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($_specialUseFolders[$this->icServer->ImapServerId])); + self::$specialUseFolders = $_specialUseFolders[$this->icServer->ImapServerId]; // make sure this one is set on function call + return $_specialUseFolders[$this->icServer->ImapServerId]; + } + if(($this->icServer instanceof defaultimap) ) + { + //error_log(__METHOD__.' ('.__LINE__.') '); + if(($this->hasCapability('SPECIAL-USE'))) + { + //error_log(__METHOD__.' ('.__LINE__.') '); + try + { + // do not query IMAP Server for SPECIAL-USE Folders, as we assume thev wizard already did that + $ret = null;//$this->icServer->getSpecialUseFolders(); + } catch (Exception $e) + { + $ret=null; + } + if (empty($ret)) + { + $_specialUseFolders[$this->icServer->ImapServerId]=array(); + if (!empty($this->icServer->acc_folder_trash)) $_specialUseFolders[$this->icServer->ImapServerId][$this->icServer->acc_folder_trash]='Trash'; + if (!empty($this->icServer->acc_folder_draft)) $_specialUseFolders[$this->icServer->ImapServerId][$this->icServer->acc_folder_draft]='Drafts'; + if (!empty($this->icServer->acc_folder_sent)) $_specialUseFolders[$this->icServer->ImapServerId][$this->icServer->acc_folder_sent]='Sent'; + if (!empty($this->icServer->acc_folder_template)) $_specialUseFolders[$this->icServer->ImapServerId][$this->icServer->acc_folder_template]='Templates'; + } + else + { + foreach ($ret as $k => $f) + { + if (isset($f['ATTRIBUTES']) && !empty($f['ATTRIBUTES']) && + !in_array('\\nonexistent',$f['ATTRIBUTES'])) + { + foreach (self::$autoFolders as $i => $n) // array('Drafts', 'Templates', 'Sent', 'Trash', 'Junk', 'Outbox'); + { + if (in_array('\\'.strtolower($n),$f['ATTRIBUTES'])) $_specialUseFolders[$this->icServer->ImapServerId][$f['MAILBOX']] = $n; + } + } + } + } + egw_cache::setCache(egw_cache::INSTANCE,'email','specialUseFolders'.trim($GLOBALS['egw_info']['user']['account_id']),$_specialUseFolders, $expiration=60*60*24*5); + } + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($_specialUseFolders[$this->icServer->ImapServerId])); + if (empty($this->icServer->acc_folder_trash) && ($f = array_search('Trash',(array)$_specialUseFolders[$this->icServer->ImapServerId]))) $this->icServer->acc_folder_trash = $f; + if (empty($this->icServer->acc_folder_draft) && ($f = array_search('Drafts',(array)$_specialUseFolders[$this->icServer->ImapServerId]))) $this->icServer->acc_folder_draft = $f; + if (empty($this->icServer->acc_folder_sent) && ($f = array_search('Sent',(array)$_specialUseFolders[$this->icServer->ImapServerId]))) $this->icServer->acc_folder_sent = $f; + if (empty($this->icServer->acc_folder_template) && ($f = array_search('Templates',(array)$_specialUseFolders[$this->icServer->ImapServerId]))) $this->icServer->acc_folder_template = $f; + } + self::$specialUseFolders = $_specialUseFolders[$this->icServer->ImapServerId]; // make sure this one is set on function call + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($_specialUseFolders[$this->icServer->ImapServerId])); + return $_specialUseFolders[$this->icServer->ImapServerId]; + } + + /** + * get IMAP folder status regarding NoSelect + * + * returns true or false regarding the noselect attribute + * + * @param foldertoselect string the foldername + * + * @return boolean + */ + function folderIsSelectable($folderToSelect) + { + $retval = true; + if($folderToSelect && ($folderStatus = $this->getFolderStatus($folderToSelect,false,true))) { + if (!empty($folderStatus['attributes']) && stripos(array2string($folderStatus['attributes']),'noselect')!==false) + { + $retval = false; + } + } + return $retval; + } + + /** + * get IMAP folder status, wrapper to store results within a single request + * + * returns an array information about the imap folder + * + * @param folderName string the foldername + * @param ignoreStatusCache bool ignore the cache used for counters + * + * @return array + */ + function _getStatus($folderName,$ignoreStatusCache=false) + { + static $folderStatus; + if (!$ignoreStatusCache && isset($folderStatus[$this->icServer->ImapServerId][$folderName])) + { + //error_log(__METHOD__.' ('.__LINE__.') '.' Using cache for status on Server:'.$this->icServer->ImapServerId.' for folder:'.$folderName.'->'.array2string($folderStatus[$this->icServer->ImapServerId][$folderName])); + return $folderStatus[$this->icServer->ImapServerId][$folderName]; + } + try + { + $folderStatus[$this->icServer->ImapServerId][$folderName] = $this->icServer->getStatus($folderName); + } + catch (Exception $e) + { + throw new egw_exception(__METHOD__.' ('.__LINE__.') '." failed for $folderName with error:".$e->getMessage()); + } + return $folderStatus[$this->icServer->ImapServerId][$folderName]; + } + + /** + * get IMAP folder status + * + * returns an array information about the imap folder, may be used as wrapper to retrieve results from cache + * + * @param _folderName string the foldername + * @param ignoreStatusCache bool ignore the cache used for counters + * @param basicInfoOnly bool retrieve only names and stuff returned by getMailboxes + * + * @return array + */ + function getFolderStatus($_folderName,$ignoreStatusCache=false,$basicInfoOnly=false) + { + if (self::$debug) error_log(__METHOD__.' ('.__LINE__.') '." called with:$_folderName,$ignoreStatusCache,$basicInfoOnly"); + if (!is_string($_folderName) || empty($_folderName)) // something is wrong. Do not proceed + { + return false; + } + static $folderInfoCache; // reduce traffic on single request + static $folderBasicInfo; + if (is_null($folderBasicInfo)) + { + $folderBasicInfo = egw_cache::getCache(egw_cache::INSTANCE,'email','folderBasicInfo'.trim($GLOBALS['egw_info']['user']['account_id']),null,array(),$expiration=60*60*1); + $folderInfoCache = $folderBasicInfo[$this->profileID]; + } + if (isset($folderInfoCache[$_folderName]) && $ignoreStatusCache==false && $basicInfoOnly) return $folderInfoCache[$_folderName]; + $retValue = array(); + $retValue['subscribed'] = false; +/* + if(!$icServer = emailadmin_account::read($this->profileID)) { + if (self::$debug) error_log(__METHOD__." no Server found for Folder:".$_folderName); + return false; + } +*/ + // does the folder exist??? + if (is_null($folderInfoCache) || !isset($folderInfoCache[$_folderName])) + { + $ret = $this->icServer->getMailboxes('', $_folderName, true); + //error_log(__METHOD__.' ('.__LINE__.') '.$_folderName.' '.array2string($ret)); + if (is_array($ret)) + { + $retkeys = array_keys($ret); + if ($retkeys[0]==$_folderName) $folderInfoCache[$_folderName] = $ret[$retkeys[0]]; + } + else + { + $folderInfoCache[$_folderName]=false; + } + } + $folderInfo = $folderInfoCache[$_folderName]; + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($folderInfo).'->'.function_backtrace()); + if(!$folderInfo|| !is_array($folderInfo)) { + try + { + $folderInfo = $this->_getStatus($_folderName); + } + catch (Exception $e) + { + $folderInfo=null; + } + if (!is_array($folderInfo)) + { + // no folder info, but there is a status returned for the folder: something is wrong, try to cope with it + $folderInfo = is_array($folderInfo)?$folderInfo:array('HIERACHY_DELIMITER'=>$this->getHierarchyDelimiter(), + 'ATTRIBUTES' => ''); + if (empty($folderInfo['HIERACHY_DELIMITER']) || (isset($folderInfo['delimiter']) && empty($folderInfo['delimiter']))) + { + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($folderInfo)); + $folderInfo['HIERACHY_DELIMITER'] = $this->getHierarchyDelimiter(); + } + } + } + #if(!is_array($folderInfo)) { + # return false; + #} + $retValue['delimiter'] = ($folderInfo['HIERACHY_DELIMITER']?$folderInfo['HIERACHY_DELIMITER']:$folderInfo['delimiter']); + $retValue['attributes'] = ($folderInfo['ATTRIBUTES']?$folderInfo['ATTRIBUTES']:$folderInfo['attributes']); + $shortNameParts = explode($retValue['delimiter'], $_folderName); + $retValue['shortName'] = array_pop($shortNameParts); + $retValue['displayName'] = $_folderName; + $retValue['shortDisplayName'] = $retValue['shortName']; + if(strtoupper($retValue['shortName']) == 'INBOX') { + $retValue['displayName'] = lang('INBOX'); + $retValue['shortDisplayName'] = lang('INBOX'); + } + // translate the automatic Folders (Sent, Drafts, ...) like the INBOX + elseif (in_array($retValue['shortName'],self::$autoFolders)) + { + $retValue['displayName'] = $retValue['shortDisplayName'] = lang($retValue['shortName']); + } + if ($folderInfo) $folderBasicInfo[$this->profileID][$_folderName]=$retValue; + egw_cache::setCache(egw_cache::INSTANCE,'email','folderBasicInfo'.trim($GLOBALS['egw_info']['user']['account_id']),$folderBasicInfo,$expiration=60*60*1); + //error_log(__METHOD__.' ('.__LINE__.') '.' '.$_folderName.array2string($retValue['attributes'])); + if ($basicInfoOnly || (isset($retValue['attributes']) && stripos(array2string($retValue['attributes']),'noselect')!==false)) + { + return $retValue; + } + // fetch all in one go for one request, instead of querying them one by one + // this should reduce communication to the imap server + static $subscribedFolders; + static $nameSpace; + static $prefix; + if (is_null($nameSpace) || empty($nameSpace[$this->profileID])) $nameSpace[$this->profileID] = $this->_getNameSpaces(); + if (!empty($nameSpace[$this->profileID])) + { + $nsNoPersonal=array(); + foreach($nameSpace[$this->profileID] as $k => $ns) + { + if ($ns['type']!='personal') $nsNoPersonal[]=$ns; + } + $nameSpace[$this->profileID]=$nsNoPersonal; + } + if (is_null($prefix) || empty($prefix[$this->profileID]) || empty($prefix[$this->profileID][$_folderName])) $prefix[$this->profileID][$_folderName] = $this->getFolderPrefixFromNamespace($nameSpace[$this->profileID], $_folderName); + + //$subscribedFolders[$this->profileID] = $this->icServer->listSubscribedMailboxes('', $_folderName); + if (is_null($subscribedFolders) || empty($subscribedFolders[$this->profileID])) $subscribedFolders[$this->profileID] = $this->icServer->listSubscribedMailboxes(); + + if(is_array($subscribedFolders[$this->profileID]) && in_array($_folderName,$subscribedFolders[$this->profileID])) { + $retValue['subscribed'] = true; + } + + try + { + $folderStatus = $this->_getStatus($_folderName,$ignoreStatusCache); + $retValue['messages'] = $folderStatus['MESSAGES']; + $retValue['recent'] = $folderStatus['RECENT']; + $retValue['uidnext'] = $folderStatus['UIDNEXT']; + $retValue['uidvalidity'] = $folderStatus['UIDVALIDITY']; + $retValue['unseen'] = $folderStatus['UNSEEN']; + if (//$retValue['unseen']==0 && + (isset($this->mailPreferences['trustServersUnseenInfo']) && // some servers dont serve the UNSEEN information + $this->mailPreferences['trustServersUnseenInfo']==false) || + (isset($this->mailPreferences['trustServersUnseenInfo']) && + $this->mailPreferences['trustServersUnseenInfo']==2 && + $prefix[$this->profileID][$_folderName] != '' && stripos($_folderName,$prefix[$this->profileID][$_folderName]) !== false) + ) + { + //error_log(__METHOD__." returned folderStatus for Folder $_folderName:".print_r($prefix,true).' TS:'.$this->mailPreferences['trustServersUnseenInfo']); + // we filter for the combined status of unseen and undeleted, as this is what we show in list + try + { + $sortResult = $this->getSortedList($_folderName, $_sort=0, $_reverse=1, $_filter=array('status'=>array('UNSEEN','UNDELETED')),$byUid=true,false); + $retValue['unseen'] = $sortResult['count']; + } + catch (Exception $ee) + { + if (self::$debug) error_log(__METHOD__." could not fetch/calculate unseen counter for $_folderName Reason:'".$ee->getMessage()."' but requested."); + } + } + } + catch (Exception $e) + { + if (self::$debug) error_log(__METHOD__." returned folderStatus for Folder $_folderName:".print_r($e->getMessage(),true)); + } + + return $retValue; + } + + /** + * getHeaders + * + * this function is a wrapper function for getSortedList and populates the resultList thereof with headerdata + * + * @param string $_folderName + * @param int $_startMessage + * @param int $_numberOfMessages number of messages to return + * @param array $_sort sort by criteria + * @param boolean $_reverse reverse sorting of the result array (may be switched, as it is passed to getSortedList by reference) + * @param array $_filter filter to apply to getSortedList + * @param mixed $_thisUIDOnly=null, if given fetch the headers of this uid only (either one, or array of uids) + * @param boolean $_cacheResult=true try touse the cache of getSortedList + * @return array result as array(header=>array,total=>int,first=>int,last=>int) + */ + function getHeaders($_folderName, $_startMessage, $_numberOfMessages, $_sort, $_reverse, $_filter, $_thisUIDOnly=null, $_cacheResult=true) + { + //self::$debug=true; + if (self::$debug) error_log(__METHOD__.' ('.__LINE__.') '.function_backtrace()); + if (self::$debug) error_log(__METHOD__.' ('.__LINE__.') '."$_folderName,$_startMessage, $_numberOfMessages, $_sort, $_reverse, ".array2string($_filter).", $_thisUIDOnly"); + $reverse = (bool)$_reverse; + // get the list of messages to fetch + $this->reopen($_folderName); + //$currentFolder = $this->icServer->getCurrentMailbox(); + //if ($currentFolder != $_folderName); $this->icServer->openMailbox($_folderName); + $rByUid = true; // try searching by uid. this var will be passed by reference to getSortedList, and may be set to false, if UID retrieval fails + #print "
";
+		#$this->icServer->setDebug(true);
+		if ($_thisUIDOnly === null)
+		{
+			if (($_startMessage || $_numberOfMessages) && !isset($_filter['range']))
+			{
+				// this will not work we must calculate the range we want to retieve as e.g.: 0:20 retirieves the first 20 mails and sorts them
+				// if sort capability is applied to the range fetched, not sort first and fetch the range afterwards
+				$start = $_startMessage-1;
+				$end = $_startMessage-1+$_numberOfMessages;
+				//$_filter['range'] ="$start:$end";
+				//$_filter['range'] ="$_startMessage:*";
+			}
+			if (self::$debug) error_log(__METHOD__.' ('.__LINE__.') '."$_folderName, $_sort, $reverse, ".array2string($_filter).", $rByUid");
+			if (self::$debug||self::$debugTimes) $starttime = microtime (true);
+			//see this example below for a 12 week datefilter (since)
+			//$_filter = array('status'=>array('UNDELETED'),'type'=>"SINCE",'string'=> date("d-M-Y", $starttime-(3600*24*7*12)));
+			$_sortResult = $this->getSortedList($_folderName, $_sort, $reverse, $_filter, $rByUid, $_cacheResult);
+			$sortResult = $_sortResult['match']->ids;
+			//$modseq = $_sortResult['modseq'];
+			//error_log(__METHOD__.' ('.__LINE__.') '.'Modsequence:'.$modseq);
+			if (self::$debug||self::$debugTimes) self::logRunTimes($starttime,null,' call getSortedList for Folder:'.$_folderName.' Filter:'.array2string($_filter).' Ids:'.array2string($_thisUIDOnly),__METHOD__.' ('.__LINE__.') ');
+
+			if (self::$debug) error_log(__METHOD__.' ('.__LINE__.') '.array2string($sortResult));
+			#$this->icServer->setDebug(false);
+			#print "
"; + // nothing found + if(!is_array($sortResult) || empty($sortResult)) { + $retValue = array(); + $retValue['info']['total'] = 0; + $retValue['info']['first'] = 0; + $retValue['info']['last'] = 0; + return $retValue; + } + + $total = $_sortResult['count']; + #_debug_array($sortResult); + #_debug_array(array_slice($sortResult, -5, -2)); + //error_log("REVERSE: $reverse"); + if($reverse === true) { + if ($_startMessage<=$total) + { + $startMessage = $_startMessage-1; + } + else + { + //error_log(__METHOD__.' ('.__LINE__.') '.' Start:'.$_startMessage.' NumberOfMessages:'.$_numberOfMessages.' Total:'.$total); + if ($_startMessage+$_numberOfMessages>$total) + { + $numberOfMessages = $total%$_numberOfMessages; + //$numberOfMessages = abs($_startMessage-$total-1); + if ($numberOfMessages>0 && $numberOfMessages<=$_numberOfMessages) $_numberOfMessages = $numberOfMessages; + //error_log(__METHOD__.' ('.__LINE__.') '.' Start:'.$_startMessage.' NumberOfMessages:'.$_numberOfMessages.' Total:'.$total); + } + $startMessage=($total-$_numberOfMessages)-1; + //$retValue['info']['first'] = $startMessage; + //$retValue['info']['last'] = $total; + + } + if ($startMessage+$_numberOfMessages>$total) + { + $_numberOfMessages = $_numberOfMessages-($total-($startMessage+$_numberOfMessages)); + //$retValue['info']['first'] = $startMessage; + //$retValue['info']['last'] = $total; + } + if($startMessage > 0) { + if (self::$debug) error_log(__METHOD__.' ('.__LINE__.') '.' StartMessage:'.(-($_numberOfMessages+$startMessage)).', '.-$startMessage.' Number of Messages:'.count($sortResult)); + $sortResult = array_slice($sortResult, -($_numberOfMessages+$startMessage), -$startMessage); + } else { + if (self::$debug) error_log(__METHOD__.' ('.__LINE__.') '.' StartMessage:'.(-($_numberOfMessages+($_startMessage-1))).', AllTheRest, Number of Messages:'.count($sortResult)); + $sortResult = array_slice($sortResult, -($_numberOfMessages+($_startMessage-1))); + } + $sortResult = array_reverse($sortResult); + } else { + if (self::$debug) error_log(__METHOD__.' ('.__LINE__.') '.' StartMessage:'.($_startMessage-1).', '.$_numberOfMessages.' Number of Messages:'.count($sortResult)); + $sortResult = array_slice($sortResult, $_startMessage-1, $_numberOfMessages); + } + if (self::$debug) error_log(__METHOD__.' ('.__LINE__.') '.array2string($sortResult)); + } + else + { + $sortResult = (is_array($_thisUIDOnly) ? $_thisUIDOnly:(array)$_thisUIDOnly); + } + + //$queryString = implode(',', $sortResult); + // fetch the data for the selected messages + if (self::$debug||self::$debugTimes) $starttime = microtime(true); + try + { + $uidsToFetch = new Horde_Imap_Client_Ids(); + $uidsToFetch->add($sortResult); + + $fquery = new Horde_Imap_Client_Fetch_Query(); + // as we need the prio, we are not using the Envelope (which is not providing it) + // fetching both headers and envelope takes too much time + $fquery->headerText(array('peek'=>true)); // needed for getHeaderText; needed for X-Priority + //$fquery->envelope(); + $fquery->size(); + $fquery->structure(); + $fquery->flags(); + //$fquery->imapDate(); + $headersNew = $this->icServer->fetch($_folderName, $fquery, array( + 'ids' => $uidsToFetch, + )); + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($headersNew->ids())); + } + catch (Exception $e) + { + $headersNew = array(); + $sortResult = array(); + } + if (self::$debug||self::$debugTimes) + { + self::logRunTimes($starttime,null,'HordeFetch: for Folder:'.$_folderName.' Filter:'.array2string($_filter),__METHOD__.' ('.__LINE__.') '); + if (self::$debug) error_log(__METHOD__.' ('.__LINE__.') '.' Query:'.$queryString.' Result:'.array2string($headersNew)); + } + + $count = 0; + + foreach((array)$sortResult as $uid) { + $sortOrder[$uid] = $count++; + } + + $count = 0; + if (is_object($headersNew)) { + if (self::$debug||self::$debugTimes) $starttime = microtime(true); + foreach($headersNew->ids() as $id) { + $_headerObject = $headersNew->get($id); + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($_headerObject)); + $headerObject = array(); + $uid = $headerObject['UID']= ($_headerObject->getUid()?$_headerObject->getUid():$id); + $headerObject['MSG_NUM'] = $_headerObject->getSeq(); + $headerObject['SIZE'] = $_headerObject->getSize(); + $headerObject['INTERNALDATE'] = $_headerObject->getImapDate(); + // as we need the prio, we are not using the Envelope (which is not providing it) + // fetching both headers and envelope takes too much time + $headerForPrio = array_change_key_case($_headerObject->getHeaderText(0,Horde_Imap_Client_Data_Fetch::HEADER_PARSE)->toArray(), CASE_UPPER); + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($headerForPrio)); + if ( isset($headerForPrio['DISPOSITION-NOTIFICATION-TO']) ) { + $headerObject['DISPOSITION-NOTIFICATION-TO'] = self::decode_header(trim($headerForPrio['DISPOSITION-NOTIFICATION-TO'])); + } else if ( isset($headerForPrio['RETURN-RECEIPT-TO']) ) { + $headerObject['DISPOSITION-NOTIFICATION-TO'] = self::decode_header(trim($headerForPrio['RETURN-RECEIPT-TO'])); + } else if ( isset($headerForPrio['X-CONFIRM-READING-TO']) ) { + $headerObject['DISPOSITION-NOTIFICATION-TO'] = self::decode_header(trim($headerForPrio['X-CONFIRM-READING-TO'])); + } /*else $sent_not = "";*/ + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($headerObject)); + $headerObject['DATE'] = $headerForPrio['DATE']; + $headerObject['SUBJECT'] = (is_array($headerForPrio['SUBJECT'])?$headerForPrio['SUBJECT'][0]:$headerForPrio['SUBJECT']); + $headerObject['FROM'] = (array)($headerForPrio['FROM']?$headerForPrio['FROM']:($headerForPrio['REPLY-TO']?$headerForPrio['REPLY-TO']:$headerForPrio['RETURN-PATH'])); + $headerObject['TO'] = (array)$headerForPrio['TO']; + $headerObject['CC'] = (array)$headerForPrio['CC']; + $headerObject['PRIORITY'] = $headerForPrio['X-PRIORITY']; + foreach (array('FROM','TO','CC') as $_k => $key) + { + $address = array(); + foreach ($headerObject[$key] as $k => $ad) + { + if (stripos($ad,'@')===false) + { + $remember=$k; + } + else + { + $address[] = (!is_null($remember)?$headerObject[$key][$remember].' ':'').$ad; + $remember=null; + } + } + $headerObject[$key] = $address; + + } + $headerObject['FLAGS'] = $_headerObject->getFlags(); + $mailStructureObject = $_headerObject->getStructure(); + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($headerObject)); + //error_log(__METHOD__.' ('.__LINE__.') '.' MimeMap:'.array2string($mailStructureObject->contentTypeMap())); + //foreach ($_headerObject->getStructure()->getParts() as $p => $part) + $headerObject['ATTACHMENTS']=null; + $skipParts=array(); + $messageMimeType=''; + foreach ($mailStructureObject->contentTypeMap() as $mime_id => $mime_type) + { + if ($mime_id==0 || $messageMimeType==='') $messageMimeType = $mime_type; + $part = $mailStructureObject->getPart($mime_id); + $partdisposition = $part->getDisposition(); + $partPrimaryType = $part->getPrimaryType(); + $cid = $part->getContentId(); + if (empty($partdisposition) && $partPrimaryType != 'multipart' && $partPrimaryType != 'text') + { + $partdisposition=($partPrimaryType == 'image'&&!empty($cid)?'inline':'attachment'); + } + if ($mime_type=='message/rfc822') + { + //error_log(__METHOD__.' ('.__LINE__.') '.' Uid:'.$uid.'->'.$mime_id.':'.array2string($part->contentTypeMap())); + foreach($part->contentTypeMap() as $sub_id => $sub_type) if ($sub_id != $mime_id) $skipParts[$sub_id] = $sub_type; + } + //error_log(__METHOD__.' ('.__LINE__.') '.' Uid:'.$uid.'->'.$mime_id.' Disp:'.$partdisposition.' Type:'.$partPrimaryType.' Skip:'.array2string($skipParts)); + if (array_key_exists($mime_id,$skipParts)) continue; + if ($partdisposition=='attachment' || + ($partdisposition=='inline'&&$partPrimaryType == 'image'&&empty($cid)) || + ($partdisposition=='inline' && $partPrimaryType != 'image' && $partPrimaryType != 'multipart' && $partPrimaryType != 'text')) + { + $headerObject['ATTACHMENTS'][$mime_id]=$part->getAllDispositionParameters(); + $headerObject['ATTACHMENTS'][$mime_id]['mimeType']=$mime_type; + $headerObject['ATTACHMENTS'][$mime_id]['uid']=$uid; + $headerObject['ATTACHMENTS'][$mime_id]['cid'] = $cid; + $headerObject['ATTACHMENTS'][$mime_id]['partID']=$mime_id; + if (!isset($headerObject['ATTACHMENTS'][$mime_id]['name']))$headerObject['ATTACHMENTS'][$mime_id]['name']=$part->getName(); + //error_log(__METHOD__.' ('.__LINE__.') '.' PartDisposition:'.$mime_id.'->'.array2string($part->getName())); + //error_log(__METHOD__.' ('.__LINE__.') '.' PartDisposition:'.$mime_id.'->'.array2string($part->getAllDispositionParameters())); + //error_log(__METHOD__.' ('.__LINE__.') '.' Attachment:'.$mime_id.'->'.array2string($headerObject['ATTACHMENTS'][$mime_id])); + } + } + //error_log(__METHOD__.' ('.__LINE__.') '.' FindBody (plain):'.array2string($mailStructureObject->findBody('plain'))); + //error_log(__METHOD__.' ('.__LINE__.') '.' FindBody (html):'.array2string($mailStructureObject->findBody('html'))); + //if($count == 0) error_log(__METHOD__.array2string($headerObject)); + if (empty($headerObject['UID'])) continue; + //$uid = ($rByUid ? $headerObject['UID'] : $headerObject['MSG_NUM']); + // make dates like "Mon, 23 Apr 2007 10:11:06 UT" working with strtotime + if(substr($headerObject['DATE'],-2) === 'UT') { + $headerObject['DATE'] .= 'C'; + } + if(substr($headerObject['INTERNALDATE'],-2) === 'UT') { + $headerObject['INTERNALDATE'] .= 'C'; + } + //error_log(__METHOD__.' ('.__LINE__.') '.' '.$headerObject['SUBJECT'].'->'.$headerObject['DATE']); + //error_log(__METHOD__.' ('.__LINE__.') '.' '.$this->decode_subject($headerObject['SUBJECT']).'->'.$headerObject['DATE']); + if (isset($headerObject['ATTACHMENTS']) && count($headerObject['ATTACHMENTS'])) foreach ($headerObject['ATTACHMENTS'] as $pID =>$a) $retValue['header'][$sortOrder[$uid]]['attachments'][]=$a; + $retValue['header'][$sortOrder[$uid]]['subject'] = $this->decode_subject($headerObject['SUBJECT']); + $retValue['header'][$sortOrder[$uid]]['size'] = $headerObject['SIZE']; + $retValue['header'][$sortOrder[$uid]]['date'] = self::_strtotime(($headerObject['DATE']&&!($headerObject['DATE']=='NIL')?$headerObject['DATE']:$headerObject['INTERNALDATE']),'ts',true); + $retValue['header'][$sortOrder[$uid]]['internaldate']= self::_strtotime($headerObject['INTERNALDATE'],'ts',true); + $retValue['header'][$sortOrder[$uid]]['mimetype'] = $messageMimeType; + $retValue['header'][$sortOrder[$uid]]['id'] = $headerObject['MSG_NUM']; + $retValue['header'][$sortOrder[$uid]]['uid'] = $headerObject['UID']; + $retValue['header'][$sortOrder[$uid]]['priority'] = ($headerObject['PRIORITY']?$headerObject['PRIORITY']:3); + if (isset($headerObject['DISPOSITION-NOTIFICATION-TO'])) $retValue['header'][$sortOrder[$uid]]['disposition-notification-to'] = $headerObject['DISPOSITION-NOTIFICATION-TO']; + if (is_array($headerObject['FLAGS'])) { + $retValue['header'][$sortOrder[$uid]] = array_merge($retValue['header'][$sortOrder[$uid]],self::prepareFlagsArray($headerObject)); + } +//error_log(__METHOD__.' ('.__LINE__.') '.$headerObject['SUBJECT'].'->'.array2string($_headerObject->getEnvelope()->__get('from'))); + if(is_array($headerObject['FROM']) && $headerObject['FROM'][0]) { + $retValue['header'][$sortOrder[$uid]]['sender_address'] = self::decode_header($headerObject['FROM'][0]); + } + + if(is_array($headerObject['TO']) && $headerObject['TO'][0]) { + $retValue['header'][$sortOrder[$uid]]['to_address'] = self::decode_header($headerObject['TO'][0]); + if (count($headerObject['TO'])>1) + { + $ki=0; + foreach($headerObject['TO'] as $k => $add) + { + if ($k==0) continue; + //error_log(__METHOD__.' ('.__LINE__.') '."-> $k:".array2string($add)); + $retValue['header'][$sortOrder[$uid]]['additional_to_addresses'][$ki] = self::decode_header($add); + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($retValue['header'][$sortOrder[$uid]]['additional_to_addresses'][$ki])); + $ki++; + } + } + } + if(is_array($headerObject['CC']) && $headerObject['CC'][0]) { + $ki=0; + foreach($headerObject['CC'] as $k => $add) + { + //error_log(__METHOD__.' ('.__LINE__.') '."-> $k:".array2string($add)); + $retValue['header'][$sortOrder[$uid]]['cc_addresses'][$ki] = self::decode_header($add); + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($retValue['header'][$sortOrder[$uid]]['additional_to_addresses'][$ki])); + $ki++; + } + } + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($retValue['header'][$sortOrder[$uid]])); + + $count++; + } + if (self::$debug||self::$debugTimes) self::logRunTimes($starttime,null,' fetching Headers and stuff for Folder:'.$_folderName,__METHOD__.' ('.__LINE__.') '); + //self::$debug=false; + // sort the messages to the requested displayorder + if(is_array($retValue['header'])) { + $countMessages = $total; + if (isset($_filter['range'])) $countMessages = self::$folderStatusCache[$this->profileID][$_folderName]['messages']; + ksort($retValue['header']); + $retValue['info']['total'] = $total; + //if ($_startMessage>$total) $_startMessage = $total-($count-1); + $retValue['info']['first'] = $_startMessage; + $retValue['info']['last'] = $_startMessage + $count - 1 ; + return $retValue; + } else { + $retValue = array(); + $retValue['info']['total'] = 0; + $retValue['info']['first'] = 0; + $retValue['info']['last'] = 0; + return $retValue; + } + } else { + if ($headersNew == null && empty($_thisUIDOnly)) error_log(__METHOD__." -> retrieval of Message Details to Query $queryString failed: ".print_r($headersNew,TRUE)); + $retValue = array(); + $retValue['info']['total'] = 0; + $retValue['info']['first'] = 0; + $retValue['info']['last'] = 0; + return $retValue; + } + } + + /** + * static function prepareFlagsArray + * prepare headerObject to return some standardized array to tell which flags are set for a message + * @param array $headerObject - array to process, a full return array from icServer->getSummary + * @return array array of flags + */ + static function prepareFlagsArray($headerObject) + { + if (is_array($headerObject['FLAGS'])) $headerFlags = array_map('strtolower',$headerObject['FLAGS']); + $retValue = array(); + $retValue['recent'] = in_array('\\recent', $headerFlags); + $retValue['flagged'] = in_array('\\flagged', $headerFlags); + $retValue['answered'] = in_array('\\answered', $headerFlags); + $retValue['forwarded'] = in_array('$forwarded', $headerFlags); + $retValue['deleted'] = in_array('\\deleted', $headerFlags); + $retValue['seen'] = in_array('\\seen', $headerFlags); + $retValue['draft'] = in_array('\\draft', $headerFlags); + $retValue['mdnsent'] = in_array('$mdnsent', $headerFlags)||in_array('mdnsent', $headerFlags); + $retValue['mdnnotsent'] = in_array('$mdnnotsent', $headerFlags)||in_array('mdnnotsent', $headerFlags); + $retValue['label1'] = in_array('$label1', $headerFlags); + $retValue['label2'] = in_array('$label2', $headerFlags); + $retValue['label3'] = in_array('$label3', $headerFlags); + $retValue['label4'] = in_array('$label4', $headerFlags); + $retValue['label5'] = in_array('$label5', $headerFlags); + //error_log(__METHOD__.' ('.__LINE__.') '.$headerObject['SUBJECT'].':'.array2string($retValue)); + return $retValue; + } + + /** + * fetches a sorted list of messages from the imap server + * private function + * + * @todo implement sort based on Net_IMAP + * @param string $_folderName the name of the folder in which the messages get searched + * @param integer $_sort the primary sort key + * @param bool $_reverse sort the messages ascending or descending + * @param array $_filter the search filter + * @param bool $resultByUid if set to true, the result is to be returned by uid, if the server does not reply + * on a query for uids, the result may be returned by IDs only, this will be indicated by this param + * @param bool $setSession if set to true the session will be populated with the result of the query + * @return mixed bool/array false or array of ids + */ + function getSortedList($_folderName, $_sort, &$_reverse, $_filter, &$resultByUid=true, $setSession=true) + { + static $cachedFolderStatus; + // in the past we needed examineMailbox to figure out if the server with the serverID support keywords + // this information is filled/provided by examineMailbox; but caching within one request seems o.k. + if (is_null($cachedFolderStatus) || !isset($cachedFolderStatus[$this->profileID][$_folderName]) ) + { + $folderStatus = $cachedFolderStatus[$this->profileID][$_folderName] = $this->icServer->examineMailbox($_folderName); + } + else + { + $folderStatus = $cachedFolderStatus[$this->profileID][$_folderName]; + } + //error_log(__METHOD__.' ('.__LINE__.') '.' F:'.$_folderName.' S:'.array2string($folderStatus)); + //error_log(__METHOD__.' ('.__LINE__.') '.' Filter:'.array2string($_filter)); + $try2useCache = true; + static $eMailListContainsDeletedMessages; + if (is_null($eMailListContainsDeletedMessages)) $eMailListContainsDeletedMessages = egw_cache::getCache(egw_cache::INSTANCE,'email','eMailListContainsDeletedMessages'.trim($GLOBALS['egw_info']['user']['account_id']),$callback=null,$callback_params=array(),$expiration=60*60*1); + // this indicates, that there is no Filter set, and the returned set/subset should not contain DELETED Messages, nor filtered for UNDELETED + if ($setSession==true && ((strpos(array2string($_filter), 'UNDELETED') === false && strpos(array2string($_filter), 'DELETED') === false))) + { + if (self::$debugTimes) $starttime = microtime(true); + if (is_null($eMailListContainsDeletedMessages) || empty($eMailListContainsDeletedMessages[$this->profileID]) || empty($eMailListContainsDeletedMessages[$this->profileID][$_folderName])) $eMailListContainsDeletedMessages = egw_cache::getCache(egw_cache::INSTANCE,'email','eMailListContainsDeletedMessages'.trim($GLOBALS['egw_info']['user']['account_id']),$callback=null,$callback_params=array(),$expiration=60*60*1); + $deletedMessages = $this->getSortedList($_folderName, 0, $three=1, array('status'=>array('DELETED')),$five=true,false); + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($deletedMessages)); + $eMailListContainsDeletedMessages[$this->profileID][$_folderName] =$deletedMessages['count']; + egw_cache::setCache(egw_cache::INSTANCE,'email','eMailListContainsDeletedMessages'.trim($GLOBALS['egw_info']['user']['account_id']),$eMailListContainsDeletedMessages, $expiration=60*60*1); + if (self::$debugTimes) self::logRunTimes($starttime,null,'setting eMailListContainsDeletedMessages for Profile:'.$this->profileID.' Folder:'.$_folderName.' to '.$eMailListContainsDeletedMessages[$this->profileID][$_folderName],__METHOD__.' ('.__LINE__.') '); //error_log(__METHOD__.' ('.__LINE__.') '.' Profile:'.$this->profileID.' Folder:'.$_folderName.' -> EXISTS/SessStat:'.array2string($folderStatus['MESSAGES']).'/'.self::$folderStatusCache[$this->profileID][$_folderName]['messages'].' ListContDelMsg/SessDeleted:'.$eMailListContainsDeletedMessages[$this->profileID][$_folderName].'/'.self::$folderStatusCache[$this->profileID][$_folderName]['deleted']); + } + $try2useCache = false; + //self::$supportsORinQuery[$this->profileID]=true; + if (is_null(self::$supportsORinQuery) || !isset(self::$supportsORinQuery[$this->profileID])) + { + self::$supportsORinQuery = egw_cache::getCache(egw_cache::INSTANCE,'email','supportsORinQuery'.trim($GLOBALS['egw_info']['user']['account_id']),$callback=null,$callback_params=array(),$expiration=60*60*10); + if (!isset(self::$supportsORinQuery[$this->profileID])) self::$supportsORinQuery[$this->profileID]=true; + } + $filter = $this->createIMAPFilter($_folderName, $_filter,self::$supportsORinQuery[$this->profileID]); + //_debug_array($filter); + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($filter)); + if($this->icServer->hasCapability('SORT')) { + if (self::$debug) error_log(__METHOD__." Mailserver has SORT Capability, SortBy: $_sort Reverse: $_reverse"); + $sortOrder = $this->_getSortString($_sort, $_reverse); + if ($_reverse && in_array(Horde_Imap_Client::SORT_REVERSE,$sortOrder)) $_reverse=false; // as we reversed the result already + if (self::$debug) error_log(__METHOD__." Mailserver runs SORT: SortBy: $sortOrder Filter: ".array2string($filter)); + try + { + $sortResult = $this->icServer->search($_folderName, $filter, array( + 'sort' => $sortOrder,)); + // if there is an Error, we assume that the server is not capable of sorting + } + catch(Exception $e) + { + $resultByUid = false; + $sortOrder = array(Horde_Imap_Client::SORT_SEQUENCE); + if ($_reverse) array_unshift($sortOrder,Horde_Imap_Client::SORT_REVERSE); + try + { + $sortResult = $this->icServer->search($_folderName, $filter, array( + 'sort' => $sortOrder)); + } + catch(Exception $e) + { + error_log(__METHOD__.'('.__LINE__.'):'.$e->getMessage()); + $sortResult = self::$folderStatusCache[$this->profileID][$_folderName]['sortResult']; + } + } + if (self::$debug) error_log(__METHOD__.print_r($sortResult,true)); + } else { + if (self::$debug) error_log(__METHOD__." Mailserver has NO SORT Capability"); + //$sortOrder = array(Horde_Imap_Client::SORT_SEQUENCE); + //if ($_reverse) array_unshift($sortOrder,Horde_Imap_Client::SORT_REVERSE); + try + { + $sortResult = $this->icServer->search($_folderName, $filter, array()/*array( + 'sort' => $sortOrder)*/); + } + catch(Exception $e) + { + //error_log(__METHOD__.'('.__LINE__.'):'.$e->getMessage()); + // possible error OR Query. But Horde gives no detailed Info :-( + self::$supportsORinQuery[$this->profileID]=false; + egw_cache::setCache(egw_cache::INSTANCE,'email','supportsORinQuery'.trim($GLOBALS['egw_info']['user']['account_id']),self::$supportsORinQuery,$expiration=60*60*10); + if (self::$debug) error_log(__METHOD__.__LINE__." Mailserver seems to have NO OR Capability for Search:".$sortResult->message); + $filter = $this->createIMAPFilter($_folderName, $_filter, self::$supportsORinQuery[$this->profileID]); + try + { + $sortResult = $this->icServer->search($_folderName, $filter, array()/*array( + 'sort' => $sortOrder)*/); + } + catch(Exception $e) + { + } + } + if(is_array($sortResult['match'])) { + // not sure that this is going so succeed as $sortResult['match'] is a hordeObject + sort($sortResult['match'], SORT_NUMERIC); + } + if (self::$debug) error_log(__METHOD__." using Filter:".print_r($filter,true)." ->".print_r($sortResult,true)); + } + if ($setSession) + { + self::$folderStatusCache[$this->profileID][$_folderName]['uidValidity'] = $folderStatus['UIDVALIDITY']; + self::$folderStatusCache[$this->profileID][$_folderName]['messages'] = $folderStatus['MESSAGES']; + self::$folderStatusCache[$this->profileID][$_folderName]['deleted'] = $eMailListContainsDeletedMessages[$this->profileID][$_folderName]; + self::$folderStatusCache[$this->profileID][$_folderName]['uidnext'] = $folderStatus['UIDNEXT']; + self::$folderStatusCache[$this->profileID][$_folderName]['filter'] = $_filter; + self::$folderStatusCache[$this->profileID][$_folderName]['sortResult'] = $sortResult; + self::$folderStatusCache[$this->profileID][$_folderName]['sort'] = $_sort; + } + //_debug_array($sortResult['match']->ids); + return $sortResult; + } + + /** + * convert the sort value from the gui(integer) into a string + * + * @param mixed _sort the integer sort order / or a valid and handeled SORTSTRING (right now: UID/ARRIVAL/INTERNALDATE (->ARRIVAL)) + * @param bool _reverse wether to add REVERSE to the Sort String or not + * @return the sort sequence for horde search + */ + function _getSortString($_sort, $_reverse=false) + { + $_reverse=false; + if (is_numeric($_sort)) + { + switch($_sort) { + case 2: + $retValue = array(Horde_Imap_Client::SORT_FROM); + break; + case 4: + $retValue = array(Horde_Imap_Client::SORT_TO); + break; + case 3: + $retValue = array(Horde_Imap_Client::SORT_SUBJECT); + break; + case 6: + $retValue = array(Horde_Imap_Client::SORT_SIZE); + break; + case 0: + default: + $retValue = array(Horde_Imap_Client::SORT_DATE); + //$retValue = 'ARRIVAL'; + break; + } + } + else + { + switch(strtoupper($_sort)) { + case 'FROMADDRESS': + $retValue = array(Horde_Imap_Client::SORT_FROM); + break; + case 'TOADDRESS': + $retValue = array(Horde_Imap_Client::SORT_TO); + break; + case 'SUBJECT': + $retValue = array(Horde_Imap_Client::SORT_SUBJECT); + break; + case 'SIZE': + $retValue = array(Horde_Imap_Client::SORT_SIZE); + break; + case 'ARRIVAL': + $retValue = array(Horde_Imap_Client::SORT_ARRIVAL); + break; + case 'UID': // should be equivalent to INTERNALDATE, which is ARRIVAL, which should be highest (latest) uid should be newest date + case 'INTERNALDATE': + $retValue = array(Horde_Imap_Client::SORT_SEQUENCE); + break; + case 'DATE': + default: + $retValue = array(Horde_Imap_Client::SORT_DATE); + break; + } + } + if ($_reverse) array_unshift($retValue,Horde_Imap_Client::SORT_REVERSE); + //error_log(__METHOD__.' ('.__LINE__.') '.' '.($_reverse?'REVERSE ':'').$_sort.'->'.$retValue); + return $retValue; + } + + /** + * this function creates an IMAP filter from the criterias given + * + * @param string $_folder used to determine the search to TO or FROM on QUICK Search wether it is a send-folder or not + * @param array $_criterias contains the search/filter criteria + * @param boolean $_supportsOrInQuery wether to use the OR Query on QuickSearch + * @return Horde_Imap_Client_Search_Query the IMAP filter + */ + function createIMAPFilter($_folder, $_criterias, $_supportsOrInQuery=true) + { + $imapFilter = new Horde_Imap_Client_Search_Query(); + + //_debug_array($_criterias); + if (self::$debug) error_log(__METHOD__.' ('.__LINE__.') '.' Criterias:'.(!is_array($_criterias)?" none -> returning $all":array2string($_criterias))); + if((!is_array($_criterias) || $_criterias['status']=='any') && (!isset($_criterias['string']) || empty($_criterias['string']))) { + $imapFilter->flag('DELETED', $set=false); + return $imapFilter; + } + //error_log(__METHOD__.' ('.__LINE__.') '.print_r($_criterias, true)); + $imapSearchFilter = new Horde_Imap_Client_Search_Query(); + $queryValid = false; + if(!empty($_criterias['string'])) { + $criteria = strtoupper($_criterias['type']); + switch ($criteria) { + case 'QUICK': + $imapSearchFilter->headerText('SUBJECT', $_criterias['string'], $not=false); + $imapFilter2 = new Horde_Imap_Client_Search_Query(); + if($this->isSentFolder($_folder)) { + $imapFilter2->headerText('TO', $_criterias['string'], $not=false); + } else { + $imapFilter2->headerText('FROM', $_criterias['string'], $not=false); + } + if ($_supportsOrInQuery) + { + $imapSearchFilter->orSearch($imapFilter2); + } + else + { + $imapSearchFilter->andSearch($imapFilter2); + } + $queryValid = true; + break; + case 'FROM': + case 'TO': + case 'CC': + case 'BCC': + case 'SUBJECT': + $imapSearchFilter->headerText($criteria, $_criterias['string'], $not=false); + $queryValid = true; + break; + case 'BODY': + case 'TEXT': + $imapSearchFilter->text($_criterias['string'],($criteria=='BODY'?true:false), $not=false); + $queryValid = true; + break; + case 'SINCE': + $imapSearchFilter->dateSearch(new DateTime($_criterias['string']), Horde_Imap_Client_Search_Query::DATE_SINCE, $header=true, $not=false); + $queryValid = true; + break; + case 'BEFORE': + $imapSearchFilter->dateSearch(new DateTime($_criterias['string']), Horde_Imap_Client_Search_Query::DATE_BEFORE, $header=true, $not=false); + $queryValid = true; + break; + case 'ON': + $imapSearchFilter->dateSearch(new DateTime($_criterias['string']), Horde_Imap_Client_Search_Query::DATE_ON, $header=true, $not=false); + $queryValid = true; + break; + } + } + if ($queryValid) $imapFilter->andSearch($imapSearchFilter); + $statusQueryValid = false; + foreach((array)$_criterias['status'] as $k => $criteria) { + $imapStatusFilter = new Horde_Imap_Client_Search_Query(); + $criteria = strtoupper($criteria); + switch ($criteria) { + case 'ANSWERED': + case 'DELETED': + case 'FLAGGED': + case 'RECENT': + case 'SEEN': + $imapStatusFilter->flag($criteria, $set=true); + $queryValid = $statusQueryValid =true; + break; + case 'READ': + $imapStatusFilter->flag('SEEN', $set=true); + $queryValid = $statusQueryValid =true; + break; + case 'LABEL1': + case 'KEYWORD1': + case 'LABEL2': + case 'KEYWORD2': + case 'LABEL3': + case 'KEYWORD3': + case 'LABEL4': + case 'KEYWORD4': + case 'LABEL5': + case 'KEYWORD5': + $imapStatusFilter->flag(str_ireplace('KEYWORD','$LABEL',$criteria), $set=true); + $queryValid = $statusQueryValid =true; + break; + case 'NEW': + $imapStatusFilter->flag('RECENT', $set=true); + $imapStatusFilter->flag('SEEN', $set=false); + $queryValid = $statusQueryValid =true; + break; + case 'OLD': + $imapStatusFilter->flag('RECENT', $set=false); + $queryValid = $statusQueryValid =true; + break; +// operate only on system flags +// $systemflags = array( +// 'ANSWERED', 'DELETED', 'DRAFT', 'FLAGGED', 'RECENT', 'SEEN' +// ); + case 'UNANSWERED': + $imapStatusFilter->flag('ANSWERED', $set=false); + $queryValid = $statusQueryValid =true; + break; + case 'UNDELETED': + $imapFilter->flag('DELETED', $set=false); + $queryValid = true; + break; + case 'UNFLAGGED': + $imapStatusFilter->flag('FLAGGED', $set=false); + $queryValid = $statusQueryValid =true; + break; + case 'UNREAD': + case 'UNSEEN': + $imapStatusFilter->flag('SEEN', $set=false); + $queryValid = $statusQueryValid =true; + break; + case 'UNLABEL1': + case 'UNKEYWORD1': + case 'UNLABEL2': + case 'UNKEYWORD2': + case 'UNLABEL3': + case 'UNKEYWORD3': + case 'UNLABEL4': + case 'UNKEYWORD4': + case 'UNLABEL5': + case 'UNKEYWORD5': + $imapStatusFilter->flag(str_ireplace(array('UNKEYWORD','UNLABEL'),'$LABEL',$criteria), $set=false); + $queryValid = $statusQueryValid =true; + break; + default: + $statusQueryValid = false; + } + if ($statusQueryValid) $imapFilter->andSearch($imapStatusFilter); + if ($statusQueryValid && !$queryValid) $queryValid=true; + } + if (isset($_criterias['range']) && !empty($_criterias['range'])) + { + //$imapFilter .= $_criterias['range'].' '; + } + if (self::$debug) + { + $query_str = $imapFilter->build(); + error_log(__METHOD__.' ('.__LINE__.') '.' '.$query_str['query']); + } + if($queryValid==false) { + $imapFilter->flag('DELETED', $set=false); + return $imapFilter; + } else { + return $imapFilter; + } + } + + /** + * decode header (or envelope information) + * if array given, note that only values will be converted + * @param mixed $_string input to be converted, if array call decode_header recursively on each value + * @param mixed/boolean $_tryIDNConversion (true/false AND FORCE): try IDN Conversion on domainparts of emailADRESSES + * @return mixed - based on the input type + */ + static function decode_header($_string, $_tryIDNConversion=false) + { + if (is_array($_string)) + { + foreach($_string as $k=>$v) + { + $_string[$k] = self::decode_header($v, $_tryIDNConversion); + } + return $_string; + } + else + { + $_string = translation::decodeMailHeader($_string,self::$displayCharset); + if ($_tryIDNConversion===true && stripos($_string,'@')!==false) + { + $rfcAddr = imap_rfc822_parse_adrlist(str_replace(',','\,',$_string),''); + if (!isset(self::$idna2)) self::$idna2 = new egw_idna; + $stringA = array(); + //$_string = str_replace($rfcAddr[0]->host,self::$idna2->decode($rfcAddr[0]->host),$_string); + foreach ((array)$rfcAddr as $_rfcAddr) + { + if ($_rfcAddr->host=='.SYNTAX-ERROR.') + { + $stringA = array(); + break; // skip idna conversion if we encounter an error here + } + $stringA[] = imap_rfc822_write_address($_rfcAddr->mailbox,self::$idna2->decode($_rfcAddr->host),$_rfcAddr->personal); + } + if (!empty($stringA)) $_string = implode(',',$stringA); + } + if ($_tryIDNConversion==='FORCE') + { + //error_log(__METHOD__.' ('.__LINE__.') '.'->'.$_string.'='.self::$idna2->decode($_string)); + $_string = self::$idna2->decode($_string); + } + return $_string; + } + } + + /** + * decode subject + * if array given, note that only values will be converted + * @param mixed $_string input to be converted, if array call decode_header recursively on each value + * @param boolean $decode try decoding + * @return mixed - based on the input type + */ + function decode_subject($_string,$decode=true) + { + #$string = $_string; + if($_string=='NIL') + { + return 'No Subject'; + } + if ($decode) $_string = self::decode_header($_string); + // make sure its utf-8 + $test = @json_encode($_string); + if (($test=="null" || $test === false || is_null($test)) && strlen($_string)>0) + { + $_string = utf8_encode($_string); + } + return $_string; + + } + + /** + * decodeEntityFolderName - remove html entities + * @param string _folderName the foldername + * @return string the converted string + */ + function decodeEntityFolderName($_folderName) + { + return html_entity_decode($_folderName, ENT_QUOTES, self::$displayCharset); + } + + /** + * convert a mailboxname from utf7-imap to displaycharset + * + * @param string _folderName the foldername + * @return string the converted string + */ + function encodeFolderName($_folderName) + { + return translation::convert($_folderName, 'UTF7-IMAP', self::$displayCharset); + } + + /** + * convert the foldername from display charset to UTF-7 + * + * @param string _parent the parent foldername + * @return ISO-8859-1 / UTF7-IMAP encoded string + */ + function _encodeFolderName($_folderName) { + return translation::convert($_folderName, self::$displayCharset, 'ISO-8859-1'); + #return translation::convert($_folderName, self::$displayCharset, 'UTF7-IMAP'); + } + + /** + * create a new folder under given parent folder + * + * @param string _parent the parent foldername + * @param string _folderName the new foldername + * @param bool _subscribe subscribe to the new folder + * + * @return mixed name of the newly created folder or false on error + */ + function createFolder($_parent, $_folderName, $_subscribe=false) + { + if (self::$debug) error_log(__METHOD__.' ('.__LINE__.') '."->"."$_parent, $_folderName, $_subscribe"); + $parent = $_parent;//$this->_encodeFolderName($_parent); + $folderName = $_folderName;//$this->_encodeFolderName($_folderName); + + if(empty($parent)) { + $newFolderName = $folderName; + } else { + $HierarchyDelimiter = $this->getHierarchyDelimiter(); + $newFolderName = $parent . $HierarchyDelimiter . $folderName; + } + if (empty($newFolderName)) return false; + if (self::$debug) error_log(__METHOD__.' ('.__LINE__.') '.'->'.$newFolderName); + if ($this->folderExists($newFolderName,true)) + { + if (self::$debug) error_log(__METHOD__.' ('.__LINE__.') '." Folder $newFolderName already exists."); + return $newFolderName; + } + try + { + $rv = $this->icServer->createMailbox($newFolderName); + } + catch (Exception $e) + { + error_log(__METHOD__.' ('.__LINE__.') '.' create Folder '.$newFolderName.'->'.$e->getMessage().' Namespace:'.array2string($this->icServer->getNameSpaces()).function_backtrace()); + return false; + } + try + { + $srv = $this->icServer->subscribeMailbox($newFolderName); + } + catch (Exception $e) + { + error_log(__METHOD__.' ('.__LINE__.') '.' subscribe to new folder '.$newFolderName.'->'.$e->getMessage()); + return false; + } + + return $newFolderName; + } + + /** + * rename a folder + * + * @param string _oldFolderName the old foldername + * @param string _parent the parent foldername + * @param string _folderName the new foldername + * + * @return mixed name of the newly created folder or false on error + */ + function renameFolder($_oldFolderName, $_parent, $_folderName) + { + $oldFolderName = $_oldFolderName;//$this->_encodeFolderName($_oldFolderName); + $parent = $_parent;//$this->_encodeFolderName($_parent); + $folderName = $_folderName;//$this->_encodeFolderName($_folderName); + + if(empty($parent)) { + $newFolderName = $folderName; + } else { + $HierarchyDelimiter = $this->getHierarchyDelimiter(); + $newFolderName = $parent . $HierarchyDelimiter . $folderName; + } + if (self::$debug) error_log("create folder: $newFolderName"); + try + { + $rv = $this->icServer->renameMailbox($oldFolderName, $newFolderName); + } + catch (Exception $e) + { + throw new egw_exception(__METHOD__." failed for $oldFolderName (rename to: $newFolderName) with error:".$e->getMessage());; + } + + return $newFolderName; + + } + + /** + * delete an existing folder + * + * @param string _folderName the name of the folder to be deleted + * + * @return bool true on success, PEAR Error on failure + */ + function deleteFolder($_folderName) + { + //$folderName = $this->_encodeFolderName($_folderName); + try + { + $this->icServer->subscribeMailbox($_folderName,false); + $this->icServer->deleteMailbox($_folderName); + } + catch (Exception $e) + { + throw new egw_exception("Deleting Folder $_foldername failed! Error:".$e->getMessage());; + } + + return true; + } + + function subscribe($_folderName, $_status) + { + if (self::$debug) error_log(__METHOD__."::".($_status?"":"un")."subscribe:".$_folderName); + if($_status === true) { + try + { + $rv = $this->icServer->subscribeMailbox($_folderName); + } + catch (Exception $e) + { + error_log(__METHOD__."::".($_status?"":"un")."subscribe:".$_folderName." failed:".$e->getMessage); + return false; + } + } else { + try + { + $rv = $this->icServer->subscribeMailbox($_folderName,false); + } + catch (Exception $e) + { + error_log(__METHOD__."::".($_status?"":"un")."subscribe:".$_folderName." failed:".$e->getMessage); + return false; + } + } + + return true; + } + + /** + * fetchUnSubscribedFolders: get unsubscribed IMAP folder list + * + * returns an array of unsubscribed IMAP folder names. + * + * @return array with folder names. eg.: 1 => INBOX/TEST + */ + function fetchUnSubscribedFolders() + { + $unSubscribedMailboxes = $this->icServer->listUnSubscribedMailboxes(); + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($unSubscribedMailboxes)); + return $unSubscribedMailboxes; + } + + /** + * get IMAP folder objects + * + * returns an array of IMAP folder objects. Put INBOX folder in first + * position. Preserves the folder seperator for later use. The returned + * array is indexed using the foldername. Use cachedObjects when retrieving subscribedFolders + * + * @param boolean _subscribedOnly get subscribed or all folders + * @param boolean _getCounters get get messages counters + * @param boolean _alwaysGetDefaultFolders this triggers to ignore the possible notavailableautofolders - preference + * as activeSync needs all folders like sent, trash, drafts, templates and outbox - if not present devices may crash + * -> autoFolders should be created if needed / accessed (if possible and configured) + * @param boolean _useCacheIfPossible - if set to false cache will be ignored and reinitialized + * + * @return array with folder objects. eg.: INBOX => {inbox object} + */ + function getFolderObjects($_subscribedOnly=false, $_getCounters=false, $_alwaysGetDefaultFolders=false,$_useCacheIfPossible=true) + { + if (self::$debug) error_log(__METHOD__.' ('.__LINE__.') '.' ServerID:'.$this->icServer->ImapServerId.", subscribedOnly:$_subscribedOnly, getCounters:$_getCounters, alwaysGetDefaultFolders:$_alwaysGetDefaultFolders, _useCacheIfPossible:$_useCacheIfPossible"); + if (self::$debugTimes) $starttime = microtime (true); + static $folders2return; + //$_subscribedOnly=false; + // always use static on single request if info is available; + // so if you require subscribed/unsubscribed results on a single request you MUST + // set $_useCacheIfPossible to false ! + if ($_useCacheIfPossible && isset($folders2return[$this->icServer->ImapServerId]) && !empty($folders2return[$this->icServer->ImapServerId])) + { + if (self::$debugTimes) self::logRunTimes($starttime,null,'using static',__METHOD__.' ('.__LINE__.') '); + return $folders2return[$this->icServer->ImapServerId]; + } + + if ($_subscribedOnly && $_getCounters===false) + { + if (is_null($folders2return)) $folders2return = egw_cache::getCache(egw_cache::INSTANCE,'email','folderObjects'.trim($GLOBALS['egw_info']['user']['account_id']),$callback=null,$callback_params=array(),$expiration=60*60*1); + if ($_useCacheIfPossible && isset($folders2return[$this->icServer->ImapServerId]) && !empty($folders2return[$this->icServer->ImapServerId])) + { + //error_log(__METHOD__.' ('.__LINE__.') '.' using Cached folderObjects'.array2string($folders2return[$this->icServer->ImapServerId])); + if (self::$debugTimes) self::logRunTimes($starttime,null,'from Cache',__METHOD__.' ('.__LINE__.') '); + return $folders2return[$this->icServer->ImapServerId]; + } + } + // use $folderBasicInfo for holding attributes and other basic folderinfo $folderBasicInfo[$this->icServer->ImapServerId] + static $folderBasicInfo; + if (is_null($folderBasicInfo)) $folderBasicInfo = egw_cache::getCache(egw_cache::INSTANCE,'email','folderBasicInfo'.trim($GLOBALS['egw_info']['user']['account_id']),null,array(),$expiration=60*60*1); + //error_log(__METHOD__.' ('.__LINE__.') '.array2string(array_keys($folderBasicInfo[$this->icServer->ImapServerId]))); + $isUWIMAP = false; + + $delimiter = $this->getHierarchyDelimiter(); + + $inboxData = new stdClass; + $inboxData->name = 'INBOX'; + $inboxData->folderName = 'INBOX'; + $inboxData->displayName = lang('INBOX'); + $inboxData->delimiter = $delimiter; + $inboxData->shortFolderName = 'INBOX'; + $inboxData->shortDisplayName = lang('INBOX'); + $inboxData->subscribed = true; + if($_getCounters == true) { + $inboxData->counter = self::getMailBoxCounters('INBOX'); + } + // force unsubscribed by preference showAllFoldersInFolderPane + if ($_subscribedOnly == true && + isset($this->mailPreferences['showAllFoldersInFolderPane']) && + $this->mailPreferences['showAllFoldersInFolderPane']==1) + { + $_subscribedOnly = false; + } + #$inboxData->attributes = 64; + $inboxFolderObject = array('INBOX' => $inboxData); + #_debug_array($folders); + + //$nameSpace = $this->icServer->getNameSpaces(); + $nameSpace = $this->_getNameSpaces(); + //error_log(__METHOD__.__LINE__.array2string($nameSpace)); + //_debug_array($nameSpace); + //_debug_array($delimiter); + if (is_array($nameSpace)) + { + foreach($nameSpace as $k => $singleNameSpace) { + $type = $singleNameSpace['type']; + $prefix_present = $singleNameSpace['prefix_present']; + // the following line (assumption that for the same namespace the delimiter should be equal) may be wrong + $foldersNameSpace[$type]['delimiter'] = $singleNameSpace['delimiter']; + + if(is_array($singleNameSpace)) { + // fetch and sort the subscribed folders + // we alway fetch the subscribed, as this provides the only way to tell + // if a folder is subscribed or not + if ($_subscribedOnly == true); + { + try + { + $subscribedMailboxes = $this->icServer->listSubscribedMailboxes($singleNameSpace['prefix'],0,true); + if (empty($subscribedMailboxes) && $type == 'shared') + { + $subscribedMailboxes = $this->icServer->listSubscribedMailboxes('',0,true); + } + } + catch(Exception $e) + { + continue; + } + //echo "subscribedMailboxes";_debug_array($subscribedMailboxes); + $subscribedFoldersPerNS = (!empty($subscribedMailboxes)?array_keys($subscribedMailboxes):array()); + //if (is_array($foldersNameSpace[$type]['subscribed'])) sort($foldersNameSpace[$type]['subscribed']); + //_debug_array($foldersNameSpace); + //error_log(__METHOD__.__LINE__.array2string($singleNameSpace).':#:'.array2string($subscribedFoldersPerNS)); + if (!empty($subscribedFoldersPerNS) && !empty($subscribedMailboxes)) + { + //error_log(__METHOD__.' ('.__LINE__.') '." $type / subscribed:". array2string($subscribedMailboxes)); + foreach ($subscribedMailboxes as $k => $finfo) + { + //error_log(__METHOD__.__LINE__.$k.':#:'.array2string($finfo)); + $folderBasicInfo[$this->icServer->ImapServerId][$k]=array( + 'MAILBOX'=>$finfo['MAILBOX'], + 'ATTRIBUTES'=>$finfo['ATTRIBUTES'], + 'delimiter'=>$finfo['delimiter'],//lowercase for some reason??? + 'SUBSCRIBED'=>$mbx['SUBSCRIBED'],//seeded by getMailboxes + ); + if (empty($foldersNameSpace[$type]['subscribed']) || !in_array($k,$foldersNameSpace[$type]['subscribed'])) + { + $foldersNameSpace[$type]['subscribed'][] = $k; + } + if (empty($foldersNameSpace[$type]['all']) || !in_array($k,$foldersNameSpace[$type]['all'])) + { + $foldersNameSpace[$type]['all'][] = $k; + } + } + } + //error_log(__METHOD__.' ('.__LINE__.') '.' '.$type.'->'.array2string($foldersNameSpace[$type]['subscribed'])); + if (!is_array($foldersNameSpace[$type]['all'])) $foldersNameSpace[$type]['all'] = array(); + if ($_subscribedOnly == true && !empty($foldersNameSpace[$type]['subscribed'])) { + continue; + } + + } + // skip the checks here completely; we rely on Hordes code/results as for now + // this improves speed tremendously for the !$_subscribedOnly - mode + /* + // only check for Folder in FolderMaintenance for Performance Reasons + if(!$_subscribedOnly) { + foreach ((array)$foldersNameSpace[$type]['subscribed'] as $folderName) + { + if ($singleNameSpace['prefix'] == $folderName || $singleNameSpace['prefix'] == $folderName.$singleNameSpace['delimiter']) continue; + //echo __METHOD__."Checking $folderName for existence
"; + if (!$this->folderExists($folderName,true)) { + //echo("eMail Folder $folderName failed to exist; should be unsubscribed; Trying ..."); + if ($this->subscribe($folderName, false)) + { + $r = " success."; + } else { + $r = " failed."; + } + error_log(__METHOD__."-> $folderName in NS: $type failed to be here; should be unsubscribed....".$r); + } + } + } + */ + + // fetch and sort all folders + //echo $type.'->'.$singleNameSpace['prefix'].'->'.($type=='shared'?0:2)."
"; + try + { + // calling with 2 lists all mailboxes on that level with fetches all + // we switch to all, to avoid further calls for subsequent levels + // that may produce problems, when encountering recursions probably + // horde is handling that, so we do not; keep that in mind! + //$allMailboxesExt = $this->icServer->getMailboxes($singleNameSpace['prefix'],2,true); + $allMailboxesExt = $this->icServer->getMailboxes($singleNameSpace['prefix'],0,true); + } + catch (Exception $e) + { + error_log(__METHOD__.' ('.__LINE__.') '.' Failed to retrieve all Boxes:'.$e->getMessage()); + $allMailboxesExt = array(); + } +/* + if (empty($allMailboxesExt) && $type == 'shared') + { + try + { + $allMailboxesExt = $this->icServer->getMailboxes('',0,true); + } + catch (Exception $e) + { + $allMailboxesExt = array(); + } + } + else + { + if ($prefix_present=='forced' && $type=='personal') // you cannot trust dovecots assumed prefix + { + try + { + $allMailboxesExtAll = $this->icServer->getMailboxes('',0,true); + } + catch (Exception $e) + { + $allMailboxesExtAll=array(); + } + foreach ($allMailboxesExtAll as $kaMEA => $aMEA) + { + if (!in_array($aMEA,$allMailboxesExt)) $allMailboxesExt[] = $aMEA; + } + } + } + $allMailBoxesExtSorted = array(); +*/ + if (!is_array($allMailboxesExt)) + { + //error_log(__METHOD__.' ('.__LINE__.') '.' Expected Array but got:'.array2string($allMailboxesExt). 'Type:'.$type.' Prefix:'.$singleNameSpace['prefix']); + continue; + //$allMailboxesExt=array(); + } + + //error_log(__METHOD__.' ('.__LINE__.') '.' '.$type.'->'.array2string($allMailboxesExt)); + foreach ($allMailboxesExt as $mbx) { + if (!isset($folderBasicInfo[$this->icServer->ImapServerId][$mbx['MAILBOX']])) + { + $folderBasicInfo[$this->icServer->ImapServerId][$mbx['MAILBOX']]=array( + 'MAILBOX'=>$mbx['MAILBOX'], + 'ATTRIBUTES'=>$mbx['ATTRIBUTES'], + 'delimiter'=>$mbx['delimiter'],//lowercase for some reason??? + 'SUBSCRIBED'=>$mbx['SUBSCRIBED'],//seeded by getMailboxes + ); + } + if ($mbx['SUBSCRIBED'] && (empty($foldersNameSpace[$type]['subscribed']) || !in_array($mbx['MAILBOX'],$foldersNameSpace[$type]['subscribed']))) + { + $foldersNameSpace[$type]['subscribed'][] = $mbx['MAILBOX']; + } + //echo __METHOD__;_debug_array($mbx); + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($mbx)); + if (isset($allMailBoxesExtSorted[$mbx['MAILBOX']])|| + isset($allMailBoxesExtSorted[$mbx['MAILBOX'].$foldersNameSpace[$type]['delimiter']])|| + (substr($mbx['MAILBOX'],-1)==$foldersNameSpace[$type]['delimiter'] && isset($allMailBoxesExtSorted[substr($mbx['MAILBOX'],0,-1)])) + ) continue; + + //echo '#'.$mbx['MAILBOX'].':'.array2string($mbx)."#
"; + $allMailBoxesExtSorted[$mbx['MAILBOX']] = $mbx; + } + if (is_array($allMailBoxesExtSorted)) ksort($allMailBoxesExtSorted); + //_debug_array(array_keys($allMailBoxesExtSorted)); + $allMailboxes = array(); + foreach ((array)$allMailBoxesExtSorted as $mbx) { + //echo $mbx['MAILBOX']."
"; + // this is not used when we are callinglistMailboxes with $restriction_search = 0 + // this lists all mailboxes so fetching mailboxes for each level would be obsolete + /* + if (in_array('\HasChildren',$mbx["ATTRIBUTES"]) || in_array('\Haschildren',$mbx["ATTRIBUTES"]) || in_array('\haschildren',$mbx["ATTRIBUTES"])) { + unset($buff); + if (!in_array($mbx['MAILBOX'],$allMailboxes)) $buff = self::getMailBoxesRecursive($mbx['MAILBOX'],$delimiter,$foldersNameSpace[$type]['prefix'],1); + if( PEAR::isError($buff) ) { + continue; + } + #_debug_array($buff); + if (is_array($buff)) $allMailboxes = array_merge($allMailboxes,$buff); + } + */ + if (!in_array($mbx['MAILBOX'],$allMailboxes)) $allMailboxes[] = $mbx['MAILBOX']; + //echo "Result:";_debug_array($allMailboxes); + } + $foldersNameSpace[$type]['all'] = $allMailboxes; + if (is_array($foldersNameSpace[$type]['all'])) sort($foldersNameSpace[$type]['all']); + } + } +/* + // check for autocreated folders + if(isset($foldersNameSpace['personal']['prefix'])) { + $personalPrefix = $foldersNameSpace['personal']['prefix']; + $personalDelimiter = $foldersNameSpace['personal']['delimiter']; + if(!empty($personalPrefix)) { + if(substr($personalPrefix, -1) != $personalDelimiter) { + $folderPrefix = $personalPrefix . $personalDelimiter; + } else { + $folderPrefix = $personalPrefix; + } + } + else + { + if(substr($personalPrefix, -1) != $personalDelimiter) { + $folderPrefixAsInbox = 'INBOX' . $personalDelimiter; + } else { + $folderPrefixAsInbox = 'INBOX'; + } + } + if (!$_alwaysGetDefaultFolders && $this->mailPreferences['notavailableautofolders'] && !empty($this->mailPreferences['notavailableautofolders'])) + { + $foldersToCheck = array_diff(self::$autoFolders,explode(',',$this->mailPreferences['notavailableautofolders'])); + } else { + $foldersToCheck = self::$autoFolders; + } + //error_log(__METHOD__.' ('.__LINE__.') '." foldersToCheck:".array2string($foldersToCheck)); + //error_log(__METHOD__.' ('.__LINE__.') '." foldersToCheck:".array2string( $this->mailPreferences['sentFolder'])); + foreach($foldersToCheck as $personalFolderName) { + $folderName = (!empty($personalPrefix) ? $folderPrefix.$personalFolderName : $personalFolderName); + //error_log(__METHOD__.' ('.__LINE__.') '." foldersToCheck: $personalFolderName / $folderName"); + if(!is_array($foldersNameSpace['personal']['all']) || !in_array($folderName, $foldersNameSpace['personal']['all'])) { + $createfolder = true; + switch($personalFolderName) + { + case 'Drafts': // => Entwürfe + $draftFolder = $this->getDraftFolder(); + if ($draftFolder && $draftFolder=='none') + $createfolder=false; + break; + case 'Junk': //] => Spammails + $junkFolder = $this->getJunkFolder(); + if ($junkFolder && $junkFolder=='none') + $createfolder=false; + break; + case 'Sent': //] => Gesendet + // ToDo: we may need more sophistcated checking here + $sentFolder = $this->getSentFolder(); + if ($sentFolder && $sentFolder=='none') + $createfolder=false; + break; + case 'Trash': //] => Papierkorb + $trashFolder = $this->getTrashFolder(); + if ($trashFolder && $trashFolder=='none') + $createfolder=false; + break; + case 'Templates': //] => Vorlagen + $templateFolder = $this->getTemplateFolder(); + if ($templateFolder && $templateFolder=='none') + $createfolder=false; + break; + case 'Outbox': // Nokia Outbox for activesync + //if ($this->mailPreferences['outboxFolder'] && $this->mailPreferences['outboxFolder']=='none') + $createfolder=false; + if ($GLOBALS['egw_info']['user']['apps']['activesync']) $createfolder = true; + break; + } + // check for the foldername as constructed with prefix (or not) + if ($createfolder && $this->folderExists($folderName)) + { + $createfolder = false; + } + // check for the folder as it comes (no prefix) + if ($createfolder && $personalFolderName != $folderName && $this->folderExists($personalFolderName)) + { + $createfolder = false; + $folderName = $personalFolderName; + } + // check for the folder as it comes with INBOX prefixed + $folderWithInboxPrefixed = $folderPrefixAsInbox.$personalFolderName; + if ($createfolder && $folderWithInboxPrefixed != $folderName && $this->folderExists($folderWithInboxPrefixed)) + { + $createfolder = false; + $folderName = $folderWithInboxPrefixed; + } + // now proceed with the folderName that may be altered in the progress of testing for existence + if ($createfolder === false && $_alwaysGetDefaultFolders) + { + if (!in_array($folderName,$foldersNameSpace['personal']['all'])) $foldersNameSpace['personal']['all'][] = $folderName; + if (!in_array($folderName,$foldersNameSpace['personal']['subscribed'])) $foldersNameSpace['personal']['subscribed'][] = $folderName; + } + + if($createfolder === true && $this->createFolder('', $folderName, true)) { + $foldersNameSpace['personal']['all'][] = $folderName; + $foldersNameSpace['personal']['subscribed'][] = $folderName; + } else { + #print "FOLDERNAME failed: $folderName
"; + } + } + } + } +*/ + } + //echo "
FolderNameSpace To Process:";_debug_array($foldersNameSpace); + $autoFolderObjects = array(); + foreach( array('personal', 'others', 'shared') as $type) { + if(isset($foldersNameSpace[$type])) { + if($_subscribedOnly) { + if( !empty($foldersNameSpace[$type]['subscribed']) ) $listOfFolders = $foldersNameSpace[$type]['subscribed']; + } else { + if( !empty($foldersNameSpace[$type]['all'])) $listOfFolders = $foldersNameSpace[$type]['all']; + } + foreach((array)$listOfFolders as $folderName) { + //echo "
FolderToCheck:$folderName
"; + //error_log(__METHOD__.__LINE__.'#Delimiter:'.$delimiter.':#'.$folderName); + if($_subscribedOnly && !(in_array($folderName, $foldersNameSpace[$type]['all'])||in_array($folderName.$foldersNameSpace[$type]['delimiter'], $foldersNameSpace[$type]['all']))) { + #echo "$folderName failed to be here
"; + continue; + } + if (isset($folders[$folderName])) continue; + if (isset($autoFolderObjects[$folderName])) continue; + if (empty($delimiter)||$delimiter != $foldersNameSpace[$type]['delimiter']) $delimiter = $foldersNameSpace[$type]['delimiter']; + $folderParts = explode($delimiter, $folderName); + $shortName = array_pop($folderParts); + + $folderObject = new stdClass; + $folderObject->delimiter = $delimiter; + $folderObject->folderName = $folderName; + $folderObject->shortFolderName = $shortName; + if(!$_subscribedOnly) { + #echo $folderName."->".$type."
"; + #_debug_array($foldersNameSpace[$type]['subscribed']); + $folderObject->subscribed = in_array($folderName, $foldersNameSpace[$type]['subscribed']); + } + + if($_getCounters == true) { + //error_log(__METHOD__.' ('.__LINE__.') '.' getCounter forFolder:'.$folderName); + $folderObject->counter = $this->getMailBoxCounters($folderName); + } + if(strtoupper($folderName) == 'INBOX') { + $folderName = 'INBOX'; + $folderObject->folderName = 'INBOX'; + $folderObject->shortFolderName = 'INBOX'; + $folderObject->displayName = lang('INBOX'); + $folderObject->shortDisplayName = lang('INBOX'); + $folderObject->subscribed = true; + // translate the automatic Folders (Sent, Drafts, ...) like the INBOX + } elseif (in_array($shortName,self::$autoFolders)) { + $tmpfolderparts = explode($delimiter,$folderObject->folderName); + array_pop($tmpfolderparts); + $folderObject->displayName = implode($delimiter,$tmpfolderparts).$delimiter.lang($shortName); + $folderObject->shortDisplayName = lang($shortName); + unset($tmpfolderparts); + } else { + $folderObject->displayName = $folderObject->folderName; + $folderObject->shortDisplayName = $shortName; + } + //$folderName = $folderName; + if (in_array($shortName,self::$autoFolders)&&self::searchValueInFolderObjects($shortName,$autoFolderObjects)===false) { + $autoFolderObjects[$folderName] = $folderObject; + } else { + $folders[$folderName] = $folderObject; + } + //error_log(__METHOD__.' ('.__LINE__.') '.':'.$folderObject->folderName); + } + } + } + if (is_array($autoFolderObjects) && !empty($autoFolderObjects)) { + uasort($autoFolderObjects,array($this,"sortByAutoFolderPos")); + } + if (is_array($folders)) uasort($folders,array($this,"sortByDisplayName")); + //$folders2return = array_merge($autoFolderObjects,$folders); + //_debug_array($folders2return); #exit; + $folders2return[$this->icServer->ImapServerId] = array_merge((array)$inboxFolderObject,(array)$autoFolderObjects,(array)$folders); + if (($_subscribedOnly && $_getCounters===false) || + ($_subscribedOnly == false && $_getCounters===false && + isset($this->mailPreferences['showAllFoldersInFolderPane']) && + $this->mailPreferences['showAllFoldersInFolderPane']==1)) + { + egw_cache::setCache(egw_cache::INSTANCE,'email','folderObjects'.trim($GLOBALS['egw_info']['user']['account_id']),$folders2return,$expiration=60*60*1); + } + egw_cache::setCache(egw_cache::INSTANCE,'email','folderBasicInfo'.trim($GLOBALS['egw_info']['user']['account_id']),$folderBasicInfo,$expiration=60*60*1); + if (self::$debugTimes) self::logRunTimes($starttime,null,function_backtrace(),__METHOD__.' ('.__LINE__.') '); + return $folders2return[$this->icServer->ImapServerId]; + } + + /** + * search Value In FolderObjects + * + * Helper function to search for a specific value within the foldertree objects + * @param string $needle + * @param array $haystack, array of folderobjects + * @return MIXED false or key + */ + static function searchValueInFolderObjects($needle, $haystack) + { + $rv = false; + foreach ($haystack as $k => $v) + { + foreach($v as $sk => $sv) if (trim($sv)==trim($needle)) return $k; + } + return $rv; + } + + /** + * sortByDisplayName + * + * Helper function to sort folder-objects by displayname + * @param object $a + * @param object $b, array of folderobjects + * @return int expect values (0, 1 or -1) + */ + function sortByDisplayName($a,$b) + { + // 0, 1 und -1 + return strcasecmp($a->displayName,$b->displayName); + } + + /** + * sortByAutoFolderPos + * + * Helper function to sort folder-objects by auto Folder Position + * @param object $a + * @param object $b, array of folderobjects + * @return int expect values (0, 1 or -1) + */ + function sortByAutoFolderPos($a,$b) + { + // 0, 1 und -1 + $pos1 = array_search(trim($a->shortFolderName),self::$autoFolders); + $pos2 = array_search(trim($b->shortFolderName),self::$autoFolders); + if ($pos1 == $pos2) return 0; + return ($pos1 < $pos2) ? -1 : 1; + } + + /** + * getMailBoxCounters + * + * function to retrieve the counters for a given folder + * @param string $folderName + * @return mixed false or array of counters array(MESSAGES,UNSEEN,RECENT,UIDNEXT,UIDVALIDITY) + */ + function getMailBoxCounters($folderName) + { + try + { + $folderStatus = $this->_getStatus($folderName); + //error_log(__METHOD__.' ('.__LINE__.') '." FolderStatus:".array2string($folderStatus)); + } + catch (Exception $e) + { + if (self::$debug) error_log(__METHOD__." returned FolderStatus for Folder $folderName:".$e->getMessage()); + return false; + } + if(is_array($folderStatus)) { + $status = new stdClass; + $status->messages = $folderStatus['MESSAGES']; + $status->unseen = $folderStatus['UNSEEN']; + $status->recent = $folderStatus['RECENT']; + $status->uidnext = $folderStatus['UIDNEXT']; + $status->uidvalidity = $folderStatus['UIDVALIDITY']; + + return $status; + } + return false; + } + + /** + * getMailBoxesRecursive + * + * function to retrieve mailboxes recursively from given mailbox + * @param string $_mailbox + * @param string $delimiter + * @param string $prefix + * @param string $reclevel 0, counter to keep track of the current recursionlevel + * @return array of mailboxes + */ + function getMailBoxesRecursive($_mailbox, $delimiter, $prefix, $reclevel=0) + { + #echo __METHOD__." retrieve SubFolders for $_mailbox$delimiter
"; + $maxreclevel=25; + if ($reclevel > $maxreclevel) { + error_log( __METHOD__." Recursion Level Exeeded ($reclevel) while looking up $_mailbox$delimiter "); + return array(); + } + $reclevel++; + // clean up double delimiters + $_mailbox = preg_replace('~'.($delimiter == '.' ? "\\".$delimiter:$delimiter).'+~s',$delimiter,$_mailbox); + //get that mailbox in question + $mbx = $this->icServer->getMailboxes($_mailbox,1,true); + $mbxkeys = array_keys($mbx); + #_debug_array($mbx); +//error_log(__METHOD__.' ('.__LINE__.') '.' Delimiter:'.array2string($delimiter)); +//error_log(__METHOD__.' ('.__LINE__.') '.array2string($mbx)); + // Example: Array([INBOX/GaGa] => Array([MAILBOX] => INBOX/GaGa[ATTRIBUTES] => Array([0] => \\unmarked)[delimiter] => /)) + if (is_array($mbx[$mbxkeys[0]]["ATTRIBUTES"]) && (in_array('\HasChildren',$mbx[$mbxkeys[0]]["ATTRIBUTES"]) || in_array('\Haschildren',$mbx[$mbxkeys[0]]["ATTRIBUTES"]) || in_array('\haschildren',$mbx[$mbxkeys[0]]["ATTRIBUTES"]))) { + // if there are children fetch them + //echo $mbx[$mbxkeys[0]]['MAILBOX']."
"; + unset($buff); + $buff = $this->icServer->getMailboxes($mbx[$mbxkeys[0]]['MAILBOX'].($mbx[$mbxkeys[0]]['MAILBOX'] == $prefix ? '':$delimiter),2,false); + //$buff = $this->icServer->getMailboxes($mbx[$mbxkeys[0]]['MAILBOX'],2,false); + //_debug_array($buff); + $allMailboxes = array(); + foreach ($buff as $mbxname) { +//error_log(__METHOD__.' ('.__LINE__.') '.array2string($mbxname)); + $mbxname = preg_replace('~'.($delimiter == '.' ? "\\".$delimiter:$delimiter).'+~s',$delimiter,$mbxname['MAILBOX']); + #echo "About to recur in level $reclevel:".$mbxname."
"; + if ( $mbxname != $mbx[$mbxkeys[0]]['MAILBOX'] && $mbxname != $prefix && $mbxname != $mbx[$mbxkeys[0]]['MAILBOX'].$delimiter) + { + $allMailboxes = array_merge($allMailboxes, self::getMailBoxesRecursive($mbxname, $delimiter, $prefix, $reclevel)); + } + } + if (!(in_array('\NoSelect',$mbx[$mbxkeys[0]]["ATTRIBUTES"]) || in_array('\Noselect',$mbx[$mbxkeys[0]]["ATTRIBUTES"]) || in_array('\noselect',$mbx[$mbxkeys[0]]["ATTRIBUTES"]))) $allMailboxes[] = $mbx[$mbxkeys[0]]['MAILBOX']; + return $allMailboxes; + } else { + return array($_mailbox); + } + } + + /** + * _getSpecialUseFolder + * abstraction layer for getDraftFolder, getTemplateFolder, getTrashFolder and getSentFolder + * @param string $type the type to fetch (Drafts|Template|Trash|Sent) + * @param boolean $_checkexistance, trigger check for existance + * @return mixed string or false + */ + function _getSpecialUseFolder($_type, $_checkexistance=TRUE) + { + static $types = array( + 'Drafts'=>array('prefName'=>'draftFolder','profileKey'=>'acc_folder_draft','autoFolderName'=>'Drafts'), + 'Template'=>array('prefName'=>'templateFolder','profileKey'=>'acc_folder_template','autoFolderName'=>'Templates'), + 'Trash'=>array('prefName'=>'trashFolder','profileKey'=>'acc_folder_trash','autoFolderName'=>'Trash'), + 'Sent'=>array('prefName'=>'sentFolder','profileKey'=>'acc_folder_sent','autoFolderName'=>'Sent'), + 'Junk'=>array('prefName'=>'junkFolder','profileKey'=>'acc_folder_junk','autoFolderName'=>'Junk'), + 'Outbox'=>array('prefName'=>'outboxFolder','profileKey'=>'acc_folder_outbox','autoFolderName'=>'Outbox'), + ); + if (!isset($types[$_type])) + { + error_log(__METHOD__.' ('.__LINE__.') '.' '.$_type.' not supported for '.__METHOD__); + return false; + } + if (is_null(self::$specialUseFolders) || empty(self::$specialUseFolders)) self::$specialUseFolders = $this->getSpecialUseFolders(); + + //highest precedence + try + { + $_folderName = $this->icServer->$types[$_type]['profileKey']; + } + catch (Exception $e) + { + // we know that outbox is not supported, but we use this here, as we autocreate expected SpecialUseFolders in this function + if ($_type != 'Outbox') error_log(__METHOD__.' ('.__LINE__.') '.' Failed to retrieve Folder'.$_folderName." for ".array2string($types[$_type]).":".$e->getMessage()); + $_folderName = false; + } + // does the folder exist??? (is configured/preset, but non-existent) + if ($_folderName && $_checkexistance && $_folderName !='none' && !$this->folderExists($_folderName,true)) { + try + { + $this->createFolder('', $_folderName, true); + } + catch(Exception $e) + { + error_log(__METHOD__.' ('.__LINE__.') '.' Failed to create Folder '.$_folderName." for $_type:".$e->getMessage()); + $_folderName = false; + } + } + // not sure yet if false is the correct behavior on none + if ($_folderName =='none') return false; + //no (valid) folder found yet; try specialUseFolders + if (empty($_folderName) && is_array(self::$specialUseFolders) && ($f = array_search($_type,self::$specialUseFolders))) $_folderName = $f; + //no specialUseFolder; try some Defaults + if (empty($_folderName) && isset($types[$_type])) + { + $nameSpace = $this->_getNameSpaces(); + $prefix=''; + foreach ($nameSpace as $nSp) + { + if ($nSp['type']=='personal') + { + //error_log(__METHOD__.__LINE__.array2string($nSp)); + $prefix = $nSp['prefix']; + break; + } + } + if ($this->folderExists($prefix.$types[$_type]['autoFolderName'],true)) + { + $_folderName = $prefix.$types[$_type]['autoFolderName']; + } + else + { + try + { + $this->createFolder('', $prefix.$types[$_type]['autoFolderName'], true); + $_folderName = $prefix.$types[$_type]['autoFolderName']; + } + catch(Exception $e) + { + error_log(__METHOD__.' ('.__LINE__.') '.' Failed to create Folder '.$_folderName." for $_type:".$e->getMessage()); + $_folderName = false; + } + } + } + return $_folderName; + } + + /** + * getDraftFolder wrapper for _getSpecialUseFolder Type Drafts + * @param boolean $_checkexistance, trigger check for existance + * @return mixed string or false + */ + function getJunkFolder($_checkexistance=TRUE) + { + return $this->_getSpecialUseFolder('Junk', $_checkexistance); + } + + /** + * getDraftFolder wrapper for _getSpecialUseFolder Type Drafts + * @param boolean $_checkexistance, trigger check for existance + * @return mixed string or false + */ + function getDraftFolder($_checkexistance=TRUE) + { + return $this->_getSpecialUseFolder('Drafts', $_checkexistance); + } + + /** + * getTemplateFolder wrapper for _getSpecialUseFolder Type Template + * @param boolean $_checkexistance, trigger check for existance + * @return mixed string or false + */ + function getTemplateFolder($_checkexistance=TRUE) + { + return $this->_getSpecialUseFolder('Template', $_checkexistance); + } + + /** + * getTrashFolder wrapper for _getSpecialUseFolder Type Trash + * @param boolean $_checkexistance, trigger check for existance + * @return mixed string or false + */ + function getTrashFolder($_checkexistance=TRUE) + { + return $this->_getSpecialUseFolder('Trash', $_checkexistance); + } + + /** + * getSentFolder wrapper for _getSpecialUseFolder Type Sent + * @param boolean $_checkexistance, trigger check for existance + * @return mixed string or false + */ + function getSentFolder($_checkexistance=TRUE) + { + return $this->_getSpecialUseFolder('Sent', $_checkexistance); + } + + /** + * getOutboxFolder wrapper for _getSpecialUseFolder Type Outbox + * @param boolean $_checkexistance, trigger check for existance + * @return mixed string or false + */ + function getOutboxFolder($_checkexistance=TRUE) + { + return $this->_getSpecialUseFolder('Outbox', $_checkexistance); + } + + /** + * isSentFolder is the given folder the sent folder or at least a subfolder of it + * @param string $_foldername, folder to perform the check on + * @param boolean $_checkexistance, trigger check for existance + * @return boolean + */ + function isSentFolder($_folderName, $_checkexistance=TRUE) + { + $sentFolder = $this->getSentFolder($_checkexistance); + if(empty($sentFolder)) { + return false; + } + // does the folder exist??? + if ($_checkexistance && !$this->folderExists($_folderName)) { + return false; + } + + if(false !== stripos($_folderName, $sentFolder)) { + return true; + } else { + return false; + } + } + + /** + * checks if the Outbox folder exists and is part of the foldername to be checked + * @param string $_foldername, folder to perform the check on + * @param boolean $_checkexistance, trigger check for existance + * @return boolean + */ + function isOutbox($_folderName, $_checkexistance=TRUE) + { + if (stripos($_folderName, 'Outbox')===false) { + return false; + } + // does the folder exist??? + if ($_checkexistance && $GLOBALS['egw_info']['user']['apps']['activesync'] && !$this->folderExists($_folderName)) { + $outboxFolder = $this->getOutboxFolder($_checkexistance); + if(false !== stripos($_folderName, $outboxFolder)) { + return true; + } else { + return false; + } + } + return true; + } + + /** + * isDraftFolder is the given folder the sent folder or at least a subfolder of it + * @param string $_foldername, folder to perform the check on + * @param boolean $_checkexistance, trigger check for existance + * @return boolean + */ + function isDraftFolder($_folderName, $_checkexistance=TRUE) + { + $draftFolder = $this->getDraftFolder($_checkexistance); + if(empty($draftFolder)) { + return false; + } + // does the folder exist??? + if ($_checkexistance && !$this->folderExists($_folderName)) { + return false; + } + + if(false !== stripos($_folderName, $draftFolder)) { + return true; + } else { + return false; + } + } + + /** + * isTrashFolder is the given folder the sent folder or at least a subfolder of it + * @param string $_foldername, folder to perform the check on + * @param boolean $_checkexistance, trigger check for existance + * @return boolean + */ + function isTrashFolder($_folderName, $_checkexistance=TRUE) + { + $trashFolder = $this->getTrashFolder($_checkexistance); + if(empty($trashFolder)) { + return false; + } + // does the folder exist??? + if ($_checkexistance && !$this->folderExists($_folderName)) { + return false; + } + + if(false !== stripos($_folderName, $trashFolder)) { + return true; + } else { + return false; + } + } + + /** + * isTemplateFolder is the given folder the sent folder or at least a subfolder of it + * @param string $_foldername, folder to perform the check on + * @param boolean $_checkexistance, trigger check for existance + * @return boolean + */ + function isTemplateFolder($_folderName, $_checkexistance=TRUE) + { + $templateFolder = $this->getTemplateFolder($_checkexistance); + if(empty($templateFolder)) { + return false; + } + // does the folder exist??? + if ($_checkexistance && !$this->folderExists($_folderName)) { + return false; + } + + if(false !== stripos($_folderName, $templateFolder)) { + return true; + } else { + return false; + } + } + + /** + * folderExists checks for existance of a given folder + * @param string $_foldername, folder to perform the check on + * @param boolean $_forceCheck, trigger check for existance on icServer + * @return mixed string or false + */ + function folderExists($_folder, $_forceCheck=false) + { + static $folderInfo; + $forceCheck = $_forceCheck; + if (empty($_folder)) + { + // this error is more or less without significance, unless we force the check + if ($_forceCheck===true) error_log(__METHOD__.' ('.__LINE__.') '.' Called with empty Folder:'.$_folder.function_backtrace()); + return false; + } + // reduce traffic within the Instance per User; Expire every 5 Minutes + //error_log(__METHOD__.' ('.__LINE__.') '.' Called with Folder:'.$_folder.function_backtrace()); + if (is_null($folderInfo)) $folderInfo = egw_cache::getCache(egw_cache::INSTANCE,'email','icServerFolderExistsInfo'.trim($GLOBALS['egw_info']['user']['account_id']),null,array(),$expiration=60*5); + //error_log(__METHOD__.' ('.__LINE__.') '.'Cached Info on Folder:'.$_folder.' for Profile:'.$this->profileID.($forceCheck?'(forcedCheck)':'').':'.array2string($folderInfo)); + if (!empty($folderInfo) && isset($folderInfo[$this->profileID]) && isset($folderInfo[$this->profileID][$_folder]) && $forceCheck===false) + { + //error_log(__METHOD__.' ('.__LINE__.') '.' Using cached Info on Folder:'.$_folder.' for Profile:'.$this->profileID); + return $folderInfo[$this->profileID][$_folder]; + } + else + { + if ($forceCheck === false) + { + //error_log(__METHOD__.' ('.__LINE__.') '.' No cached Info on Folder:'.$_folder.' for Profile:'.$this->profileID.' FolderExistsInfoCache:'.array2string($folderInfo[$this->profileID])); + $forceCheck = true; // try to force the check, in case there is no connection, we may need that + } + } + + // does the folder exist??? + //error_log(__METHOD__."->Connected?".$this->icServer->_connected.", ".$_folder.", ".($forceCheck?' forceCheck activated':'dont check on server')); + if ( $forceCheck || empty($folderInfo) || !isset($folderInfo[$this->profileID]) || !isset($folderInfo[$this->profileID][$_folder])) { + //error_log(__METHOD__."->NotConnected and forceCheck with profile:".$this->profileID); + //return false; + //try to connect + } + try + { + $folderInfo[$this->profileID][$_folder] = $this->icServer->mailboxExist($_folder); + } + catch (Exception $e) + { + //error_log(__METHOD__.__LINE__.$e->getMessage()); + $folderInfo[$this->profileID][$_folder] = false; + } + //error_log(__METHOD__.' ('.__LINE__.') '.' Folder Exists:'.$folderInfo[$this->profileID][$_folder].function_backtrace()); + + if(!empty($folderInfo) && isset($folderInfo[$this->profileID][$_folder]) && + $folderInfo[$this->profileID][$_folder] !== true) + { + $folderInfo[$this->profileID][$_folder] = false; // set to false, whatever it was (to have a valid returnvalue for the static return) + } + egw_cache::setCache(egw_cache::INSTANCE,'email','icServerFolderExistsInfo'.trim($GLOBALS['egw_info']['user']['account_id']),$folderInfo,$expiration=60*5); + return (!empty($folderInfo) && isset($folderInfo[$this->profileID][$_folder]) ? $folderInfo[$this->profileID][$_folder] : false); + } + + /** + * remove any messages which are marked as deleted or + * remove any messages from the trashfolder + * + * @param string _folderName the foldername + * @return nothing + */ + function compressFolder($_folderName = false) + { + $folderName = ($_folderName ? $_folderName : $this->sessionData['mailbox']); + $deleteOptions = $GLOBALS['egw_info']['user']['preferences']['mail']['deleteOptions']; + $trashFolder = $this->getTrashFolder(); + + $this->icServer->openMailbox($folderName); + + if($folderName == $trashFolder && $deleteOptions == "move_to_trash") { + $this->deleteMessages('all',$folderName,'remove_immediately'); + } else { + $this->icServer->expunge($folderName); + } + } + + /** + * delete a Message + * + * @param mixed array/string _messageUID array of ids to flag, or 'all' + * @param string _folder foldername + * @param string _forceDeleteMethod - "no", or deleteMethod like 'move_to_trash',"mark_as_deleted","remove_immediately" + * + * @return bool true, as we do not handle return values yet + */ + function deleteMessages($_messageUID, $_folder=NULL, $_forceDeleteMethod='no') + { + //error_log(__METHOD__.' ('.__LINE__.') '.'->'.array2string($_messageUID).','.array2string($_folder).', '.$_forceDeleteMethod); + $msglist = ''; + $oldMailbox = ''; + if (is_null($_folder) || empty($_folder)) $_folder = $this->sessionData['mailbox']; + if (empty($_messageUID)) + { + if (self::$debug) error_log(__METHOD__." no messages Message(s): ".implode(',',$_messageUID)); + return false; + } + elseif ($_messageUID==='all') + { + $_messageUID= null; + } + else + { + $uidsToDelete = new Horde_Imap_Client_Ids(); + if (!(is_object($_messageUID) || is_array($_messageUID))) $_messageUID = (array)$_messageUID; + $uidsToDelete->add($_messageUID); + } + $deleteOptions = $_forceDeleteMethod; // use forceDeleteMethod if not "no", or unknown method + if ($_forceDeleteMethod === 'no' || !in_array($_forceDeleteMethod,array('move_to_trash',"mark_as_deleted","remove_immediately"))) $deleteOptions = ($this->mailPreferences['deleteOptions']?$this->mailPreferences['deleteOptions']:"mark_as_deleted"); + //error_log(__METHOD__.' ('.__LINE__.') '.'->'.array2string($_messageUID).','.$_folder.'/'.$this->sessionData['mailbox'].' Option:'.$deleteOptions); + $trashFolder = $this->getTrashFolder(); + $draftFolder = $this->getDraftFolder(); //$GLOBALS['egw_info']['user']['preferences']['mail']['draftFolder']; + $templateFolder = $this->getTemplateFolder(); //$GLOBALS['egw_info']['user']['preferences']['mail']['templateFolder']; + if(($_folder == $trashFolder && $deleteOptions == "move_to_trash") || + ($_folder == $draftFolder)) { + $deleteOptions = "remove_immediately"; + } + if($this->icServer->getCurrentMailbox() != $_folder) { + $oldMailbox = $this->icServer->getCurrentMailbox(); + $this->icServer->openMailbox($_folder); + } + //error_log(__METHOD__.' ('.__LINE__.') '.'->'.array2string($_messageUID).','.$_folder.'/'.$this->sessionData['mailbox'].' Option:'.$deleteOptions); + $updateCache = false; + switch($deleteOptions) { + case "move_to_trash": + //error_log(__METHOD__.' ('.__LINE__.') '); + $updateCache = true; + if(!empty($trashFolder)); { + if (self::$debug) error_log(__METHOD__.' ('.__LINE__.') '.implode(' : ', $_messageUID)); + if (self::$debug) error_log(__METHOD__.' ('.__LINE__.') '."$trashFolder <= $_folder / ". $this->sessionData['mailbox']); + // copy messages + try + { + $retValue = $this->icServer->copy($_folder, $trashFolder, array('ids'=>$uidsToDelete,'move'=>true)); + } + catch (Exception $e) + { + throw new egw_exception("Failed to move Messages (".array2string($uidsToDelete).") from Folder $_folder to $trashFolder Error:".$e->getMessage()); + } + } + break; + + case "mark_as_deleted": + //error_log(__METHOD__.' ('.__LINE__.') '); + // mark messages as deleted + if (is_null($_messageUID)) $_messageUID='all'; + foreach((array)$_messageUID as $key =>$uid) + { + //flag messages, that are flagged for deletion as seen too + $this->flagMessages('read', $uid, $_folder); + $flags = $this->getFlags($uid); + $this->flagMessages('delete', $uid, $_folder); + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($flags)); + if (strpos( array2string($flags),'Deleted')!==false) $undelete[] = $uid; + unset($flags); + } + foreach((array)$undelete as $key =>$uid) + { + $this->flagMessages('undelete', $uid, $_folder); + } + break; + + case "remove_immediately": + //error_log(__METHOD__.' ('.__LINE__.') '); + $updateCache = true; + if (is_null($_messageUID)) $_messageUID='all'; + if (is_object($_messageUID)) + { + $this->flagMessages('delete', $_messageUID, $_folder); + } + else + { + foreach((array)$_messageUID as $key =>$uid) + { + //flag messages, that are flagged for deletion as seen too + $this->flagMessages('delete', $uid, $_folder); + } + } + // delete the messages finaly + $this->icServer->expunge($_folder); + break; + } + if($oldMailbox != '') { + $this->icServer->openMailbox($oldMailbox); + } + + return true; + } + + /** + * get flags for a Message + * + * @param mixed string _messageUID array of id to retrieve the flags for + * + * @return null/array flags + */ + function getFlags ($_messageUID) { + try + { + $uidsToFetch = new Horde_Imap_Client_Ids(); + if (!(is_object($_messageUID) || is_array($_messageUID))) $_messageUID = (array)$_messageUID; + $uidsToFetch->add($_messageUID); + $_folderName = $this->icServer->getCurrentMailbox(); + $fquery = new Horde_Imap_Client_Fetch_Query(); + $fquery->flags(); + $headersNew = $this->icServer->fetch($_folderName, $fquery, array( + 'ids' => $uidsToFetch, + )); + if (is_object($headersNew)) { + foreach($headersNew->ids() as $id) { + $_headerObject = $headersNew->get($id); + $flags = $_headerObject->getFlags(); + } + } + } + catch (Exception $e) + { + error_log(__METHOD__.' ('.__LINE__.') '."Failed to fetch flags for ".array2string($_messageUID)." Error:".$e->getMessage()); + return null; + //throw new egw_exception("Failed to fetch flags for ".array2string($_messageUID)" Error:".$e->getMessage()); + } + return $flags; + } + + /** + * get and parse the flags response for the Notifyflag for a Message + * + * @param string _messageUID array of id to retrieve the flags for + * @param array flags - to avoid additional server call + * + * @return null/boolean + */ + function getNotifyFlags ($_messageUID, $flags=null) + { + if (self::$debug) error_log(__METHOD__.$_messageUID.' Flags:'.array2string($flags)); + try + { + if($flags===null) $flags = $this->getFlags($_messageUID); + } + catch (Exception $e) + { + return null; + } + + if ( stripos( array2string($flags),'MDNSent')!==false) + return true; + + if ( stripos( array2string($flags),'MDNnotSent')!==false) + return false; + + return null; + } + + /** + * flag a Message + * + * @param string _flag (readable name) + * @param mixed array/string _messageUID array of ids to flag, or 'all' + * @param string _folder foldername + * + * @todo handle handle icserver->setFlags returnValue + * + * @return bool true, as we do not handle icserver->setFlags returnValue + */ + function flagMessages($_flag, $_messageUID,$_folder=NULL) + { + //error_log(__METHOD__.' ('.__LINE__.') '.'->' .$_flag." ".array2string($_messageUID).",$_folder /".$this->sessionData['mailbox']); + if (empty($_messageUID)) + { + if (self::$debug) error_log(__METHOD__." no messages Message(s): ".implode(',',$_messageUID)); + return false; + } + elseif ($_messageUID==='all') + { + $uidsToModify= null; + } + else + { + $uidsToModify = new Horde_Imap_Client_Ids(); + if (!(is_object($_messageUID) || is_array($_messageUID))) $_messageUID = (array)$_messageUID; + $uidsToModify->add($_messageUID); + } + + $this->icServer->openMailbox(($_folder?$_folder:$this->sessionData['mailbox'])); + $folder = $this->icServer->getCurrentMailbox();; + switch($_flag) { + case "delete": + $ret = $this->icServer->store($folder, array('add'=>array('\\Deleted'), 'ids'=> $uidsToModify)); + break; + case "undelete": + $ret = $this->icServer->store($folder, array('remove'=>array('\\Deleted'), 'ids'=> $uidsToModify)); + break; + case "flagged": + $ret = $this->icServer->store($folder, array('add'=>array('\\Flagged'), 'ids'=> $uidsToModify)); + break; + case "read": + case "seen": + $ret = $this->icServer->store($folder, array('add'=>array('\\Seen'), 'ids'=> $uidsToModify)); + break; + case "forwarded": + $ret = $this->icServer->store($folder, array('add'=>array('$Forwarded'), 'ids'=> $uidsToModify)); + case "answered": + $ret = $this->icServer->store($folder, array('add'=>array('\\Answered'), 'ids'=> $uidsToModify)); + break; + case "unflagged": + $ret = $this->icServer->store($folder, array('remove'=>array('\\Flagged'), 'ids'=> $uidsToModify)); + break; + case "unread": + case "unseen": + $ret = $this->icServer->store($folder, array('remove'=>array('\\Seen','\\Answered','$Forwarded'), 'ids'=> $uidsToModify)); + break; + case "mdnsent": + $ret = $this->icServer->store($folder, array('add'=>array('MDNSent'), 'ids'=> $uidsToModify)); + break; + case "mdnnotsent": + $ret = $this->icServer->store($folder, array('add'=>array('MDNnotSent'), 'ids'=> $uidsToModify)); + break; + case "label1": + case "labelone": + $ret = $this->icServer->store($folder, array('add'=>array('$label1'), 'ids'=> $uidsToModify)); + break; + case "unlabel1": + case "unlabelone": + $ret = $this->icServer->store($folder, array('remove'=>array('$label1'), 'ids'=> $uidsToModify)); + break; + case "label2": + case "labeltwo": + $ret = $this->icServer->store($folder, array('add'=>array('$label2'), 'ids'=> $uidsToModify)); + break; + case "unlabel2": + case "unlabeltwo": + $ret = $this->icServer->store($folder, array('remove'=>array('$label2'), 'ids'=> $uidsToModify)); + break; + case "label3": + case "labelthree": + $ret = $this->icServer->store($folder, array('add'=>array('$label3'), 'ids'=> $uidsToModify)); + break; + case "unlabel3": + case "unlabelthree": + $ret = $this->icServer->store($folder, array('remove'=>array('$label3'), 'ids'=> $uidsToModify)); + break; + case "label4": + case "labelfour": + $ret = $this->icServer->store($folder, array('add'=>array('$label4'), 'ids'=> $uidsToModify)); + break; + case "unlabel4": + case "unlabelfour": + $ret = $this->icServer->store($folder, array('remove'=>array('$label4'), 'ids'=> $uidsToModify)); + break; + case "label5": + case "labelfive": + $ret = $this->icServer->store($folder, array('add'=>array('$label5'), 'ids'=> $uidsToModify)); + break; + case "unlabel5": + case "unlabelfive": + $ret = $this->icServer->store($folder, array('remove'=>array('$label5'), 'ids'=> $uidsToModify)); + break; + case "unlabel": + $ret = $this->icServer->store($folder, array('remove'=>array('$label1'), 'ids'=> $uidsToModify)); + $ret = $this->icServer->store($folder, array('remove'=>array('$label2'), 'ids'=> $uidsToModify)); + $ret = $this->icServer->store($folder, array('remove'=>array('$label3'), 'ids'=> $uidsToModify)); + $ret = $this->icServer->store($folder, array('remove'=>array('$label4'), 'ids'=> $uidsToModify)); + $ret = $this->icServer->store($folder, array('remove'=>array('$label5'), 'ids'=> $uidsToModify)); + break; + } + + self::$folderStatusCache[$this->profileID][(!empty($_folder)?$_folder: $this->sessionData['mailbox'])]['uidValidity'] = 0; + + //error_log(__METHOD__.' ('.__LINE__.') '.'->' .$_flag." ".array2string($_messageUID).",".($_folder?$_folder:$this->sessionData['mailbox'])); + return true; // as we do not catch/examine setFlags returnValue + } + + /** + * move Message(s) + * + * @param string _foldername target folder + * @param mixed array/string _messageUID array of ids to flag, or 'all' + * @param boolean $deleteAfterMove - decides if a mail is moved (true) or copied (false) + * @param string $currentFolder + * @param boolean $returnUIDs - control wether or not the action called should return the new uids + * caveat: not all servers do support that + * @param int $_targetProfileID - target profile ID, should only be handed over when target server is differen from source + * + * @return mixed/bool true,false or new uid + */ + function moveMessages($_foldername, $_messageUID, $deleteAfterMove=true, $currentFolder = Null, $returnUIDs = false, $_targetProfileID = Null) + { + $msglist = ''; + + $deleteOptions = $GLOBALS['egw_info']["user"]["preferences"]["mail"]["deleteOptions"]; + if (empty($_messageUID)) + { + if (self::$debug) error_log(__METHOD__." no messages Message(s): ".implode(',',$_messageUID)); + return false; + } + elseif ($_messageUID==='all') + { + $uidsToMove= null; + } + else + { + $uidsToMove = new Horde_Imap_Client_Ids(); + if (!(is_object($_messageUID) || is_array($_messageUID))) $_messageUID = (array)$_messageUID; + $uidsToMove->add($_messageUID); + } + $sourceFolder = (!empty($currentFolder)?$currentFolder: $this->sessionData['mailbox']); + if (!is_null($_targetProfileID) && $_targetProfileID !== $this->icServer->ImapServerId) + { + $sourceFolder = $this->icServer->getMailbox($sourceFolder); + $target = emailadmin_account::read($_targetProfileID)->imapServer(); + $foldername = $target->getMailbox($_foldername); + //error_log(__METHOD__.' ('.__LINE__.') '.' Sourceserver:'.$this->icServer->ImapServerId.' TargetServer:'.$_targetProfileID.' TargetFolderObject:'.array2string($foldername)); + $this->icServer->openMailbox($sourceFolder); + $uidsToFetch = new Horde_Imap_Client_Ids(); + $uidsToFetch->add($_messageUID); + + $fquery = new Horde_Imap_Client_Fetch_Query(); + $fquery->fullText(array('peek'=>true)); + $fquery->flags(); + $headersNew = $this->icServer->fetch($sourceFolder, $fquery, array( + 'ids' => $uidsToFetch, + )); + if (is_object($headersNew)) { + $c=0; + $retUid = new Horde_Imap_Client_Ids(); + // make sure the target folder is open and ready + $target->openMailbox($foldername); + // we copy chunks of 5 to avoid too much memory and/or server stress + foreach($headersNew as $id=>$_headerObject) { + $c++; + $flags = $_headerObject->getFlags(); //unseen status seems to be lost when retrieving the full message + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($flags)); + $body = $_headerObject->getFullMsg(); + $dataNflags[] = array('data'=>array(array('t'=>'text','v'=>"$body")), 'flags'=>$flags); + if ($c==5) + { + $ret = $target->append($foldername,$dataNflags); + $retUid->add($ret); + unset($dataNflags); + time_nanosleep(0,500000);// sleep 500 miliseconds + $c=0; + } + } + if (isset($dataNflags)) + { + $ret = $target->append($foldername,$dataNflags); + $retUid->add($ret); + unset($dataNflags); + } + // make sure we are back to source + $this->icServer->openMailbox($sourceFolder); + if ($deleteAfterMove) + { + $this->deleteMessages($_messageUID, $sourceFolder, $_forceDeleteMethod='remove_immediately'); + } + } + } + else + { + try + { + $retUid = $this->icServer->copy($sourceFolder, $_foldername, array('ids'=>$uidsToMove,'move'=>$deleteAfterMove)); + } + catch (exception $e) + { + error_log(__METHOD__.' ('.__LINE__.') '."Copying to Folder $_foldername failed! Error:".$e->getMessage()); + throw new egw_exception("Copying to Folder $_foldername failed! Error:".$e->getMessage()); + return false; + } + } + + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($retUid)); + return ($returnUIDs ? $retUid : true); + } + + /** + * Helper function to handle wrong or unrecognized timezones + * returns the date as it is parseable by strtotime, or current timestamp if everything failes + * @param string date to be parsed/formatted + * @param string format string, if none is passed, use the users common dateformat supplemented by the time hour:minute:second + * @return string returns the date as it is parseable by strtotime, or current timestamp if everything failes + */ + static function _strtotime($date='',$format=NULL,$convert2usertime=false) + { + if ($format==NULL) $format = $GLOBALS['egw_info']['user']['preferences']['common']['dateformat'].' '.($GLOBALS['egw_info']['user']['preferences']['common']['timeformat']==12?'h:i:s a':'H:i:s'); + $date2return = ($convert2usertime ? egw_time::server2user($date,$format) : egw_time::to($date,$format)); + if ($date2return==null) + { + $dtarr = explode(' ',$date); + $test = null; + while ($test===null && count($dtarr)>=1) + { + array_pop($dtarr); + $test= ($convert2usertime ? egw_time::server2user(implode(' ',$dtarr),$format): egw_time::to(implode(' ',$dtarr),$format)); + if ($test) $date2return = $test; + } + if ($test===null) $date2return = egw_time::to('now',$format); + } + return $date2return; + } + + /** + * htmlentities + * helperfunction to cope with wrong encoding in strings + * @param string $_string input to be converted + * @param mixed $charset false or string -> Target charset, if false emailadmin_imapbase displayCharset will be used + * @return string + */ + static function htmlentities($_string, $_charset=false) + { + //setting the charset (if not given) + if ($_charset===false) $_charset = self::$displayCharset; + $_stringORG = $_string; + $_string = @htmlentities($_string,ENT_QUOTES,$_charset, false); + if (empty($_string) && !empty($_stringORG)) $_string = @htmlentities(translation::convert($_stringORG,translation::detect_encoding($_stringORG),$_charset),ENT_QUOTES | ENT_IGNORE,$_charset, false); + return $_string; + } + + /** + * htmlspecialchars + * helperfunction to cope with wrong encoding in strings; + * seems to be outdated and not needed any more for et2 + * @param string $_string input to be converted + * @param mixed $charset false or string -> Target charset, if false mail displayCharset will be used + * @return string + */ + static function htmlspecialchars($_string, $_charset=false) + { + return $_string; +/* + //setting the charset (if not given) + if ($_charset===false) $_charset = self::$displayCharset; + $_stringORG = $_string; + $_string = @htmlspecialchars($_string,ENT_QUOTES,$_charset, false); + if (empty($_string) && !empty($_stringORG)) $_string = @htmlspecialchars(translation::convert($_stringORG,translation::detect_encoding($_stringORG),$_charset),ENT_QUOTES | ENT_IGNORE,$_charset, false); + return $_string; +*/ + } + + /** + * clean a message from elements regarded as potentially harmful + * param string/reference $_html is the text to be processed + * param boolean $usepurify - obsolet, as we always use htmlLawed + * param boolean $cleanTags - use tidy (if available) to clean/balance tags + * return nothing + */ + static function getCleanHTML(&$_html, $usepurify = false, $cleanTags=true) + { + // remove CRLF and TAB as it is of no use in HTML. + // but they matter in
, so we rather don't
+		//$_html = str_replace("\r\n",' ',$_html);
+		//$_html = str_replace("\t",' ',$_html);
+		//error_log($_html);
+		//repair doubleencoded ampersands, and some stuff htmLawed stumbles upon with balancing switched on
+		$_html = str_replace(array('&amp;','

',"
 
",'
 
','','
','','','',''), + array('&', '
', '
', '
', '','', '', '', '', ''),$_html); + //$_html = str_replace(array('&amp;'),array('&'),$_html); + if (stripos($_html,'style')!==false) translation::replaceTagsCompletley($_html,'style'); // clean out empty or pagewide style definitions / left over tags + if (stripos($_html,'head')!==false) translation::replaceTagsCompletley($_html,'head'); // Strip out stuff in head + //if (stripos($_html,'![if')!==false && stripos($_html,'')!==false) translation::replaceTagsCompletley($_html,'!\[if','',false); // Strip out stuff in ifs + //if (stripos($_html,'!--[if')!==false && stripos($_html,'')!==false) translation::replaceTagsCompletley($_html,'!--\[if','',false); // Strip out stuff in ifs + //error_log(__METHOD__.' ('.__LINE__.') '.$_html); + // force the use of kses, as it is still have the edge over purifier with some stuff + $usepurify = true; + if ($usepurify) + { + // we need a customized config, as we may allow external images, $GLOBALS['egw_info']['user']['preferences']['mail']['allowExternalIMGs'] + if (get_magic_quotes_gpc() === 1) $_html = stripslashes($_html); + // Strip out doctype in head, as htmlLawed cannot handle it TODO: Consider extracting it and adding it afterwards + if (stripos($_html,'!doctype')!==false) translation::replaceTagsCompletley($_html,'!doctype'); + if (stripos($_html,'?xml:namespace')!==false) translation::replaceTagsCompletley($_html,'\?xml:namespace','/>',false); + if (stripos($_html,'?xml version')!==false) translation::replaceTagsCompletley($_html,'\?xml version','\?>',false); + if (strpos($_html,'!CURSOR')!==false) translation::replaceTagsCompletley($_html,'!CURSOR'); + // htmLawed filter only the 'body' + //preg_match('`(]*>)(.+?)(.*?)`ims', $_html, $matches); + //if ($matches[2]) + //{ + // $hasOther = true; + // $_html = $matches[2]; + //} + // purify got switched to htmLawed + // some testcode to test purifying / htmlawed + //$_html = "
hi
there
kram
".$_html; + $_html = html::purify($_html,self::$htmLawed_config,array(),true); + //if ($hasOther) $_html = $matches[1]. $_html. $matches[3]; + // clean out comments , should not be needed as purify should do the job. + $search = array( + '@url\(http:\/\/[^\)].*?\)@si', // url calls e.g. in style definitions + '@@', // Strip multi-line comments including CDATA + ); + $_html = preg_replace($search,"",$_html); + // remove non printable chars + $_html = preg_replace('/([\000-\012])/','',$_html); + //error_log(__METHOD__.':'.__LINE__.':'.$_html); + } + // using purify above should have tidied the tags already sufficiently + if ($usepurify == false && $cleanTags==true) + { + if (extension_loaded('tidy')) + { + $tidy = new tidy(); + $cleaned = $tidy->repairString($_html, self::$tidy_config,'utf8'); + // Found errors. Strip it all so there's some output + if($tidy->getStatus() == 2) + { + error_log(__METHOD__.' ('.__LINE__.') '.' ->'.$tidy->errorBuffer); + } + else + { + $_html = $cleaned; + } + } + else + { + //$to = ini_get('max_execution_time'); + //@set_time_limit(10); + $htmLawed = new egw_htmLawed(); + $_html = $htmLawed->egw_htmLawed($_html); + //error_log(__METHOD__.' ('.__LINE__.') '.$_html); + //@set_time_limit($to); + } + } + } + + /** + * Header and Bodystructure stuff + */ + + /** + * getMimePartCharset - fetches the charset mimepart if it exists + * @param $_mimePartObject structure object + * @return mixed mimepart or false if no CHARSET is found, the missing charset has to be handled somewhere else, + * as we cannot safely assume any charset as we did earlier + */ + function getMimePartCharset($_mimePartObject) + { + //$charSet = 'iso-8859-1';//self::$displayCharset; //'iso-8859-1'; // self::displayCharset seems to be asmarter fallback than iso-8859-1 + $CharsetFound=false; + //echo "#".$_mimePartObject->encoding.'#
'; + if(is_array($_mimePartObject->parameters)) { + if(isset($_mimePartObject->parameters['CHARSET'])) { + $charSet = $_mimePartObject->parameters['CHARSET']; + $CharsetFound=true; + } + } + // this one is dirty, but until I find something that does the trick of detecting the encoding, .... + //if ($CharsetFound == false && $_mimePartObject->encoding == "QUOTED-PRINTABLE") $charSet = 'iso-8859-1'; //assume quoted-printable to be ISO + //if ($CharsetFound == false && $_mimePartObject->encoding == "BASE64") $charSet = 'utf-8'; // assume BASE64 to be UTF8 + return ($CharsetFound ? $charSet : $CharsetFound); + } + + /** + * decodeMimePart - fetches the charset mimepart if it exists + * @param string $_mimeMessage - the message to be decoded + * @param string $_encoding - the encoding used BASE64 and QUOTED-PRINTABLE is supported + * @param string $_charset - not used + * @return string decoded mimePart + */ + function decodeMimePart($_mimeMessage, $_encoding, $_charset = '') + { + // decode the part + if (self::$debug) error_log(__METHOD__."() with $_encoding and $_charset:".print_r($_mimeMessage,true)); + switch (strtoupper($_encoding)) + { + case 'BASE64': + // use imap_base64 to decode, not any longer, as it is strict, and fails if it encounters invalid chars + return base64_decode($_mimeMessage); + break; + case 'QUOTED-PRINTABLE': + // use imap_qprint to decode + return quoted_printable_decode($_mimeMessage); + break; + case 'WEDONTKNOWTHEENCODING': + // try base64 + $r = base64_decode($_mimeMessage); + if (json_encode($r)) + { + return $r; + } + //we do not know the encoding, so we do not decode + default: + // it is either not encoded or we don't know about it + return $_mimeMessage; + break; + } + } + + /** + * getMultipartAlternative + * get part of the message, if its stucture is indicating its of multipart alternative style + * a wrapper for multipartmixed + * @param string/int $_uid the messageuid, + * @param Horde_Mime_Part $_structure structure for parsing + * @param string $_htmlMode, how to display a message, html, plain text, ... + * @param boolean $_preserveSeen flag to preserve the seenflag by using body.peek + * @return array containing the desired part + */ + function getMultipartAlternative($_uid, Horde_Mime_Part $_structure, $_htmlMode, $_preserveSeen = false) + { + // a multipart/alternative has exactly 2 parts (text and html OR text and something else) + // sometimes there are 3 parts, when there is an ics/ical attached/included-> we want to show that + // as attachment AND as abstracted ical information (we use our notification style here). + $partText = $partCalendar = $partHTML = null; + if (self::$debug) _debug_array(array("METHOD"=>__METHOD__,"LINE"=>__LINE__,"STRUCTURE"=>$_structure)); + //error_log(__METHOD__.' ('.__LINE__.') '); + $ignore_first_part = true; + foreach($_structure->contentTypeMap() as $mime_id => $mime_type) + { + //error_log(__METHOD__."($_uid, ".$_structure->getMimeId().") $mime_id: $mime_type"." ignoreFirstPart:".$ignore_first_part); + if (self::$debug) echo __METHOD__."($_uid, partID=".$_structure->getMimeId().") $mime_id: $mime_type
"; + + if ($ignore_first_part) + { + $ignore_first_part = false; + continue; // ignore multipart/alternative itself + } + + $mimePart = $_structure->getPart($mime_id); + + switch($mimePart->getPrimaryType()) + { + case 'text': + switch($mimePart->getSubType()) + { + case 'calendar': // only if there is no partText set already + //if ($partText) break; + if ($mimePart->getBytes() > 0) $partCalendar = $mimePart; + break; + // fall throught + case 'plain': + if ($mimePart->getBytes() > 0) $partText = $mimePart; + break; + + case 'html': + if ($mimePart->getBytes() > 0) $partHTML = $mimePart; + break; + } + break; + + case 'multipart': + switch($mimePart->getSubType()) + { + case 'related': + case 'mixed': + if (count($mimePart->getParts()) > 1) + { + // in a multipart alternative we treat the multipart/related as html part + if (self::$debug) error_log(__METHOD__." process MULTIPART/".$mimePart->getSubType()." with array as subparts"); + $partHTML = $mimePart; + break 3; // GET OUT OF LOOP, will be processed according to type + } + break; + case 'alternative': + if (count($mimePart->getParts()) > 1) + { + //cascading multipartAlternative structure, assuming only the first one is to be used + return $this->getMultipartAlternative($_uid, $mimePart, $_htmlMode, $_preserveSeen); + } + } + } + } + // seems we have a multipart/alternative; as we assume we are able to display events + // the text/calendar part has higher precedence then text/plain + if ($partCalendar && $partText) $partText = $partCalendar; + + switch($_htmlMode) + { + case 'html_only': + case 'always_display': + if ($partHTML) + { + switch($partHTML->getSubType()) + { + case 'related': + return $this->getMultipartRelated($_uid, $partHTML, $_htmlMode, $_preserveSeen); + + case 'mixed': + return $this->getMultipartMixed($_uid, $partHTML, $_htmlMode, $_preserveSeen); + + default: + return $this->getTextPart($_uid, $partHTML, $_htmlMode, $_preserveSeen); + } + } + elseif ($partText && $_htmlMode=='always_display') + { + return $this->getTextPart($_uid, $partText, $_htmlMode, $_preserveSeen); + } + break; + + case 'only_if_no_text': + if ($partText) + { + return $this->getTextPart($_uid, $partText, $_htmlMode, $_preserveSeen); + } + if ($partHTML) + { + if ($partHTML->getPrimaryType()) + { + return $this->getMultipartRelated($_uid, $partHTML, $_htmlMode, $_preserveSeen); + } + return $this->getTextPart($_uid, $partHTML, 'always_display', $_preserveSeen); + } + break; + + default: + if ($partText) + { + return $this->getTextPart($_uid, $partText, $_htmlMode, $_preserveSeen); + } + $bodyPart = array( + 'body' => lang("no plain text part found"), + 'mimeType' => 'text/plain', + 'charSet' => self::$displayCharset, + ); + break; + } + return $bodyPart; + } + + /** + * Get part of the message, if its stucture is indicating its of multipart mixed style + * + * @param int $_uid the messageuid, + * @param Horde_Mime_Part $_structure='', if given use structure for parsing + * @param string $_htmlMode, how to display a message, html, plain text, ... + * @param boolean $_preserveSeen flag to preserve the seenflag by using body.peek + * @return array containing the desired part + */ + function getMultipartMixed($_uid, Horde_Mime_Part $_structure, $_htmlMode, $_preserveSeen = false) + { + if (self::$debug) echo __METHOD__."$_uid, $_htmlMode
"; + $bodyPart = array(); + if (self::$debug) _debug_array($_structure); + + $ignore_first_part = true; + $skipParts = array(); + foreach($_structure->contentTypeMap() as $mime_id => $mime_type) + { + //error_log(__METHOD__."($_uid, ".$_structure->getMimeId().") $mime_id: $mime_type"); + if (self::$debug) echo __METHOD__."($_uid, partID=".$_structure->getMimeId().") $mime_id: $mime_type
"; + if ($ignore_first_part) + { + $ignore_first_part = false; + //error_log(__METHOD__."($_uid, ".$_structure->getMimeId().") SKIPPED FirstPart $mime_id: $mime_type"); + continue; // ignore multipart/mixed itself + } + if (array_key_exists($mime_id,$skipParts)) + { + //error_log(__METHOD__."($_uid, ".$_structure->getMimeId().") SKIPPED $mime_id: $mime_type"); + continue; + } + + $part = $_structure->getPart($mime_id); + + switch($part->getPrimaryType()) + { + case 'multipart': + if ($part->getDisposition() == 'attachment') continue; + switch($part->getSubType()) + { + case 'alternative': + return array($this->getMultipartAlternative($_uid, $part, $_htmlMode, $_preserveSeen)); + + case 'mixed': + case 'signed': + $bodyPart = array_merge($bodyPart, $this->getMultipartMixed($_uid, $part, $_htmlMode, $_preserveSeen)); + break; + + case 'related': + $bodyPart = array_merge($bodyPart, $this->getMultipartRelated($_uid, $part, $_htmlMode, $_preserveSeen)); + break; + } + break; + + case 'text': + switch($part->getSubType()) + { + case 'plain': + case 'html': + case 'calendar': // inline ics/ical files + if($part->getDisposition() != 'attachment') + { + $bodyPart[] = $this->getTextPart($_uid, $part, $_htmlMode, $_preserveSeen); + } + //error_log(__METHOD__.' ('.__LINE__.') '.' ->'.$part->type."/".$part->subType.' -> BodyPart:'.array2string($bodyPart[count($bodyPart)-1])); + break; + } + break; + + case 'message': + //skip attachments + if($part->getSubType() == 'delivery-status' && $part->getDisposition() != 'attachment') + { + $bodyPart[] = $this->getTextPart($_uid, $part, $_htmlMode, $_preserveSeen); + } + // do not descend into attached Messages + if($part->getSubType() == 'rfc822' || $part->getDisposition() == 'attachment') + { + $skipParts[$mime_id.'.0'] = $mime_type; + foreach($part->contentTypeMap() as $sub_id => $sub_type) $skipParts[$sub_id] = $sub_type; + //error_log(__METHOD__.' ('.__LINE__.') '.' Uid:'.$_uid.' Part:'.$mime_id.':'.array2string($skipParts)); + //break 2; + } + break; + + default: + // do nothing + // the part is a attachment + } + } + return $bodyPart; + } + + /** + * getMultipartRelated + * get part of the message, if its stucture is indicating its of multipart related style + * a wrapper for multipartmixed + * @param string/int $_uid the messageuid, + * @param Horde_Mime_Part $_structure, if given use structure for parsing + * @param string $_htmlMode, how to display a message, html, plain text, ... + * @param boolean $_preserveSeen flag to preserve the seenflag by using body.peek + * @return array containing the desired part + */ + function getMultipartRelated($_uid, Horde_Mime_Part $_structure, $_htmlMode, $_preserveSeen = false) + { + return $this->getMultipartMixed($_uid, $_structure, $_htmlMode, $_preserveSeen); + } + + /** + * Fetch a body part + * + * @param int $_uid + * @param string $_partID=null + * @param string $_folder=null + * @param boolean $_preserveSeen=false + * @param boolean $_stream=false true return a stream, false return string + * @param string &$_encoding=null on return: transfer encoding of returned part + * @return string|resource + */ + function getBodyPart($_uid, $_partID=null, $_folder=null, $_preserveSeen=false, $_stream=false, &$_encoding=null) + { + if (self::$debug) error_log( __METHOD__."($_uid, $_partID, $_folder, $_preserveSeen)"); + + if (empty($_folder)) + { + $_folder = ($this->sessionData['mailbox']? $this->sessionData['mailbox'] : $this->icServer->getCurrentMailbox()); + } + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($_folder).'/'.$this->icServer->getCurrentMailbox().'/'. $this->sessionData['mailbox']); + // querying contents of body part + $uidsToFetch = new Horde_Imap_Client_Ids(); + if (!(is_object($_uid) || is_array($_uid))) $_uid = (array)$_uid; + $uidsToFetch->add($_uid); + + $fquery = new Horde_Imap_Client_Fetch_Query(); + $fquery->bodyPart($_partID, array( + 'peek' => $_preserveSeen, + 'decode' => true, // try decode on server, does NOT neccessary work + )); + + $part = $this->icServer->fetch($_folder, $fquery, array( + 'ids' => $uidsToFetch, + ))->first(); + + if (!$part) return null; + + $_encoding = $part->getBodyPartDecode($_partID); + + return $part->getBodyPart($_partID, $_stream); + } + + /** + * Get Body from message + * + * @param int $_uid the messageuid + * @param Horde_Mime_Part $_structure=null, if given use structure for parsing + * @param string $_htmlMode how to display a message: 'html_only', 'always_display', 'only_if_no_text' or '' + * @param boolean $_preserveSeen flag to preserve the seenflag by using body.peek + * @param boolean $_stream=false true return a stream, false return string + * @return array containing the desired text part, mimeType and charset + */ + function getTextPart($_uid, Horde_Mime_Part $_structure, $_htmlMode='', $_preserveSeen=false, $_stream=false) + { + //error_log(__METHOD__.' ('.__LINE__.') '.'->'.$_uid.':'.array2string($_structure).' '.function_backtrace()); + $bodyPart = array(); + if (self::$debug) _debug_array(array($_structure,function_backtrace())); + + if($_structure->getSubType() == 'html' && !in_array($_htmlMode, array('html_only', 'always_display', 'only_if_no_text'))) + { + $bodyPart = array( + 'error' => 1, + 'body' => lang("displaying html messages is disabled"), + 'mimeType' => 'text/html', + 'charSet' => self::$displayCharset, + ); + } + elseif ($_structure->getSubType() == 'plain' && $_htmlMode == 'html_only') + { + $bodyPart = array( + 'error' => 1, + 'body' => lang("displaying plain messages is disabled"), + 'mimeType' => 'text/plain', // make sure we do not return mimeType text/html + 'charSet' => self::$displayCharset, + ); + } + else + { + // some Servers append PropertyFile___ ; strip that here for display + // RB: not sure what this is: preg_replace('/PropertyFile___$/','',$this->decodeMimePart($mimePartBody, $_structure->encoding, $this->getMimePartCharset($_structure))), + $this->fetchPartContents($_uid, $_structure, $_stream, $_preserveSeen); + + $bodyPart = array( + 'body' => $_structure->getContents(array( + 'stream' => $_stream, + )), + 'mimeType' => $_structure->getType() == 'text/html' ? 'text/html' : 'text/plain', + 'charSet' => $_structure->getCharset(), + ); +/* RB: not sure this is still necessary + if ($_structure->type == 'TEXT' && $_structure->subType == 'PLAIN' && + is_array($_structure->parameters) && isset($_structure->parameters['FORMAT']) && + trim(strtolower($_structure->parameters['FORMAT']))=='flowed' + ) + { + if (self::$debug) error_log(__METHOD__.' ('.__LINE__.') '." detected TEXT/PLAIN Format:flowed -> removing leading blank ('\r\n ') per line"); + $bodyPart['body'] = str_replace("\r\n ","\r\n", $bodyPart['body']); + } +*/ + if ($_structure->getSubType() == 'calendar') + { + $bodyPart['body'] = $this->getEvent($_structure->getContents(), $_structure->getContentTypeParameter('METHOD')); + } + } + return $bodyPart; + } + + /** + * Return inline ical as html + * + * @param string $ical iCal data + * @param string $method iTip method eg. 'REPLY' + * @return string text to display instead + */ + function getEvent($ical, $method=null) + { + // we get an inline CALENDAR ical/ics, we display it using the calendar notification style + $calobj = new calendar_ical; + $calboupdate = new calendar_boupdate; + // timezone stuff + $tz_diff = $GLOBALS['egw_info']['user']['preferences']['common']['tz_offset'] - $this->common_prefs['tz_offset']; + // form an event out of ical + $events = $calobj->icaltoegw($ical); + $event =& $events[0]; + // preset the olddate + $olddate = $calboupdate->format_date($event['start']+$tz_diff); + // search egw, if we can find it + $eventid = $calobj->find_event(array('uid'=>$event['uid'])); + if ((int)$eventid[0]>0) + { + // we found an event, we use the first one + $oldevent = $calobj->read($eventid); + // we set the olddate, to comply with the possible merge params for the notification message + if($oldevent != False && $oldevent[$eventid[0]]['start']!=$event[$eventid[0]]['start']) { + $olddate = $calboupdate->format_date($oldevent[$eventid[0]]['start']+$tz_diff); + } + // we merge the changes and the original event + $event = array_merge($oldevent[$eventid[0]],$event); + // for some strange reason, the title of the old event is not replaced with the new title + // if you klick on the ics and import it into egw, so we dont show the title here. + // so if it is a mere reply, we dont use the new title (more detailed info/work needed here) + if ($method == 'REPLY') $event['title'] = $oldevent[$eventid[0]]['title']; + } + // we prepare the message + $details = $calboupdate->_get_event_details($event,$action,$event_arr); + $details['olddate']=$olddate; + //_debug_array($_structure); + list($subject,$info) = $calboupdate->get_update_message($event, $method !='REPLY'); + $info = $GLOBALS['egw']->preferences->parse_notify($info,$details); + + // we set the bodyPart, we only show the event, we dont actually do anything, as we expect the user to + // click on the attached ics to update his own eventstore + $text = $subject; + $text .= "\n".$info; + $text .= "\n\n".lang('Event Details follow').":\n"; + foreach($event_arr as $key => $val) + { + if(strlen($details[$key])) { + switch($key){ + case 'access': + case 'priority': + case 'link': + break; + default: + $text .= sprintf("%-20s %s\n",$val['field'].':',$details[$key]); + break; + } + } + } + return $text; + } + + /** + * Get Body of message + * + * @param int $_uid the messageuid, + * @param string $_htmlOptions, how to display a message, html, plain text, ... + * @param string $_partID=null , the partID, may be omitted + * @param Horde_Mime_Part $_structure=null if given use structure for parsing + * @param boolean $_preserveSeen flag to preserve the seenflag by using body.peek + * @param string $_folder folder to work on + * @return array containing the message body, mimeType and charset + */ + function getMessageBody($_uid, $_htmlOptions='', $_partID=null, Horde_Mime_Part $_structure=null, $_preserveSeen = false, $_folder = '') + { + if (self::$debug) echo __METHOD__."$_uid, $_htmlOptions, $_partID
"; + if($_htmlOptions != '') { + $this->htmlOptions = $_htmlOptions; + } + if (empty($_folder)) + { + $_folder = $this->sessionData['mailbox']; + } + if (empty($this->sessionData['mailbox']) && !empty($_folder)) + { + $this->sessionData['mailbox'] = $_folder; + } + + if (!isset($_structure)) + { + $_structure = $this->getStructure($_uid, $_partID, $_folder, $_preserveSeen); + } + if (!is_object($_structure)) + { + return array( + array( + 'error' => 1, + 'body' => 'Error: Could not fetch structure on mail:'.$_uid." as $_htmlOptions". 'for Mailprofile'.$this->icServer->ImapServerId.' User:'.$GLOBALS['egw_info']['user']['account_lid'], + 'mimeType' => 'text/plain', + 'charSet' => self::$displayCharset, + ) + ); + } + if (!empty($_partID)) + { + $_structure->contentTypeMap(); + $_structure = $_structure->getPart($_partID); + //_debug_array($_structure->getMimeId()); exit; + } + + switch($_structure->getPrimaryType()) + { + case 'application': + return array( + array( + 'body' => '', + 'mimeType' => 'text/plain', + 'charSet' => 'iso-8859-1', + ) + ); + + case 'multipart': + switch($_structure->getSubType()) + { + case 'alternative': + $bodyParts = array($this->getMultipartAlternative($_uid, $_structure, $this->htmlOptions, $_preserveSeen)); + break; + + case 'nil': // multipart with no Alternative + case 'mixed': + case 'report': + case 'signed': + $bodyParts = $this->getMultipartMixed($_uid, $_structure, $this->htmlOptions, $_preserveSeen); + break; + + case 'related': + $bodyParts = $this->getMultipartRelated($_uid, $_structure, $this->htmlOptions, $_preserveSeen); + break; + } + return self::normalizeBodyParts($bodyParts); + + case 'video': + case 'audio': // some servers send audiofiles and imagesfiles directly, without any stuff surround it + case 'image': // they are displayed as Attachment NOT INLINE + return array( + array( + 'body' => '', + 'mimeType' => $_structure->getSubType(), + ), + ); + + case 'text': + $bodyPart = array(); + if ($_structure->getDisposition() != 'attachment') + { + switch($_structure->getSubType()) + { + case 'calendar': + // this is handeled in getTextPart + case 'html': + case 'plain': + default: + $bodyPart = array($this->getTextPart($_uid, $_structure, $this->htmlOptions, $_preserveSeen)); + } + } else { + // what if the structure->disposition is attachment ,... + } + return self::normalizeBodyParts($bodyPart); + + case 'attachment': + case 'message': + switch($_structure->getSubType()) + { + case 'rfc822': + $newStructure = $_structure->getParts(); + if (self::$debug) {echo __METHOD__." Message -> RFC -> NewStructure:"; _debug_array($newStructure[0]);} + return self::normalizeBodyParts($this->getMessageBody($_uid, $_htmlOptions, $newStructure[0]->getMimeId(), $newStructure[0], $_preserveSeen, $_folder)); + } + break; + + default: + if (self::$debug) _debug_array($_structure); + return array( + array( + 'body' => lang('The mimeparser can not parse this message.').$_structure->getType(), + 'mimeType' => 'text/plain', + 'charSet' => self::$displayCharset, + ) + ); + } + } + + /** + * normalizeBodyParts - function to gather and normalize all body Information + * as we may recieve a bodyParts structure from within getMessageBody nested deeper than expected + * so this is used to normalize the output, so we are able to rely on our expectation + * @param _bodyParts - Body Array + * @return array - a normalized Bodyarray + */ + static function normalizeBodyParts($_bodyParts) + { + if (is_array($_bodyParts)) + { + foreach($_bodyParts as $singleBodyPart) + { + if (!isset($singleBodyPart['body'])) { + $buff = self::normalizeBodyParts($singleBodyPart); + foreach ((array)$buff as $val) $body2return[] = $val; + continue; + } + $body2return[] = $singleBodyPart; + } + } + else + { + $body2return = $_bodyParts; + } + return $body2return; + } + + /** + * getdisplayableBody - creates the bodypart of the email as textual representation + * @param object $mailClass the mailClass object to be used + * @param array $bodyParts with the bodyparts + * @return string a preformatted string with the mails converted to text + */ + static function &getdisplayableBody(&$mailClass, $bodyParts, $preserveHTML = false) + { + for($i=0; $i'.$bodyParts[$i]['body']); + $newBody = translation::convert($bodyParts[$i]['body'], $bodyParts[$i]['charSet']); + //error_log(__METHOD__.' ('.__LINE__.') '.' MimeType:'.$bodyParts[$i]['mimeType'].'->'.$newBody); + /* + // in a way, this tests if we are having real utf-8 (the displayCharset) by now; we should if charsets reported (or detected) are correct + if (strtoupper(self::$displayCharset) == 'UTF-8') + { + $test = json_encode($newBody); + //error_log(__METHOD__.' ('.__LINE__.') '.'#'.$test.'# ->'.strlen($newBody).' Error:'.json_last_error()); + if (json_last_error() != JSON_ERROR_NONE && strlen($newBody)>0) + { + // this should not be needed, unless something fails with charset detection/ wrong charset passed + error_log(__METHOD__.' ('.__LINE__.') '.' Charset Reported:'.$bodyParts[$i]['charSet'].' Carset Detected:'.translation::detect_encoding($bodyParts[$i]['body'])); + $newBody = utf8_encode($newBody); + } + } + */ + //error_log(__METHOD__.' ('.__LINE__.') '.' before purify:'.$newBody); + $mailClass->activeMimeType = 'text/plain'; + if ($bodyParts[$i]['mimeType'] == 'text/html') { + $mailClass->activeMimeType = $bodyParts[$i]['mimeType']; + // as translation::convert reduces \r\n to \n and purifier eats \n -> peplace it with a single space + $newBody = str_replace("\n"," ",$newBody); + // convert HTML to text, as we dont want HTML in infologs + if (extension_loaded('tidy')) + { + $tidy = new tidy(); + $cleaned = $tidy->repairString($newBody, self::$tidy_config,'utf8'); + // Found errors. Strip it all so there's some output + if($tidy->getStatus() == 2) + { + error_log(__METHOD__.' ('.__LINE__.') '.' ->'.$tidy->errorBuffer); + } + else + { + $newBody = $cleaned; + } + if (!$preserveHTML) + { + // filter only the 'body', as we only want that part, if we throw away the html + preg_match('`(]*>)(.+?)(.*?)`ims', $newBody, $matches); + if ($matches[2]) + { + $hasOther = true; + $newBody = $matches[2]; + } + } + } + else + { + // htmLawed filter only the 'body' + preg_match('`(]*>)(.+?)(.*?)`ims', $newBody, $matches); + if ($matches[2]) + { + $hasOther = true; + $newBody = $matches[2]; + } + $htmLawed = new egw_htmLawed(); + // the next line should not be needed, but produces better results on HTML 2 Text conversion, + // as we switched off HTMLaweds tidy functionality + $newBody = str_replace(array('&amp;','

',"
 
",'
 
'),array('&','
','
','
'),$newBody); + $newBody = $htmLawed->egw_htmLawed($newBody); + if ($hasOther && $preserveHTML) $newBody = $matches[1]. $newBody. $matches[3]; + } + //error_log(__METHOD__.' ('.__LINE__.') '.' after purify:'.$newBody); + if ($preserveHTML==false) $newBody = translation::convertHTMLToText($newBody,self::$displayCharset,true,$stripalltags=true); + //error_log(__METHOD__.' ('.__LINE__.') '.' after convertHTMLToText:'.$newBody); + if ($preserveHTML==false) $newBody = nl2br($newBody); // we need this, as htmLawed removes \r\n + $mailClass->getCleanHTML($newBody,false,$preserveHTML); // remove stuff we regard as unwanted + if ($preserveHTML==false) $newBody = str_replace("
","\r\n",$newBody); + //error_log(__METHOD__.' ('.__LINE__.') '.' after getClean:'.$newBody); + $message .= $newBody; + continue; + } + $newBody =self::htmlspecialchars($newBody); + //error_log(__METHOD__.' ('.__LINE__.') '.' Body(after specialchars):'.$newBody); + $newBody = strip_tags($newBody); //we need to fix broken tags (or just stuff like "<800 USD/p" ) + //error_log(__METHOD__.' ('.__LINE__.') '.' Body(after strip tags):'.$newBody); + $newBody = htmlspecialchars_decode($newBody,ENT_QUOTES); + //error_log(__METHOD__.' ('.__LINE__.') '.' Body (after hmlspc_decode):'.$newBody); + $message .= $newBody; + //continue; + } + return $message; + } + + static function wordwrap($str, $cols, $cut, $dontbreaklinesstartingwith=false) + { + $lines = explode("\n", $str); + $newStr = ''; + foreach($lines as $line) + { + // replace tabs by 8 space chars, or any tab only counts one char + //$line = str_replace("\t"," ",$line); + //$newStr .= wordwrap($line, $cols, $cut); + $allowedLength = $cols-strlen($cut); + if (strlen($line) > $allowedLength && + ($dontbreaklinesstartingwith==false || + ($dontbreaklinesstartingwith && + strlen($dontbreaklinesstartingwith)>=1 && + substr($line,0,strlen($dontbreaklinesstartingwith)) != $dontbreaklinesstartingwith + ) + ) + ) + { + $s=explode(" ", $line); + $line = ""; + $linecnt = 0; + foreach ($s as $k=>$v) { + $cnt = strlen($v); + // only break long words within the wordboundaries, + // but it may destroy links, so we check for href and dont do it if we find one + // we check for any html within the word, because we do not want to break html by accident + if($cnt > $allowedLength && stripos($v,'href=')===false && stripos($v,'onclick=')===false && $cnt == strlen(html_entity_decode($v))) + { + $v=wordwrap($v, $allowedLength, $cut, true); + } + // the rest should be broken at the start of the new word that exceeds the limit + if ($linecnt+$cnt > $allowedLength) { + $v=$cut.$v; + #$linecnt = 0; + $linecnt =strlen($v)-strlen($cut); + } else { + $linecnt += $cnt; + } + if (strlen($v)) $line .= (strlen($line) ? " " : "").$v; + } + } + $newStr .= $line . "\n"; + } + return $newStr; + } + + /** + * getMessageEnvelope + * get parsed headers from message + * @param string/int $_uid the messageuid, + * @param string/int $_partID='' , the partID, may be omitted + * @param boolean $decode flag to do the decoding on the fly + * @param string $_folder folder to work on + * @param boolean $_useHeaderInsteadOfEnvelope - force getMessageHeader method to be used for fetching Envelope Information + * @return array the message header + */ + function getMessageEnvelope($_uid, $_partID = '',$decode=false, $_folder='', $_useHeaderInsteadOfEnvelope=false) + { + //error_log(__METHOD__.' ('.__LINE__.') '.":$_uid,$_partID,$decode,$_folder".function_backtrace()); + if (empty($_folder)) $_folder = ($this->sessionData['mailbox']? $this->sessionData['mailbox'] : $this->icServer->getCurrentMailbox()); + //error_log(__METHOD__.' ('.__LINE__.') '.":$_uid,$_partID,$decode,$_folder"); + if((empty($_partID)||$_partID=='null')&&$_useHeaderInsteadOfEnvelope===false) { + $uidsToFetch = new Horde_Imap_Client_Ids(); + if (!(is_object($_uid) || is_array($_uid))) $_uid = (array)$_uid; + $uidsToFetch->add($_uid); + + $fquery = new Horde_Imap_Client_Fetch_Query(); + $envFields = new Horde_Mime_Headers(); + $fquery->envelope(); + $headersNew = $this->icServer->fetch($_folder, $fquery, array( + 'ids' => $uidsToFetch, + )); + if (is_object($headersNew)) { + foreach($headersNew as $id=>$_headerObject) { + $env = $_headerObject->getEnvelope(); + //_debug_array($envFields->singleFields()); + foreach ($envFields->singleFields() as $e => $v) + { + switch ($v) + { + case 'to': + case 'reply-to': + case 'from': + case 'cc': + case 'bcc': + case 'sender': + //error_log(__METHOD__.' ('.__LINE__.') '.$v.'->'.array2string($env->$v->addresses)); + $envelope[$v]=$env->$v->addresses; + $address = array(); + if (!is_array($envelope[$v])) break; + foreach ($envelope[$v] as $k => $ad) + { + if (stripos($ad,'@')===false) + { + $remember=$k; + } + else + { + $address[] = (!is_null($remember)?$envelope[$v][$remember].' ':'').$ad; + $remember=null; + } + } + $envelope[$v] = $address; + break; + case 'date': + $envelope[$v]=egw_time::to($env->$v); + break; + default: + $envelope[$v]=$env->$v; + } + } + } + } + $envelope = array_change_key_case($envelope,CASE_UPPER); + //if ($decode) _debug_array($envelope); + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($envelope)); + return ($decode ? self::decode_header($envelope,true): $envelope); + } else { + + $headers = $this->getMessageHeader($_uid, $_partID, true,true,$_folder); + + //error_log(__METHOD__.' ('.__LINE__.') '.':'.array2string($headers)); + //_debug_array($headers); + $newData = array( + 'DATE' => $headers['DATE'], + 'SUBJECT' => ($decode ? self::decode_header($headers['SUBJECT']):$headers['SUBJECT']), + 'MESSAGE_ID' => $headers['MESSAGE-ID'] + ); + //_debug_array($newData); + $recepientList = array('FROM', 'TO', 'CC', 'BCC', 'SENDER', 'REPLY-TO'); + foreach($recepientList as $recepientType) { + if(isset($headers[$recepientType])) { + if ($decode) $headers[$recepientType] = self::decode_header($headers[$recepientType],true); + $addresses = imap_rfc822_parse_adrlist($headers[$recepientType], ''); + foreach($addresses as $singleAddress) { + $addressData = array( + 'PERSONAL_NAME' => $singleAddress->personal ? $singleAddress->personal : 'NIL', + 'AT_DOMAIN_LIST' => $singleAddress->adl ? $singleAddress->adl : 'NIL', + 'MAILBOX_NAME' => $singleAddress->mailbox ? $singleAddress->mailbox : 'NIL', + 'HOST_NAME' => $singleAddress->host ? $singleAddress->host : 'NIL', + 'EMAIL' => $singleAddress->host ? $singleAddress->mailbox.'@'.$singleAddress->host : $singleAddress->mailbox, + ); + if($addressData['PERSONAL_NAME'] != 'NIL') { + $addressData['RFC822_EMAIL'] = imap_rfc822_write_address($singleAddress->mailbox, $singleAddress->host, $singleAddress->personal); + } else { + $addressData['RFC822_EMAIL'] = 'NIL'; + } + $newData[$recepientType][] = ($addressData['RFC822_EMAIL']!='NIL'?$addressData['RFC822_EMAIL']:$addressData['EMAIL']);//$addressData; + } + } else { + if($recepientType == 'SENDER' || $recepientType == 'REPLY-TO') { + $newData[$recepientType] = $newData['FROM']; + } else { + $newData[$recepientType] = array(); + } + } + } + //if ($decode) _debug_array($newData); + return $newData; + } + } + + /** + * getMessageHeader + * get parsed headers from message + * @param string/int $_uid the messageuid, + * @param string/int $_partID='' , the partID, may be omitted + * @param boolean $decode flag to do the decoding on the fly + * @param boolean $preserveUnSeen flag to preserve the seen flag where applicable + * @param string $_folder folder to work on + * @return array the message header + */ + function getMessageHeader($_uid, $_partID = '',$decode=false, $preserveUnSeen=false, $_folder='') + { + //error_log(__METHOD__.' ('.__LINE__.') '.':'.$_uid.', '.$_partID.', '.$decode.', '.$preserveUnSeen.', '.$_folder); + if (empty($_folder)) $_folder = ($this->sessionData['mailbox']? $this->sessionData['mailbox'] : $this->icServer->getCurrentMailbox()); + $uidsToFetch = new Horde_Imap_Client_Ids(); + if (!(is_object($_uid) || is_array($_uid))) $_uid = (array)$_uid; + $uidsToFetch->add($_uid); + + $fquery = new Horde_Imap_Client_Fetch_Query(); + if ($_partID != '') + { + $fquery->headerText(array('id'=>$_partID,'peek'=>$preserveUnSeen)); + $fquery->structure(); + } + else + { + $fquery->headerText(array('peek'=>$preserveUnSeen)); + } + $headersNew = $this->icServer->fetch($_folder, $fquery, array( + 'ids' => $uidsToFetch, + )); + if (is_object($headersNew)) { + foreach($headersNew as $id=>$_headerObject) { + $retValue = $_headerObject->getHeaderText(0,Horde_Imap_Client_Data_Fetch::HEADER_PARSE)->toArray(); + if ($_partID != '') + { + $mailStructureObject = $_headerObject->getStructure(); + foreach ($mailStructureObject->contentTypeMap() as $mime_id => $mime_type) + { + if ($mime_id==$_partID) + { + //error_log(__METHOD__.' ('.__LINE__.') '."$mime_id == $_partID".array2string($_headerObject->getHeaderText($mime_id,Horde_Imap_Client_Data_Fetch::HEADER_PARSE)->toArray())); + $retValue = $_headerObject->getHeaderText($mime_id,Horde_Imap_Client_Data_Fetch::HEADER_PARSE)->toArray(); + break; + } + } + } + } + } + $retValue = array_change_key_case($retValue,CASE_UPPER); + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($retValue)); + // if SUBJECT is an array, use thelast one, as we assume something with the unfolding for the subject did not work + if (is_array($retValue['SUBJECT'])) + { + $retValue['SUBJECT'] = $retValue['SUBJECT'][count($retValue['SUBJECT'])-1]; + } + //error_log(__METHOD__.' ('.__LINE__.') '.':'.array2string($decode ? self::decode_header($retValue,true):$retValue)); + return ($decode ? self::decode_header($retValue,true):$retValue); + } + + /** + * getMessageRawHeader + * get messages raw header data + * @param string/int $_uid the messageuid, + * @param string/int $_partID='' , the partID, may be omitted + * @param string $_folder folder to work on + * @return string the message header + */ + function getMessageRawHeader($_uid, $_partID = '', $_folder = '') + { + static $rawHeaders; + if (empty($_folder)) $_folder = ($this->sessionData['mailbox']? $this->sessionData['mailbox'] : $this->icServer->getCurrentMailbox()); + //error_log(__METHOD__.' ('.__LINE__.') '." Try Using Cache for raw Header $_uid, $_partID in Folder $_folder"); + + if (is_null($rawHeaders)) $rawHeaders = egw_cache::getCache(egw_cache::INSTANCE,'email','rawHeadersCache'.trim($GLOBALS['egw_info']['user']['account_id']),$callback=null,$callback_params=array(),$expiration=60*60*1); + if (isset($rawHeaders[$this->icServer->ImapServerId][$_folder][$_uid][(empty($_partID)?'NIL':$_partID)])) + { + //error_log(__METHOD__.' ('.__LINE__.') '." Using Cache for raw Header $_uid, $_partID in Folder $_folder"); + return $rawHeaders[$this->icServer->ImapServerId][$_folder][$_uid][(empty($_partID)?'NIL':$_partID)]; + } + $uidsToFetch = new Horde_Imap_Client_Ids(); + $uid = $_uid; + if (!(is_object($_uid) || is_array($_uid))) $uid = (array)$_uid; + $uidsToFetch->add($uid); + + $fquery = new Horde_Imap_Client_Fetch_Query(); + if ($_partID != '') + { + $fquery->headerText(array('id'=>$_partID,'peek'=>true)); + $fquery->structure(); + } + else + { + $fquery->headerText(array('peek'=>true)); + } + $headersNew = $this->icServer->fetch($_folder, $fquery, array( + 'ids' => $uidsToFetch, + )); + if (is_object($headersNew)) { + foreach($headersNew as $id=>$_headerObject) { + $retValue = $_headerObject->getHeaderText(); + if ($_partID != '') + { + $mailStructureObject = $_headerObject->getStructure(); + foreach ($mailStructureObject->contentTypeMap() as $mime_id => $mime_type) + { + if ($mime_id==$_partID) + { + $retValue = $_headerObject->getHeaderText($mime_id); + } + } + } + } + } + $rawHeaders[$this->icServer->ImapServerId][$_folder][$_uid][(empty($_partID)?'NIL':$_partID)]=$retValue; + egw_cache::setCache(egw_cache::INSTANCE,'email','rawHeadersCache'.trim($GLOBALS['egw_info']['user']['account_id']),$rawHeaders,$expiration=60*60*1); + return $retValue; + } + + /** + * getStyles - extracts the styles from the given bodyparts + * @param array $bodyParts with the bodyparts + * @return string a preformatted string with the mails converted to text + */ + static function &getStyles($_bodyParts) + { + $style = ''; + if (empty($_bodyParts)) return ""; + foreach((array)$_bodyParts as $singleBodyPart) { + if (!isset($singleBodyPart['body'])) { + $singleBodyPart['body'] = self::getStyles($singleBodyPart); + $style .= $singleBodyPart['body']; + continue; + } + + if ($singleBodyPart['charSet']===false) $singleBodyPart['charSet'] = translation::detect_encoding($singleBodyPart['body']); + $singleBodyPart['body'] = translation::convert( + $singleBodyPart['body'], + strtolower($singleBodyPart['charSet']) + ); + $ct = 0; + + if (stripos($singleBodyPart['body'],'(.+)#isU', $singleBodyPart['body'], $newStyle); + if ($ct>0) + { + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($newStyle[0])); + $style2buffer = implode('',$newStyle[0]); + } + if ($style2buffer && strtoupper(self::$displayCharset) == 'UTF-8') + { + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($style2buffer)); + $test = json_encode($style2buffer); + //error_log(__METHOD__.' ('.__LINE__.') '.'#'.$test.'# ->'.strlen($style2buffer).' Error:'.json_last_error()); + //if (json_last_error() != JSON_ERROR_NONE && strlen($style2buffer)>0) + if ($test=="null" && strlen($style2buffer)>0) + { + // this should not be needed, unless something fails with charset detection/ wrong charset passed + error_log(__METHOD__.' ('.__LINE__.') '.' Found Invalid sequence for utf-8 in CSS:'.$style2buffer.' Charset Reported:'.$singleBodyPart['charSet'].' Carset Detected:'.translation::detect_encoding($style2buffer)); + $style2buffer = utf8_encode($style2buffer); + } + } + $style .= $style2buffer; + } + // clean out comments and stuff + $search = array( + '@url\(http:\/\/[^\)].*?\)@si', // url calls e.g. in style definitions +// '@@', // Strip multi-line comments including CDATA +// '@ in stylesheet are outdated, and ck-editor does not understand it, we remove it + $css = str_replace(array(':',''),array(': ','',''),$css); + //error_log(__METHOD__.' ('.__LINE__.') '.$css); + // TODO: we may have to strip urls and maybe comments and ifs + return $css; + } + + /** + * getMessageRawBody + * get the message raw body + * @param string/int $_uid the messageuid, + * @param string/int $_partID='' , the partID, may be omitted + * @param string $_folder folder to work on + * @return string the message body + */ + function getMessageRawBody($_uid, $_partID = '', $_folder='') + { + //TODO: caching einbauen static! + static $rawBody; + if (is_null($rawBody)) $rawBody = array(); + if (empty($_folder)) $_folder = ($this->sessionData['mailbox']? $this->sessionData['mailbox'] : $this->icServer->getCurrentMailbox()); + if (isset($rawBody[$this->icServer->ImapServerId][$_folder][$_uid][(empty($_partID)?'NIL':$_partID)])) + { + //error_log(__METHOD__.' ('.__LINE__.') '." Using Cache for raw Body $_uid, $_partID in Folder $_folder"); + return $rawBody[$this->icServer->ImapServerId][$_folder][$_uid][(empty($_partID)?'NIL':$_partID)]; + } + + $uidsToFetch = new Horde_Imap_Client_Ids(); + $uid = $_uid; + if (!(is_object($_uid) || is_array($_uid))) $uid = (array)$_uid; + $uidsToFetch->add($uid); + + $fquery = new Horde_Imap_Client_Fetch_Query(); + $fquery->fullText(array('peek'=>true)); + if ($_partID != '') + { + $fquery->structure(); + $fquery->bodyPart($_partID,array('peek'=>true)); + } + $headersNew = $this->icServer->fetch($_folder, $fquery, array( + 'ids' => $uidsToFetch, + )); + if (is_object($headersNew)) { + foreach($headersNew as $id=>$_headerObject) { + $body = $_headerObject->getFullMsg(); + if ($_partID != '') + { + $mailStructureObject = $_headerObject->getStructure(); + //_debug_array($mailStructureObject->contentTypeMap()); + foreach ($mailStructureObject->contentTypeMap() as $mime_id => $mime_type) + { + if ($mime_id==$_partID) + { + $body = $_headerObject->getBodyPart($mime_id); + } + } + } + } + } + //error_log(__METHOD__.' ('.__LINE__.') '."[$this->icServer->ImapServerId][$_folder][$_uid][".(empty($_partID)?'NIL':$_partID)."]"); + $rawBody[$this->icServer->ImapServerId][$_folder][$_uid][(empty($_partID)?'NIL':$_partID)] = $body; + return $body; + } + + /** + * Get structure of a mail or part of a mail + * + * @param int $_uid + * @param string $_partID=null + * @param string $_folder=null + * @param boolean $_preserveSeen=false flag to preserve the seenflag by using body.peek + * @param Horde_Imap_Client_Fetch_Query $fquery=null default query just structure + * @return Horde_Mime_Part + */ + function getStructure($_uid, $_partID=null, $_folder=null, $_preserveSeen=false) + { + if (self::$debug) error_log( __METHOD__.' ('.__LINE__.') '.":$_uid, $_partID"); + + if (empty($_folder)) + { + $_folder = ($this->sessionData['mailbox']? $this->sessionData['mailbox'] : $this->icServer->getCurrentMailbox()); + } + $uidsToFetch = new Horde_Imap_Client_Ids(); + if (!(is_object($_uid) || is_array($_uid))) $_uid = (array)$_uid; + $uidsToFetch->add($_uid); + try + { + $_fquery = new Horde_Imap_Client_Fetch_Query(); + // not sure why Klaus add these, seem not necessary + // $fquery->envelope(); + // $fquery->size(); + $_fquery->structure(); + if ($_partID) $_fquery->bodyPart($_partID, array('peek' => $_preserveSeen)); + + $mail = $this->icServer->fetch($_folder, $_fquery, array( + 'ids' => $uidsToFetch, + ))->first(); + + return $mail->getStructure(); + } + catch (Exception $e) + { + error_log(__METHOD__.' ('.__LINE__.') '.' Could not fetch structure on mail:'.$_uid.' Serverprofile->'.$this->icServer->ImapServerId.' Message:'.$e->getMessage().' Stack:'.function_backtrace()); + return null; + } + } + + /** + * Parse the structure for attachments + * + * Returns not the attachments itself, but an array of information about the attachment + * + * @param int $_uid the messageuid, + * @param string $_partID=null , the partID, may be omitted + * @param Horde_Mime_Part $_structure=null if given use structure for parsing + * @param boolean $fetchEmbeddedImages=true, + * @param boolean $fetchTextCalendar=false, + * @param boolean $resolveTNEF=true + * @param string $_folder folder to work on + * @return array an array of information about the attachment: array of array(name, size, mimeType, partID, encoding) + */ + function getMessageAttachments($_uid, $_partID=null, Horde_Mime_Part $_structure=null, $fetchEmbeddedImages=true, $fetchTextCalendar=false, $resolveTNEF=true, $_folder='') + { + if (self::$debug) error_log( __METHOD__.":$_uid, $_partID"); + if (empty($_folder)) $_folder = ($this->sessionData['mailbox']? $this->sessionData['mailbox'] : $this->icServer->getCurrentMailbox()); + + if (!isset($_structure)) + { + $_structure = $this->getStructure($_uid, $_partID,$_folder,true); + //error_log(__METHOD__.' ('.__LINE__.') '.':'.print_r($_structure->contentTypeMap(),true)); + } + if (!$_structure || !$_structure->contentTypeMap()) return array(); + if (!empty($_partID)) $_structure = $_structure->getPart($_partID); + $skipParts = array(); + foreach($_structure->contentTypeMap() as $mime_id => $mime_type) + { + $part = $_structure->getPart($mime_id); + //error_log(__METHOD__.' ('.__LINE__.') '.':'.' Uid:'.$uid.' Part:'.$_partID.'->'.array2string($part->getMimeId())); + //error_log(__METHOD__.' ('.__LINE__.') '.':'.' Uid:'.$uid.' Part:'.$_partID.'->'.$part->getPrimaryType().'/'.$part->getSubType().'->'.$part->getDisposition()); + //error_log(__METHOD__.' ('.__LINE__.') '.':'.' Uid:'.$uid.' Part:'.$_partID.'->'.array2string($part->getAllDispositionParameters())); + //error_log(__METHOD__.' ('.__LINE__.') '.':'.' Uid:'.$uid.' Part:'.$_partID.'->'.array2string($part->getAllContentTypeParameters())); + $partDisposition = $part->getDisposition(); + $partPrimaryType = $part->getPrimaryType(); + // we only want to retrieve the attachments of the current mail, not those of possible + // attached mails + if ($mime_type=='message/rfc822' && $_partID!=$mime_id) + { + //error_log(__METHOD__.' ('.__LINE__.') '.' Uid:'.$uid.'->'.$mime_id.':'.array2string($part->contentTypeMap())); + foreach($part->contentTypeMap() as $sub_id => $sub_type) if ($sub_id != $mime_id) $skipParts[$sub_id] = $sub_type; + } + //error_log(__METHOD__.' ('.__LINE__.') '.' Uid:'.$uid.' Part:'.$_partID.'->'.$mime_id.':'.array2string($skipParts)); + if (array_key_exists($mime_id,$skipParts)) continue; + + if ($partDisposition == 'attachment' || + (($partDisposition == 'inline' || empty($partDisposition)) && $partPrimaryType == 'image' && $part->getContentId()=='') || + (($partDisposition == 'inline' || empty($partDisposition)) && $partPrimaryType != 'image' && $partPrimaryType != 'text' && $partPrimaryType != 'multipart') || + ($fetchEmbeddedImages && ($partDisposition == 'inline' || empty($partDisposition)) && $partPrimaryType == 'image') || + ($fetchTextCalendar && $partPrimaryType == 'text' && $part->getSubType() == 'calendar')) + { + // if type is message/rfc822 and _partID is given, and MimeID equals partID + // we attempt to fetch "ourselves" + if ($_partID==$part->getMimeId() && $part->getPrimaryType()=='message') continue; + $attachment = $part->getAllDispositionParameters(); + $attachment['mimeType'] = $mime_type; + $attachment['uid'] = $_uid; + $attachment['partID'] = $mime_id; + if (!isset($attachment['name'])||empty($attachment['name'])) $attachment['name'] = $part->getName(); + if ($fetchTextCalendar) + { + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($part->getAllContentTypeParameters())); + $method = $part->getContentTypeParameter('method'); + if ($method) $attachment['method'] = $method; + if (!isset($attachment['name'])) $attachment['name'] = 'event.ics'; + } + $attachment['size'] = $part->getBytes(); + if (($cid = $part->getContentId())) $attachment['cid'] = $cid; + if (empty($attachment['name'])) $attachment['name'] = (isset($attachment['cid'])&&!empty($attachment['cid'])?$attachment['cid']:lang("unknown").'_Uid'.$_uid.'_Part'.$mime_id).'.'.mime_magic::mime2ext($mime_type); + $attachments[] = $attachment; + } + } + return $attachments; + } + + /** + * retrieve a attachment + * + * @param int _uid the uid of the message + * @param string _partID the id of the part, which holds the attachment + * @param int _winmail_nr winmail.dat attachment nr. + * @param boolean _returnPart flag to indicate if the attachment is to be returned as horde mime part object + * @param boolean _stream flag to indicate if the attachment is to be fetched or returned as filepointer + * + * @return array + */ + function getAttachment($_uid, $_partID, $_winmail_nr=0, $_returnPart=true, $_stream=false) + { + $_folder = ($this->sessionData['mailbox']? $this->sessionData['mailbox'] : $this->icServer->getCurrentMailbox()); + + $uidsToFetch = new Horde_Imap_Client_Ids(); + if (!(is_object($_uid) || is_array($_uid))) $_uid = (array)$_uid; + $uidsToFetch->add($_uid); + + $fquery = new Horde_Imap_Client_Fetch_Query(); + $fquery->structure(); + $fquery->bodyPart($_partID, array('peek'=>true)); + $headersNew = $this->icServer->fetch($_folder, $fquery, array( + 'ids' => $uidsToFetch, + )); + if (is_object($headersNew)) { + foreach($headersNew as $id=>$_headerObject) { + $body = $_headerObject->getFullMsg(); + if ($_partID != '') + { + $mailStructureObject = $_headerObject->getStructure(); + $mailStructureObject->contentTypeMap(); + $part = $mailStructureObject->getPart($_partID); + $partDisposition = ($part?$part->getDisposition():'failed'); + if ($partDisposition=='failed') + { + error_log(__METHOD__.'('.__LINE__.'):'.array2string($_uid).','.$_partID.' ID:'.$id.' HObject:'.array2string($_headerObject).' StructureObject:'.array2string($mailStructureObject->contentTypeMap()).'->'.function_backtrace()); + } + // if $partDisposition is empty, we assume attachment, and hope that the function + // itself is only triggered to fetch attachments + if (empty($partDisposition)) $partDisposition='attachment'; + if ($part && ($partDisposition=='attachment' || $partDisposition=='inline' || ($part->getPrimaryType() == 'text' && $part->getSubType() == 'calendar'))) + { + $headerObject['ATTACHMENTS'][$mime_id]=$part->getAllDispositionParameters(); + + $structure_bytes = $part->getBytes(); + $structure_mime=$part->getType(); + $structure_partID=$part->getMimeId(); + $filename=$part->getName(); + $this->fetchPartContents($_uid, $part, $_stream, $_preserveSeen=true); + if ($_returnPart) return $part; + } + } + } + } + $ext = mime_magic::mime2ext($structure_mime); + if ($ext && stripos($filename,'.')===false && stripos($filename,$ext)===false) $filename = trim($filename).'.'.$ext; + $attachmentData = array( + 'type' => $structure_mime, + 'filename' => $filename, + 'attachment' => $part->getContents(array('stream'=>$_stream)) + ); +/* + // try guessing the mimetype, if we get the application/octet-stream + if (strtolower($attachmentData['type']) == 'application/octet-stream') $attachmentData['type'] = mime_magic::filename2mime($attachmentData['filename']); + # if the attachment holds a winmail number and is a winmail.dat then we have to handle that. + if ( $filename == 'winmail.dat' && $_winmail_nr > 0 && + ( $wmattach = $this->decode_winmail( $_uid, $_partID, $_winmail_nr ) ) ) + { + $ext = mime_magic::mime2ext($wmattach['type']); + if ($ext && stripos($wmattach['name'],'.')===false && stripos($wmattach['name'],$ext)===false) $wmattach['name'] = trim($wmattach['name']).'.'.$ext; + $attachmentData = array( + 'type' => $wmattach['type'], + 'filename' => $wmattach['name'], + 'attachment' => $wmattach['attachment'], + ); + } +*/ + return $attachmentData; + } + + /** + * Fetch a specific attachment from a message by it's cid + * + * this function is based on a on "Building A PHP-Based Mail Client" + * http://www.devshed.com + * + * @param string|int $_uid + * @param string $_cid + * @param string $_part + * @param boolean $_stream=null null do NOT fetch content, use fetchPartContents later + * true: + * @return Horde_Mime_Part + */ + function getAttachmentByCID($_uid, $_cid, $_part, $_stream=null) + { + // some static variables to avoid fetching the same mail multiple times + static $uid=null, $part=null, $structure=null; + //error_log(__METHOD__.' ('.__LINE__.') '.":$_uid, $_cid, $_part"); + + if(empty($_cid)) return false; + + if ($_uid != $uid || $_part != $part) + { + $structure = $this->getStructure($uid=$_uid, $part=$_part); + } + /** @var Horde_Mime_Part */ + $attachment = null; + foreach($structure->contentTypeMap() as $mime_id => $mime_type) + { + $part = $structure->getPart($mime_id); + + if ($part->getPrimaryType() == 'image' && + (($cid = $part->getContentId()) && + // RB: seem a bit fague to search for inclusion in both ways + (strpos($cid, $_cid) !== false || strpos($_cid, $cid) !== false)) || + (($name = $part->getName()) && + (strpos($name, $_cid) !== false || strpos($_cid, $name) !== false))) + { + // if we have a direct match, dont search any further + if ($cid == $_cid) + { + $attachment = $part; + } + // everything else we only consider after we checked all + if (!isset($attachment)) $attachment = $part; + // do we want content fetched, can be done later, if not needed + if (isset($_stream)) + { + $this->fetchPartContents($_uid, $attachment, $_stream); + } + if (isset($attachment)) break; + } + } + // set name as filename, if not set + if ($attachment && !$attachment->getDispositionParameter('filename')) + { + $attachment->setDispositionParameter('filename', $attachment->getName()); + } + // guess type, if not set + if ($attachment && $attachment->getType() == 'application/octet-stream') + { + $attachment->setType(mime_magic::filename2mime($attachment->getDispositionParameter('filename'))); + } + //error_log(__METHOD__."($_uid, '$_cid', '$_part') returning ".array2string($attachment)); + return $attachment; + } + + /** + * Fetch and add contents to a part + * + * To get contents you use $part->getContents(); + * + * @param int $_uid + * @param Horde_Mime_Part $part + * @param boolean $_stream=false true return a stream, false a string + * @param boolean $_preserveSeen flag to preserve the seenflag by using body.peek + * @return Horde_Mime_Part + */ + public function fetchPartContents($_uid, Horde_Mime_Part $part=null, $_stream=false, $_preserveSeen=false) + { + if (is_null($part)) return null;//new Horde_Mime_Part; + $encoding = null; + // we need to set content on structure to decode transfer encoding + $part->setContents( + $this->getBodyPart($_uid, $part->getMimeId(), null, $_preserveSeen, $_stream, $encoding), + array('encoding' => $encoding)); + + return $part; + } + + /** + * save a message in folder + * throws exception on failure + * @todo set flags again + * + * @param string _folderName the foldername + * @param string _header the header of the message + * @param string _body the body of the message + * @param string _flags the imap flags to set for the saved message + * + * @return the id of the message appended or exception + */ + function appendMessage($_folderName, $_header, $_body, $_flags) + { + //error_log(__METHOD__.' ('.__LINE__.') '."$_folderName, $_header, $_body, $_flags"); + $header = ltrim(str_replace("\n","\r\n",$_header)); + $body = str_replace("\n","\r\n",$_body); + // the recent flag is the default enforced here ; as we assume the _flags is always set, + // we default it to hordes default (Recent) (, other wise we should not pass the parameter + // for flags at all) + if (empty($_flags)) $_flags = '\\Recent'; + //if (!is_array($_flags) && stripos($_flags,',')!==false) $_flags=explode(',',$_flags); + //if (!is_array($_flags)) $_flags = (array) $_flags; + try + { + $dataNflags = array(); + $dataNflags[] = array('data'=>array(array('t'=>'text','v'=>"$header"."$body")), 'flags'=>array($_flags)); + $messageid = $this->icServer->append($_folderName,$dataNflags); + } + catch (Exception $e) + { + if (self::$debug) error_log("Could not append Message:".$e->getMessage()); + throw new egw_exception_wrong_userinput(lang("Could not append Message:".$e->getMessage)); + //return false; + } + //error_log(__METHOD__.' ('.__LINE__.') '.' appended UID:'.$messageid); + //$messageid = true; // for debug reasons only + if ($messageid === true) // try to figure out the message uid + { + $list = $this->getHeaders($_folderName, $_startMessage=1, $_numberOfMessages=1, $_sort='INTERNALDATE', $_reverse=true, $_filter=array(),$_thisUIDOnly=null, $_cacheResult=false); + if ($list) + { + if (self::$debug) error_log(__METHOD__.' ('.__LINE__.') '.' MessageUid:'.$messageid.' but found:'.array2string($list)); + $messageid = $list['header'][0]['uid']; + } + } + return $messageid; + } + + /** + * getRandomString - function to be used to fetch a random string and md5 encode that one + * @param none + * @return string - a random number which is md5 encoded + */ + static function getRandomString() { + mt_srand((float) microtime() * 1000000); + return md5(mt_rand (100000, 999999)); + } + + /** + * functions to allow access to mails through other apps to fetch content + * used in infolog, tracker + */ + + /** + * get_mailcontent - fetches the actual mailcontent, and returns it as well defined array + * @param object mailClass the mailClassobject to be used + * @param uid the uid of the email to be processed + * @param partid the partid of the email + * @param mailbox the mailbox, that holds the message + * @param preserveHTML flag to pass through to getdisplayableBody + * @param addHeaderSection flag to be able to supress headersection + * @param includeAttachments flag to be able to supress possible attachments + * @return array/bool with 'mailaddress'=>$mailaddress, + * 'subject'=>$subject, + * 'message'=>$message, + * 'attachments'=>$attachments, + * 'headers'=>$headers,; boolean false on failure + */ + static function get_mailcontent(&$mailClass,$uid,$partid='',$mailbox='', $preserveHTML = false, $addHeaderSection=true, $includeAttachments=true) + { + //echo __METHOD__." called for $uid,$partid
"; + $headers = $mailClass->getMessageHeader($uid,$partid,true,false,$mailbox); + if (empty($headers)) return false; + // dont force retrieval of the textpart, let mailClass preferences decide + $bodyParts = $mailClass->getMessageBody($uid,($preserveHTML?'always_display':'only_if_no_text'),$partid,null,false,$mailbox); + // if we do not want HTML but there is no TextRepresentation with the message itself, try converting + if ( !$preserveHTML && $bodyParts[0]['mimeType']=='text/html') + { + foreach($bodyParts as $i => $part) + { + if ($bodyParts[$i]['mimeType']=='text/html') + { + $bodyParts[$i]['body'] = translation::convertHTMLToText($bodyParts[$i]['body'],$bodyParts[$i]['charSet'],true,$stripalltags=true); + $bodyParts[$i]['mimeType']='text/plain'; + } + } + } + //error_log(array2string($bodyParts)); + $attachments = $includeAttachments?$mailClass->getMessageAttachments($uid,$partid):array(); + + if ($mailClass->isSentFolder($mailbox)) $mailaddress = $headers['TO']; + elseif (isset($headers['FROM'])) $mailaddress = $headers['FROM']; + elseif (isset($headers['SENDER'])) $mailaddress = $headers['SENDER']; + if (isset($headers['CC'])) $mailaddress .= ','.$headers['CC']; + //_debug_array(array($headers,$mailaddress)); + $subject = $headers['SUBJECT']; + + $message = self::getdisplayableBody($mailClass, $bodyParts, $preserveHTML); + if ($preserveHTML && $mailClass->activeMimeType == 'text/plain') $message = '
'.$message.'
'; + $headdata = ($addHeaderSection ? self::createHeaderInfoSection($headers, '',$preserveHTML) : ''); + $message = $headdata.$message; + //echo __METHOD__.'
'; + //_debug_array($attachments); + if (is_array($attachments)) + { + foreach ($attachments as $num => $attachment) + { + if ($attachment['mimeType'] == 'MESSAGE/RFC822') + { + //_debug_array($mailClass->getMessageHeader($uid, $attachment['partID'])); + //_debug_array($mailClass->getMessageBody($uid,'', $attachment['partID'])); + //_debug_array($mailClass->getMessageAttachments($uid, $attachment['partID'])); + $mailcontent = self::get_mailcontent($mailClass,$uid,$attachment['partID']); + $headdata =''; + if ($mailcontent['headers']) + { + $headdata = self::createHeaderInfoSection($mailcontent['headers'],'',$preserveHTML); + } + if ($mailcontent['message']) + { + $tempname =tempnam($GLOBALS['egw_info']['server']['temp_dir'],$GLOBALS['egw_info']['flags']['currentapp']."_"); + $attachedMessages[] = array( + 'type' => 'TEXT/PLAIN', + 'name' => $mailcontent['subject'].'.txt', + 'tmp_name' => $tempname, + ); + $tmpfile = fopen($tempname,'w'); + fwrite($tmpfile,$headdata.$mailcontent['message']); + fclose($tmpfile); + } + foreach($mailcontent['attachments'] as $tmpattach => $tmpval) + { + $attachedMessages[] = $tmpval; + } + unset($attachments[$num]); + } + else + { + $attachments[$num] = array_merge($attachments[$num],$mailClass->getAttachment($uid, $attachment['partID'],0,false,false)); + if (empty($attachments[$num]['attachment'])&&$attachments[$num]['cid']) + { + $c = $mailClass->getAttachmentByCID($uid, $attachment['cid'], $attachment['partID'],true); + $attachments[$num]['attachment'] = $c->getContents(); + } + if (isset($attachments[$num]['charset'])) { + if ($attachments[$num]['charset']===false) $attachments[$num]['charset'] = translation::detect_encoding($attachments[$num]['attachment']); + translation::convert($attachments[$num]['attachment'],$attachments[$num]['charset']); + } + $attachments[$num]['type'] = $attachments[$num]['mimeType']; + $attachments[$num]['tmp_name'] = tempnam($GLOBALS['egw_info']['server']['temp_dir'],$GLOBALS['egw_info']['flags']['currentapp']."_"); + $tmpfile = fopen($attachments[$num]['tmp_name'],'w'); + fwrite($tmpfile,$attachments[$num]['attachment']); + fclose($tmpfile); + unset($attachments[$num]['attachment']); + } + } + if (is_array($attachedMessages)) $attachments = array_merge($attachments,$attachedMessages); + } + return array( + 'mailaddress'=>$mailaddress, + 'subject'=>$subject, + 'message'=>$message, + 'attachments'=>$attachments, + 'headers'=>$headers, + ); + } + + /** + * createHeaderInfoSection - creates a textual headersection from headerobject + * @param array header headerarray may contain SUBJECT,FROM,SENDER,TO,CC,BCC,DATE,PRIORITY,IMPORTANCE + * @param string headline Text tom use for headline, if SUPPRESS, supress headline and footerline + * @param bool createHTML do it with HTML breaks + * @return string a preformatted string with the information of the header worked into it + */ + static function createHeaderInfoSection($header,$headline='', $createHTML = false) + { + $headdata = null; + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($header).function_backtrace()); + if ($header['SUBJECT']) $headdata = lang('subject').': '.$header['SUBJECT'].($createHTML?"
":"\n"); + if ($header['FROM']) $headdata .= lang('from').': '.self::convertAddressArrayToString($header['FROM'], $createHTML).($createHTML?"
":"\n"); + if ($header['SENDER']) $headdata .= lang('sender').': '.self::convertAddressArrayToString($header['SENDER'], $createHTML).($createHTML?"
":"\n"); + if ($header['TO']) $headdata .= lang('to').': '.self::convertAddressArrayToString($header['TO'], $createHTML).($createHTML?"
":"\n"); + if ($header['CC']) $headdata .= lang('cc').': '.self::convertAddressArrayToString($header['CC'], $createHTML).($createHTML?"
":"\n"); + if ($header['BCC']) $headdata .= lang('bcc').': '.self::convertAddressArrayToString($header['BCC'], $createHTML).($createHTML?"
":"\n"); + if ($header['DATE']) $headdata .= lang('date').': '.$header['DATE'].($createHTML?"
":"\n"); + if ($header['PRIORITY'] && $header['PRIORITY'] != 'normal') $headdata .= lang('priority').': '.$header['PRIORITY'].($createHTML?"
":"\n"); + if ($header['IMPORTANCE'] && $header['IMPORTANCE'] !='normal') $headdata .= lang('importance').': '.$header['IMPORTANCE'].($createHTML?"
":"\n"); + //if ($mailcontent['headers']['ORGANIZATION']) $headdata .= lang('organization').': '.$mailcontent['headers']['ORGANIZATION']."\ + if (!empty($headdata)) + { + if (!empty($headline) && $headline != 'SUPPRESS') $headdata = "---------------------------- $headline ----------------------------".($createHTML?"
":"\n").$headdata; + if (empty($headline)) $headdata = ($headline != 'SUPPRESS'?"--------------------------------------------------------".($createHTML?"
":"\n"):'').$headdata; + $headdata .= ($headline != 'SUPPRESS'?"--------------------------------------------------------".($createHTML?"
":"\n"):''); + } + else + { + $headdata = ($headline != 'SUPPRESS'?"--------------------------------------------------------".($createHTML?"
":"\n"):''); + } + return $headdata; + } + + /** + * adaptSubjectForImport - strips subject from unwanted Characters, and does some normalization + * to meet expectations + * @param string $subject string to process + * @return string + */ + static function adaptSubjectForImport($subject) + { + $subject = str_replace('$$','__',($subject?$subject:lang('(no subject)'))); + $subject = str_ireplace(array('[FWD]','[',']','{','}','<','>'),array('Fwd:',' ',' ',' ',' ',' ',' '),trim($subject)); + return $subject; + } + + /** + * convertAddressArrayToString - converts an mail envelope Address Array To String + * @param array $rfcAddressArray an addressarray as provided by mail retieved via egw_pear.... + * @return string a comma separated string with the mailaddress(es) converted to text + */ + static function convertAddressArrayToString($rfcAddressArray, $createHTML = false) + { + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($rfcAddressArray)); + $returnAddr =''; + if (is_array($rfcAddressArray)) + { + foreach((array)$rfcAddressArray as $addressData) { + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($addressData)); + if($addressData['MAILBOX_NAME'] == 'NIL') { + continue; + } + if(strtolower($addressData['MAILBOX_NAME']) == 'undisclosed-recipients') { + continue; + } + if ($addressData['RFC822_EMAIL']) + { + $addressObjectA = imap_rfc822_parse_adrlist((get_magic_quotes_gpc()?stripslashes($addressData['RFC822_EMAIL']):$addressData['RFC822_EMAIL']),''); + } + else + { + $emailaddress = ($addressData['PERSONAL_NAME']?$addressData['PERSONAL_NAME'].' <'.$addressData['EMAIL'].'>':$addressData['EMAIL']); + $addressObjectA = imap_rfc822_parse_adrlist((get_magic_quotes_gpc()?stripslashes($emailaddress):$emailaddress),''); + } + $addressObject = $addressObjectA[0]; + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($addressObject)); + if ($addressObject->host == '.SYNTAX-ERROR.') continue; + //$mb =(string)$addressObject->mailbox; + //$h = (string)$addressObject->host; + //$p = (string)$addressObject->personal; + $returnAddr .= (strlen($returnAddr)>0?',':''); + //error_log(__METHOD__.' ('.__LINE__.') '.$p.' <'.$mb.'@'.$h.'>'); + $buff = imap_rfc822_write_address($addressObject->mailbox, self::$idna2->decode($addressObject->host), $addressObject->personal); + $buff = str_replace(array('<','>'),array('[',']'),$buff); + if ($createHTML) $buff = emailadmin_imapbase::htmlspecialchars($buff); + //error_log(__METHOD__.' ('.__LINE__.') '.' Address: '.$returnAddr); + $returnAddr .= $buff; + } + } + else + { + // do not mess with strings, return them untouched /* ToDo: validate string as Address */ + $rfcAddressArray = self::decode_header($rfcAddressArray,true); + $rfcAddressArray = str_replace(array('<','>'),array('[',']'),$rfcAddressArray); + if (is_string($rfcAddressArray)) return ($createHTML ? emailadmin_imapbase::htmlspecialchars($rfcAddressArray) : $rfcAddressArray); + } + return $returnAddr; + } + + /** + * Merges a given content with contact data + * + * @param string $content + * @param array $ids array with contact id(s) + * @param string &$err error-message on error + * @return string/boolean merged content or false on error + */ + function merge($content,$ids,$mimetype='') + { + $contacts = new addressbook_bo(); + $mergeobj = new addressbook_merge(); + + if (empty($mimetype)) $mimetype = (strlen(strip_tags($content)) == strlen($content) ?'text/plain':'text/html'); + $rv = $mergeobj->merge_string($content,$ids,$err,$mimetype, array(), self::$displayCharset); + if (empty($rv) && !empty($content) && !empty($err)) $rv = $content; + if (!empty($err) && !empty($content) && !empty($ids)) error_log(__METHOD__.' ('.__LINE__.') '.' Merge failed for Ids:'.array2string($ids).' ContentType:'.$mimetype.' Content:'.$content.' Reason:'.array2string($err)); + return $rv; + } + + /** + * Returns a string showing the size of the message/attachment + * + * @param integer $bytes + * @return string formatted string + */ + static function show_readable_size($bytes) + { + $bytes /= 1024; + $type = 'k'; + + if ($bytes / 1024 > 1) + { + $bytes /= 1024; + $type = 'M'; + + if ($bytes / 1024 > 1) + { + $bytes *= 10; + settype($bytes, 'integer'); + $bytes /= 10; + $bytes /= 1024; + $type = 'G'; + } + + } + + if ($bytes < 10) + { + $bytes *= 10; + settype($bytes, 'integer'); + $bytes /= 10; + } + else + settype($bytes, 'integer'); + + return $bytes . ' ' . $type ; + } + + static function detect_qp(&$sting) { + $needle = '/(=[0-9][A-F])|(=[A-F][0-9])|(=[A-F][A-F])|(=[0-9][0-9])/'; + return preg_match("$needle",$string); + } + + /** + * logRunTimes + * logs to the error log all parameters given; output only if self::$debugTimes is true + * + * @param int $_starttime starttime of the action measured based on microtime(true) + * @param int $_endtime endtime of the action measured, if not given microtime(true) is used + * @param string $_message message to output details or params, whatever seems neccesary + * @param string $_methodNline - Information where the log was taken + * @return void + */ + static function logRunTimes($_starttime,$_endtime=null,$_message='',$_methodNline='') + { + if (is_null($_endtime)) $_endtime = microtime(true); + $usagetime = microtime(true) - $_starttime; + if (self::$debugTimes) error_log($_methodNline.' took:'.number_format($usagetime,5).'(s) '.($_message?'Details:'.$_message:'')); + } + + /** + * checkFileBasics + * check if formdata meets basic restrictions (in tmp dir, or vfs, mimetype, etc.) + * + * @param array $_formData passed by reference Array with information of name, type, file and size, mimetype may be adapted + * @param string $IDtoAddToFileName id to enrich the returned tmpfilename + * @param string $reqMimeType /(default message/rfc822, if set to false, mimetype check will not be performed + * @return mixed $fullPathtoFile or exception + */ + static function checkFileBasics(&$_formData, $IDtoAddToFileName='', $reqMimeType='message/rfc822') + { + //error_log(__METHOD__.__FILE__.array2string($_formData).' Id:'.$IDtoAddToFileName.' ReqMimeType:'.$reqMimeType); + $importfailed = $tmpFileName = false; + if ($_formData['size'] != 0 && (is_uploaded_file($_formData['file']) || + realpath(dirname($_formData['file'])) == realpath($GLOBALS['egw_info']['server']['temp_dir']) || + parse_url($_formData['file'],PHP_URL_SCHEME) == 'vfs')) + { + // ensure existance of eGW temp dir + // note: this is different from apache temp dir, + // and different from any other temp file location set in php.ini + if (!file_exists($GLOBALS['egw_info']['server']['temp_dir'])) + { + @mkdir($GLOBALS['egw_info']['server']['temp_dir'],0700); + } + + // if we were NOT able to create this temp directory, then make an ERROR report + if (!file_exists($GLOBALS['egw_info']['server']['temp_dir'])) + { + $alert_msg .= 'Error:'.'
' + .'Server is unable to access phpgw tmp directory'.'
' + .$GLOBALS['egw_info']['server']['temp_dir'].'
' + .'Please check your configuration'.'
' + .'
'; + } + + // sometimes PHP is very clue-less about MIME types, and gives NO file_type + // rfc default for unknown MIME type is: + if ($reqMimeType == 'message/rfc822') + { + $mime_type_default = 'message/rfc'; + } + else + { + $mime_type_default = $reqMimeType; + } + // check the mimetype by extension. as browsers seem to report crap + // maybe its application/octet-stream -> this may mean that we could not determine the type + // so we check for the suffix too + // trust vfs mime-types, trust the mimetype if it contains a method + if ((substr($_formData['file'],0,6) !== 'vfs://' || $_formData['type'] == 'application/octet-stream') && stripos($_formData['type'],'method=')===false) + { + $buff = explode('.',$_formData['name']); + $suffix = ''; + if (is_array($buff)) $suffix = array_pop($buff); // take the last extension to check with ext2mime + if (!empty($suffix)) $sfxMimeType = mime_magic::ext2mime($suffix); + if (!empty($suffix) && !empty($sfxMimeType) && + (strlen(trim($_formData['type']))==0 || (strtolower(trim($_formData['type'])) != $sfxMimeType))) + { + error_log(__METHOD__.' ('.__LINE__.') '.' Data:'.array2string($_formData)); + error_log(__METHOD__.' ('.__LINE__.') '.' Form reported Mimetype:'.$_formData['type'].' but seems to be:'.$sfxMimeType); + $_formData['type'] = $sfxMimeType; + } + } + if (trim($_formData['type']) == '') + { + $_formData['type'] = 'application/octet-stream'; + } + // if reqMimeType is set to false do not test for that + if ($reqMimeType) + { + // so if PHP did not pass any file_type info, then substitute the rfc default value + if (substr(strtolower(trim($_formData['type'])),0,strlen($mime_type_default)) != $mime_type_default) + { + if (!(strtolower(trim($_formData['type'])) == "application/octet-stream" && $sfxMimeType == $reqMimeType)) + { + //error_log("Message rejected, no message/rfc. Is:".$_formData['type']); + $importfailed = true; + $alert_msg .= lang("File rejected, no %2. Is:%1",$_formData['type'],$reqMimeType); + } + if ((strtolower(trim($_formData['type'])) != $reqMimeType && $sfxMimeType == $reqMimeType)) + { + $_formData['type'] = mime_magic::ext2mime($suffix); + } + } + } + // as FreeBSD seems to have problems with the generated temp names we append some more random stuff + $randomString = chr(rand(65,90)).chr(rand(48,57)).chr(rand(65,90)).chr(rand(48,57)).chr(rand(65,90)); + $tmpFileName = $GLOBALS['egw_info']['server']['temp_dir']. + SEP. + $GLOBALS['egw_info']['user']['account_id']. + trim($IDtoAddToFileName).basename($_formData['file']).'_'.$randomString; + + if (parse_url($_formData['file'],PHP_URL_SCHEME) == 'vfs') + { + $tmpFileName = $_formData['file']; // no need to store it somewhere + } + elseif (is_uploaded_file($_formData['file'])) + { + move_uploaded_file($_formData['file'],$tmpFileName); // requirement for safe_mode! + } + else + { + rename($_formData['file'],$tmpFileName); + } + } else { + //error_log("Import of message ".$_formData['file']." failes to meet basic restrictions"); + $importfailed = true; + $alert_msg .= lang("Processing of file %1 failed. Failed to meet basic restrictions.",$_formData['name']); + } + if ($importfailed == true) + { + throw new egw_exception_wrong_userinput($alert_msg); + } + else + { + if (parse_url($tmpFileName,PHP_URL_SCHEME) == 'vfs') + { + egw_vfs::load_wrapper('vfs'); + } + return $tmpFileName; + } + } + + /** + * processURL2InlineImages - parses a html text for images, and adds them as inline attachment + * we do not use the functionality of the phpmailer here, as phpmailers functionality requires + * files to be present within the filesystem, which we do not require as we make this happen + * (we load the file, and store it temporarily for the use of attaching it to the file send + * @param object $_mailObject instance of the egw_mailer/phpmailer Object to be used + * @param string $_html2parse the html to parse and to be altered, if conditions meet + * @return void + */ + static function processURL2InlineImages(&$_mailObject, &$_html2parse) + { + $imageC = 0; + preg_match_all("/(src|background)=\"(.*)\"/Ui", $_html2parse, $images); + if(isset($images[2])) { + foreach($images[2] as $i => $url) { + //$isData = false; + $basedir = ''; + $needTempFile = true; + //error_log(__METHOD__.' ('.__LINE__.') '.$url); + //error_log(__METHOD__.' ('.__LINE__.') '.$GLOBALS['egw_info']['server']['webserver_url']); + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($GLOBALS['egw_info']['user'])); + // do not change urls for absolute images (thanks to corvuscorax) + if (!(substr($url,0,strlen('data:'))=='data:')) { + //error_log(__METHOD__.' ('.__LINE__.') '.' -> '.$i.': '.array2string($images[$i])); + $filename = basename($url); + $directory = dirname($url); + ($directory == '.')?$directory='':''; + $cid = 'cid:' . md5($filename); + $ext = pathinfo($filename, PATHINFO_EXTENSION); + $mimeType = $_mailObject->_mime_types($ext); + if ( strlen($directory) > 1 && substr($directory,-1) != '/') { $directory .= '/'; } + $myUrl = $directory.$filename; + if ($myUrl[0]=='/') // local path -> we only allow path's that are available via http/https (or vfs) + { + $basedir = ($_SERVER['HTTPS']?'https://':'http://'.$_SERVER['HTTP_HOST']); + } + // use vfs instead of url containing webdav.php + // ToDo: we should test if the webdav url is of our own scope, as we cannot handle foreign + // webdav.php urls as vfs + if (strpos($myUrl,'webdav.php') !== false) // we have a webdav link, so we build a vfs/sqlfs link of it. + { + egw_vfs::load_wrapper('vfs'); + list($garbage,$vfspart) = explode('webdav.php',$myUrl,2); + $myUrl = $vfspart; + $basedir = 'vfs://default'; + $needTempFile = false; + } + if ( strlen($basedir) > 1 && substr($basedir,-1) != '/' && $myUrl[0]!='/') { $basedir .= '/'; } + //error_log(__METHOD__.' ('.__LINE__.') '.$basedir.$myUrl); + if ($needTempFile) $data = file_get_contents($basedir.urldecode($myUrl)); + } + if (substr($url,0,strlen('data:'))=='data:') + { + //error_log(__METHOD__.' ('.__LINE__.') '.' -> '.$i.': '.array2string($images[$i])); + // we only support base64 encoded data + $tmp = substr($url,strlen('data:')); + list($mimeType,$data) = explode(';base64,',$tmp); + list($what,$exactly) = explode('/',$mimeType); + $needTempFile = true; + $filename = ($what?$what:'data').$imageC++.'.'.$exactly; + $cid = 'cid:' . md5($filename); + $data = base64_decode($data); + //$isData = true; + } + if ($data || $needTempFile === false) + { + if ($needTempFile) + { + $attachment_file =tempnam($GLOBALS['egw_info']['server']['temp_dir'],$GLOBALS['egw_info']['flags']['currentapp']."_"); + $tmpfile = fopen($attachment_file,'w'); + fwrite($tmpfile,$data); + fclose($tmpfile); + } + else + { + $attachment_file = $basedir.urldecode($myUrl); + } + //error_log(__METHOD__.' ('.__LINE__.') '.' '.$url.' -> '.$basedir.$myUrl. ' TmpFile:'.$tmpfile); + //error_log(__METHOD__.' ('.__LINE__.') '.' '.$url.' -> '.$mimeType. ' TmpFile:'.$attachment_file); + if ( $_mailObject->AddEmbeddedImage($attachment_file, md5($filename), $filename, 'base64',$mimeType) ) + { + //$_html2parse = preg_replace("/".$images[1][$i]."=\"".preg_quote($url, '/')."\"/Ui", $images[1][$i]."=\"".$cid."\"", $_html2parse); + $_html2parse = str_replace($images[1][$i]."=\"".$url."\"", $images[1][$i]."=\"".$cid."\"", $_html2parse); + } + } + } + } + } + + /** + * importMessageToMergeAndSend + * + * @param object &bo_merge bo_merge object + * @param string $document the full filename + * @param array $SendAndMergeTocontacts array of contact ids + * @param string $_folder (passed by reference) will set the folder used. must be set with a folder, but will hold modifications if + * folder is modified + * @param string $importID ID for the imported message, used by attachments to identify them unambiguously + * @return mixed array of messages with success and failed messages or exception + */ + function importMessageToMergeAndSend(bo_merge $bo_merge, $document, $SendAndMergeTocontacts, &$_folder, &$importID='') + { + $importfailed = false; + $processStats = array('success'=>array(),'failed'=>array()); + if (empty($SendAndMergeTocontacts)) + { + $importfailed = true; + $alert_msg .= lang("Import of message %1 failed. No Contacts to merge and send to specified.",$_formData['name']); + } + + // check if formdata meets basic restrictions (in tmp dir, or vfs, mimetype, etc.) + /* as the file is provided by bo_merge, we do not check + try + { + $tmpFileName = emailadmin_imapbase::checkFileBasics($_formData,$importID); + } + catch (egw_exception_wrong_userinput $e) + { + $importfailed = true; + $alert_msg .= $e->getMessage(); + } + */ + $tmpFileName = $document; + // ----------------------------------------------------------------------- + if ($importfailed === false) + { + $mailObject = new egw_mailer(); + try + { + $this->parseFileIntoMailObject($mailObject,$tmpFileName,$Header,$Body); + } + catch (egw_exception_assertion_failed $e) + { + $importfailed = true; + $alert_msg .= $e->getMessage(); + } + + //_debug_array($Body); + $this->openConnection(); + if (empty($_folder)) + { + $_folder = $this->getSentFolder(); + } + $delimiter = $this->getHierarchyDelimiter(); + if($_folder=='INBOX'.$delimiter) $_folder='INBOX'; + if ($importfailed === false) + { + $Subject = $mailObject->Subject; + //error_log(__METHOD__.' ('.__LINE__.') '.' Subject:'.$Subject); + $Body = $mailObject->Body; + //error_log(__METHOD__.' ('.__LINE__.') '.' Body:'.$Body); + //error_log(__METHOD__.' ('.__LINE__.') '.' BodyContentType:'.$mailObject->BodyContentType); + $AltBody = $mailObject->AltBody; + //error_log(__METHOD__.' ('.__LINE__.') '.' AltBody:'.$AltBody); + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($mailObject->GetReplyTo())); + // Fetch ReplyTo - Address if existing to check if we are to replace it + $replyTo = $mailObject->GetReplyTo(); + if (isset($replyTo['replace@import.action'])) + { + $mailObject->ClearReplyTos(); + $activeMailProfiles = $this->mail->getAccountIdentities($this->profileID); + $activeMailProfile = array_shift($activeMailProfiles); + + $mailObject->AddReplyTo(self::$idna2->encode($activeMailProfile['ident_email']),emailadmin_imapbase::generateIdentityString($activeMailProfile,false)); + } + foreach ($SendAndMergeTocontacts as $k => $val) + { + $mailObject->ErrorInfo = $errorInfo = ''; + //$mailObject->SMTPDebug = 5; + $mailObject->set('error_count',0); + $sendOK = $openComposeWindow = $openAsDraft = null; + //error_log(__METHOD__.' ('.__LINE__.') '.' Id To Merge:'.$val); + if (/*$GLOBALS['egw_info']['flags']['currentapp'] == 'addressbook' &&*/ + count($SendAndMergeTocontacts) > 1 && + is_numeric($val) || $GLOBALS['egw']->accounts->name2id($val)) // do the merge + { + + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($mailObject)); + $contact = $bo_merge->contacts->read($val); + //error_log(__METHOD__.' ('.__LINE__.') '.' ID:'.$val.' Data:'.array2string($contact)); + $email = ($contact['email'] ? $contact['email'] : $contact['email_home']); + $nfn = ($contact['n_fn'] ? $contact['n_fn'] : $contact['n_given'].' '.$contact['n_family']); + $activeMailProfiles = $this->getAccountIdentities($this->profileID); + $activeMailProfile = array_shift($activeMailProfiles); + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($activeMailProfile)); + $mailObject->From = $activeMailProfile['ident_email']; + //$mailObject->From = $_identity->emailAddress; + $mailObject->FromName = $mailObject->EncodeHeader(self::generateIdentityString($activeMailProfile,false)); + + $mailObject->MessageID = ''; + $mailObject->ClearAllRecipients(); + $mailObject->ClearCustomHeaders(); + $mailObject->AddAddress(self::$idna2->encode($email),$mailObject->EncodeHeader($nfn)); + $mailObject->Subject = $bo_merge->merge_string($Subject, $val, $e, 'text/plain', array(), self::$displayCharset); + if (!empty($AltBody)) + { + $mailObject->IsHTML(true); + } + elseif (empty($AltBody) && $mailObject->BodyContentType=='text/html') + { + $mailObject->IsHTML(true); + $AltBody = translation::convertHTMLToText($Body,self::$displayCharset,false,$stripalltags=true); + } + else + { + $mailObject->IsHTML(false); + } + //error_log(__METHOD__.' ('.__LINE__.') '.' ContentType:'.$mailObject->BodyContentType); + if (!empty($Body)) $mailObject->Body = $bo_merge->merge_string($Body, $val, $e, $mailObject->BodyContentType, array(), self::$displayCharset); + //error_log(__METHOD__.' ('.__LINE__.') '.' Result:'.$mailObject->Body.' error:'.array2string($e)); + if (!empty($AltBody)) $mailObject->AltBody = $bo_merge->merge_string($AltBody, $val, $e, $mailObject->AltBodyContentType, array(), self::$displayCharset); + + $ogServer = emailadmin_account::read($this->profileID)->smtpServer(); + #_debug_array($ogServer); + $mailObject->Host = $ogServer->host; + $mailObject->Port = $ogServer->port; + // SMTP Auth?? + if($ogServer->smtpAuth) { + $mailObject->SMTPAuth = true; + // check if username contains a ; -> then a sender is specified (and probably needed) + list($username,$senderadress) = explode(';', $ogServer->username,2); + if (isset($senderadress) && !empty($senderadress)) $mailObject->Sender = $senderadress; + $mailObject->Username = $username; + $mailObject->Password = $ogServer->password; + } + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($mailObject)); + // set a higher timeout for big messages + @set_time_limit(120); + $sendOK = true; + try { + $mailObject->Send(); + } + catch(phpmailerException $e) { + $sendOK = false; + $errorInfo = $e->getMessage(); + if ($mailObject->ErrorInfo) // use the complete mailer ErrorInfo, for full Information + { + if (stripos($mailObject->ErrorInfo, $errorInfo)===false) + { + $errorInfo = 'Send Failed for '.$mailObject->Subject.' to '.$nfn.'<'.$email.'> Error:'.$mailObject->ErrorInfo.'
'.$errorInfo; + } + else + { + $errorInfo = 'Send Failed for '.$mailObject->Subject.' to '.$nfn.'<'.$email.'> Error:'.$mailObject->ErrorInfo; + } + } + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($errorInfo)); + } + } + elseif (!$k) // 1. entry, further entries will fail for apps other then addressbook + { + $openAsDraft = true; + $mailObject->MessageID = ''; + $mailObject->ClearAllRecipients(); + $mailObject->ClearCustomHeaders(); + if (/*$GLOBALS['egw_info']['flags']['currentapp'] == 'addressbook' &&*/ + is_numeric($val) || $GLOBALS['egw']->accounts->name2id($val)) // do the merge + { + $contact = $bo_merge->contacts->read($val); + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($contact)); + $email = ($contact['email'] ? $contact['email'] : $contact['email_home']); + $nfn = ($contact['n_fn'] ? $contact['n_fn'] : $contact['n_given'].' '.$contact['n_family']); + $mailObject->AddAddress(self::$idna2->encode($email),$mailObject->EncodeHeader($nfn)); + } + $mailObject->Subject = $bo_merge->merge_string($Subject, $val, $e, 'text/plain', array(), self::$displayCharset); + if (!empty($AltBody)) + { + $mailObject->IsHTML(true); + } + elseif (empty($AltBody) && $mailObject->BodyContentType=='text/html') + { + $mailObject->IsHTML(true); + $AltBody = translation::convertHTMLToText($Body,self::$displayCharset,false,$stripalltags=true); + } + else + { + $mailObject->IsHTML(false); + } + //error_log(__METHOD__.' ('.__LINE__.') '.' ContentType:'.$mailObject->BodyContentType); + if (!empty($Body)) $mailObject->Body = $bo_merge->merge_string($Body, $val, $e, $mailObject->BodyContentType, array(), self::$displayCharset); + //error_log(__METHOD__.' ('.__LINE__.') '.' Result:'.$mailObject->Body.' error:'.array2string($e)); + if (!empty($AltBody)) $mailObject->AltBody = $bo_merge->merge_string($AltBody, $val, $e, $mailObject->AltBodyContentType, array(), self::$displayCharset); + $_folder = $this->getDraftFolder(); + } + if ($sendOK || $openAsDraft) + { + $BCCmail = ''; + if ($this->folderExists($_folder,true)) + { + if($this->isSentFolder($_folder)) + { + $flags = '\\Seen'; + } elseif($this->isDraftFolder($_folder)) { + $flags = '\\Draft'; + } else { + $flags = ''; + } + unset($mailObject->sentHeader); + unset($mailObject->sentBody); + $savefailed = false; + try + { + $messageUid =$this->appendMessage($_folder, + $BCCmail.$mailObject->getMessageHeader(), + $mailObject->getMessageBody(), + $flags); + } + catch (egw_exception_wrong_userinput $e) + { + $savefailed = true; + $alert_msg .= lang("Save of message %1 failed. Could not save message to folder %2 due to: %3",$Subject,$_folder,$e->getMessage()); + } + // no send, save successful, and message_uid present + if ($savefailed===false && $messageUid && is_null($sendOK)) + { + $importID = $messageUid; + $openComposeWindow = true; + } + } + else + { + $savefailed = true; + $alert_msg .= lang("Saving of message %1 failed. Destination Folder %2 does not exist.",$Subject,$_folder); + } + if ($sendOK) + { + $processStats['success'][$val] = 'Send succeeded to '.$nfn.'<'.$email.'>'.($savefailed?' but failed to store to Folder:'.$_folder:''); + } + else + { + if (!$openComposeWindow) $processStats['failed'][$val] = $errorInfo?$errorInfo:'Send failed to '.$nfn.'<'.$email.'> See error_log for details'; + } + } + if (!is_null($sendOK) && $sendOK===false && is_null($openComposeWindow)) + { + $processStats['failed'][$val] = $errorInfo?$errorInfo:'Send failed to '.$nfn.'<'.$email.'> See error_log for details'; + } + } + } + unset($mailObject); + } + // set the url to open when refreshing + if ($importfailed == true) + { + throw new egw_exception_wrong_userinput($alert_msg); + } + else + { + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($processStats)); + return $processStats; + } + } + + /** + * functions to allow the parsing of message/rfc files + * used in felamimail to import mails, or parsev a message from file enrich it with addressdata (merge) and send it right away. + */ + + /** + * parseFileIntoMailObject - parses a message/rfc mail from file to the mailobject and returns the header and body via reference + * throws egw_exception_assertion_failed when the required Pear Class is not found/loadable + * @param object $mailObject instance of the SMTP Mailer Object + * @param string $tmpFileName string that points/leads to the file to be imported + * @param string &$Header reference used to return the imported Mailheader + * @param string &$Body reference to return the imported Body + * @return void Mailheader and body is returned via Reference in $Header $Body + */ + function parseFileIntoMailObject($mailObject,$tmpFileName,&$Header,&$Body) + { + $message = file_get_contents($tmpFileName); + try + { + return $this->parseRawMessageIntoMailObject($mailObject,$message,$Header,$Body); + } + catch (egw_exception_assertion_failed $e) + { // not sure that this is needed to pass on exeptions + throw new egw_exception_assertion_failed($e->getMessage()); + } + } + + /** + * parseRawMessageIntoMailObject - parses a message/rfc mail from file to the mailobject and returns the header and body via reference + * throws egw_exception_assertion_failed when the required Pear Class is not found/loadable + * @param object $mailObject instance of the SMTP Mailer Object + * @param string $message string containing the RawMessage + * @param string &$Header reference used to return the imported Mailheader + * @param string &$Body reference to return the imported Body + * @return void Mailheader and body is returned via Reference in $Header $Body + */ + function parseRawMessageIntoMailObject($mailObject,$message,&$Header,&$Body) + { + /** + * pear/Mail_mimeDecode requires package "pear/Mail_Mime" (version >= 1.4.0, excluded versions: 1.4.0) + * ./pear upgrade Mail_Mime + * ./pear install Mail_mimeDecode + */ + //echo '
'.$message.'
'; + //error_log(__METHOD__.' ('.__LINE__.') '.$message); + if (class_exists('Mail_mimeDecode',false)==false && (@include_once 'Mail/mimeDecode.php') === false) throw new egw_exception_assertion_failed(lang('Required PEAR class Mail/mimeDecode.php not found.')); + $mailDecode = new Mail_mimeDecode($message); + $structure = $mailDecode->decode(array('include_bodies'=>true,'decode_bodies'=>true,'decode_headers'=>true)); + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($structure)); + //_debug_array($structure); + //exit; + // now create a message to view, save it in Drafts and open it + $mailObject->PluginDir = EGW_SERVER_ROOT."/phpgwapi/inc/"; + $mailObject->IsSMTP(); + $mailObject->CharSet = self::$displayCharset; // some default, may be altered by BodyImport + if (isset($structure->ctype_parameters['charset'])) $mailObject->CharSet = trim($structure->ctype_parameters['charset']); + $mailObject->Encoding = 'quoted-printable'; // some default, may be altered by BodyImport +/* + $mailObject->AddAddress($emailAddress, $addressObject->personal); + $mailObject->AddCC($emailAddress, $addressObject->personal); + $mailObject->AddBCC($emailAddress, $addressObject->personal); + $mailObject->AddReplyto($emailAddress, $addressObject->personal); +*/ + $result =''; + $contenttypecalendar = ''; + $myReplyTo = ''; + foreach((array)$structure->headers as $key => $val) + { + //error_log(__METHOD__.' ('.__LINE__.') '.$key.'->'.$val); + foreach((array)$val as $i => $v) + { + if ($key!='content-type' && $key !='content-transfer-encoding' && + $key != 'message-id' && + $key != 'subject' && + $key != 'from' && + $key != 'to' && + $key != 'cc' && + $key != 'bcc' && + $key != 'reply-to' && + $key != 'x-priority') // the omitted values to that will be set at the end + { + $Header .= $mailObject->HeaderLine($key, trim($v)); + } + } + switch ($key) + { + case 'x-priority': + $mailObject->Priority = $val; + break; + case 'message-id': + $mailObject->MessageID = $val; // ToDo: maybe we want to regenerate the message id all the time + break; + case 'sender': + $mailObject->Sender = $val; + break; + case 'to': + case 'cc': + case 'bcc': + case 'from': + case 'reply-to': + $address_array = imap_rfc822_parse_adrlist((get_magic_quotes_gpc()?stripslashes($val):$val),''); + $i = 0; + foreach((array)$address_array as $addressObject) + { + $mb = $addressObject->mailbox. (!empty($addressObject->host) ? '@'.$addressObject->host : ''); + $pName = $addressObject->personal; + if ($key=='from') + { + $mailObject->From = $mb; + $mailObject->FromName = $pName; + } + ${$key}[$i] = array($mb,$pName); + $i++; + } + if ($key=='reply-to') + { + $myReplyTo = ${$key}; + //break; // break early as we add that later + } + $Header .= $mailObject->TextLine(trim($mailObject->AddrAppend(ucfirst($key),${$key}))); + break; + case 'content-transfer-encoding': + $mailObject->Encoding = $val; + break; + case 'content-type': + //error_log(__METHOD__.' ('.__LINE__.') '.' '.$key.'->'.$val); + if (stripos($val,'calendar')) $contenttypecalendar = $val; + break; + case 'subject': + $mailObject->Subject = $mailObject->EncodeHeader($mailObject->SecureHeader($val)); + $Header .= $mailObject->HeaderLine('Subject',$mailObject->Subject); + break; + default: + // stuff like X- ... + //$mailObject->AddCustomHeader('X-Mailer: FeLaMiMail'); + if (!strtolower(substr($key,0,2))=='x-') break; + //case 'priority': // priority is a cusom header field + // $mailObject->Priority = $val; + // break; + case 'disposition-notification-to': + case 'organization': + foreach((array)$val as $i => $v) $mailObject->AddCustomHeader($key.': '. $v); + break; + } + } + // handle reply-to, wich may be set, set the first one found + if (!empty($myReplyTo)) + { + $mailObject->ClearReplyTos(); + $mailObject->AddReplyTo($myReplyTo[0][0],$myReplyTo[0][1]); + } + + $seemsToBePlainMessage = false; + if (strtolower($structure->ctype_primary)=='text' && $structure->body) + { + $mailObject->IsHTML(strtolower($structure->ctype_secondary)=='html'?true:false); + if (strtolower($structure->ctype_primary) == 'text' && strtolower($structure->ctype_secondary) == 'plain' && + is_array($structure->ctype_parameters) && isset($structure->ctype_parameters['format']) && + trim(strtolower($structure->ctype_parameters['format']))=='flowed' + ) + { + if (self::$debug) error_log(__METHOD__.' ('.__LINE__.') '." detected TEXT/PLAIN Format:flowed -> removing leading blank ('\r\n ') per line"); + $structure->body = str_replace("\r\n ","\r\n", $structure->body); + } + $mailObject->Body = $structure->body; + $seemsToBePlainMessage = true; + } + $this->createBodyFromStructure($mailObject, $structure, $parenttype=null); + $mailObject->SetMessageType(); + $mailObject->CreateHeader(); // this sets the boundary stufff + //echo "Boundary:".$mailObject->FetchBoundary(1).'
'; + //$boundary =''; + //if (isset($structure->ctype_parameters['boundary'])) $boundary = ' boundary="'.$mailObject->FetchBoundary(1).'";'; + if ($seemsToBePlainMessage && !empty($contenttypecalendar) && strtolower($mailObject->ContentType)=='text/plain') + { + $Header .= $mailObject->HeaderLine('Content-Transfer-Encoding', $mailObject->Encoding); + $Header .= $mailObject->HeaderLine('Content-type', $contenttypecalendar); + } + else + { + $Header .= $mailObject->GetMailMIME(); + } + $Body = $mailObject->getMessageBody(); // this is a method of the egw_mailer/phpmailer class + //_debug_array($Header); + //_debug_array($Body); + //_debug_array($mailObject); + //exit; + } + + /** + * createBodyFromStructure - fetches/creates the bodypart of the email as textual representation + * is called recursively to be able to fetch the stuctureparts of the mail parsed from Mail/mimeDecode + * @param object $mailObject instance of the SMTP Mailer Object + * @param array $structure array that represents structure and content of a mail parsed from Mail/mimeDecode + * @param string $parenttype type of the parent node + * @return void Parsed Information is passed to the mailObject to be processed there + */ + function createBodyFromStructure($mailObject, $structure, $parenttype=null, $decode=false) + { + static $attachmentnumber; + static $isHTML; + static $alternatebodyneeded; + if (is_null($isHTML)) $isHTML = strtolower($structure->ctype_secondary)=='html'?true:false; + if (is_null($attachmentnumber)) $attachmentnumber = 0; + if ($structure->parts && strtolower($structure->ctype_primary)=='multipart') + { + if (is_null($alternatebodyneeded)) $alternatebodyneeded = false; + foreach($structure->parts as $part) + { + //error_log(__METHOD__.' ('.__LINE__.') '.' Structure Content Type:'.$structure->ctype_primary.'/'.$structure->ctype_secondary.' Decoding:'.($decode?'on':'off')); + //error_log(__METHOD__.' ('.__LINE__.') '.' '.$structure->ctype_primary.'/'.$structure->ctype_secondary.' => '.$part->ctype_primary.'/'.$part->ctype_secondary); + //error_log(__METHOD__.' ('.__LINE__.') '.' Part:'.array2string($part)); + $partFetched = false; + //echo __METHOD__.' ('.__LINE__.') '.$structure->ctype_primary.'/'.$structure->ctype_secondary.'
'; + if ($part->headers['content-transfer-encoding']) $mailObject->Encoding = $part->headers['content-transfer-encoding']; + //$mailObject->IsHTML($part->ctype_secondary=='html'?true:false); // we do not set this here, as the default is text/plain + if (isset($part->ctype_parameters['charset'])) $mailObject->CharSet = trim($part->ctype_parameters['charset']); + if ((strtolower($structure->ctype_secondary)=='alternative'|| + strtolower($structure->ctype_secondary)=='mixed' || + // strtolower($structure->ctype_secondary)=='related' || // may hold text/plain directly ?? I doubt it ?? + strtolower($structure->ctype_secondary)=='signed') && strtolower($part->ctype_primary)=='text' && strtolower($part->ctype_secondary)=='plain' && $part->body) + { + //echo __METHOD__.' ('.__LINE__.') '.$part->ctype_primary.'/'.$part->ctype_secondary.'
'; + //error_log(__METHOD__.' ('.__LINE__.') '.$part->ctype_primary.'/'.$part->ctype_secondary.' already fetched Content is HTML='.$isHTML.' Body:'.$part->body); + $bodyPart = $part->body; + if ($decode) $bodyPart = $this->decodeMimePart($part->body,($part->headers['content-transfer-encoding']?$part->headers['content-transfer-encoding']:'WeDontKnowTheEncoding')); +/* + if (strtolower($part->ctype_primary) == 'text' && strtolower($part->ctype_secondary) == 'plain' && + is_array($part->ctype_parameters) && isset($part->ctype_parameters['format']) && + trim(strtolower($part->ctype_parameters['format']))=='flowed' + ) + { + if (self::$debug) error_log(__METHOD__.' ('.__LINE__.') '." detected TEXT/PLAIN Format:flowed -> removing leading blank ('\r\n ') per line"); + $bodyPart = str_replace("\r\n ","\r\n", $bodyPart); + } +*/ + $mailObject->Body = ($isHTML==false?$mailObject->Body:'').$bodyPart; + $mailObject->AltBody .= $bodyPart; + $partFetched = true; + } + if ((strtolower($structure->ctype_secondary)=='alternative'|| + strtolower($structure->ctype_secondary)=='mixed' || + strtolower($structure->ctype_secondary)=='related' || // may hold text/html directly + strtolower($structure->ctype_secondary)=='signed' ) && + strtolower($part->ctype_primary)=='text' && strtolower($part->ctype_secondary)=='html' && $part->body) + { + //echo __METHOD__.' ('.__LINE__.') '.$part->ctype_primary.'/'.$part->ctype_secondary.'
'; + //error_log(__METHOD__.' ('.__LINE__.') '.$part->ctype_primary.'/'.$part->ctype_secondary.' already fetched Content is HTML='.$isHTML.' Body:'.$part->body); + $bodyPart = $part->body; + if ($decode) $bodyPart = $this->decodeMimePart($part->body,($part->headers['content-transfer-encoding']?$part->headers['content-transfer-encoding']:'WeDontKnowTheEncoding')); + $mailObject->IsHTML(true); // we need/want that here, because looping through all message parts may mess up the message body mimetype + $mailObject->Body = ($isHTML?$mailObject->Body:'').$bodyPart; + $alternatebodyneeded = true; + $isHTML=true; + $partFetched = true; + } + if ((strtolower($structure->ctype_secondary)=='alternative'|| + strtolower($structure->ctype_secondary)=='mixed' || + strtolower($structure->ctype_secondary)=='signed' ) && + strtolower($part->ctype_primary)=='text' && strtolower($part->ctype_secondary)=='calendar' && $part->body) + { + //error_log(__METHOD__.' ('.__LINE__.') '.$part->ctype_primary.'/'.$part->ctype_secondary.' BodyPart:'.array2string($part)); + $bodyPart = $part->body; + if ($decode) $bodyPart = $this->decodeMimePart($part->body,($part->headers['content-transfer-encoding']?$part->headers['content-transfer-encoding']:'WeDontKnowTheEncoding')); + $mailObject->AltExtended = $bodyPart; + // "text/calendar; charset=utf-8; name=meeting.ics; method=REQUEST" + // [ctype_parameters] => Array([charset] => utf-8[name] => meeting.ics[method] => REQUEST) + $mailObject->AltExtendedContentType = $part->ctype_primary.'/'.$part->ctype_secondary.';'. + ($part->ctype_parameters['name']?' name='.$part->ctype_parameters['name'].';':''). + ($part->ctype_parameters['method']?' method='.$part->ctype_parameters['method'].'':''); + $partFetched = true; + } + if ((strtolower($structure->ctype_secondary)=='mixed' || + strtolower($structure->ctype_secondary)=='related' || + strtolower($structure->ctype_secondary)=='alternative' || + strtolower($structure->ctype_secondary)=='signed') && strtolower($part->ctype_primary)=='multipart') + { + //error_log( __METHOD__.' ('.__LINE__.') '." Recursion to fetch subparts:".$part->ctype_primary.'/'.$part->ctype_secondary); + $this->createBodyFromStructure($mailObject, $part, $parenttype=null, $decode); + } + //error_log(__METHOD__.' ('.__LINE__.') '.$structure->ctype_primary.'/'.$structure->ctype_secondary.' => '.$part->ctype_primary.'/'.$part->ctype_secondary.' Part:'.array2string($part)); + if ($part->body && ((strtolower($structure->ctype_secondary)=='mixed' && strtolower($part->ctype_primary)!='multipart') || + trim(strtolower($part->disposition)) == 'attachment' || + trim(strtolower($part->disposition)) == 'inline' || + isset($part->headers['content-id']))) + { + //error_log(__METHOD__.' ('.__LINE__.') '.$structure->ctype_secondary.'=>'.$part->ctype_primary.'/'.$part->ctype_secondary.'->'.array2string($part)); + $attachmentnumber++; + $filename = trim(($part->ctype_parameters['name']?$part->ctype_parameters['name']:$part->d_parameters['filename'])); + if (strlen($filename)==0) + { + //error_log(__METHOD__.' ('.__LINE__.') '.$structure->ctype_secondary.'=>'.$part->ctype_primary.'/'.$part->ctype_secondary.'->'.array2string($part)); + foreach(array('content-type','content-disposition') as $k => $v) + { + foreach(array('filename','name') as $sk => $n) + { + if (stripos($part->headers[$v],$n)!== false) + { + $buff = explode($n,$part->headers[$v]); + //error_log(__METHOD__.' ('.__LINE__.') '.array2string($buff)); + $namepart = array_pop($buff); + //$disposition = array_pop($buff); + //error_log(__METHOD__.' ('.__LINE__.') '.$namepart); + $fp = strpos($namepart,'"'); + //error_log(__METHOD__.' ('.__LINE__.') '.' Start:'.$fp); + if ($fp !== false) + { + $np = strpos($namepart,'"', $fp+1); + //error_log(__METHOD__.' ('.__LINE__.') '.' End:'.$np); + if ($np !== false) + { + $filename = trim(substr($namepart,$fp+1,$np-$fp-1)); + $filename = $mailObject->EncodeHeader($filename); + if (!empty($filename)) + { + if (strpos($part->disposition,';')!==false) + { + //chance is, disposition is broken too + $dbuff = explode(';',$part->disposition); + $part->disposition = trim($dbuff[0]); + } + break 2; + } + } + } + } + } + } + } + if (strlen($filename)==0) $filename = 'noname_'.$attachmentnumber; + //error_log(__METHOD__.' ('.__LINE__.') '.' '.$filename); + //echo $part->headers['content-transfer-encoding'].'#
'; + if ($decode) $part->body = $this->decodeMimePart($part->body,($part->headers['content-transfer-encoding']?$part->headers['content-transfer-encoding']:'WeDontKnowTheEncoding')); + if ((trim(strtolower($part->disposition))=='attachment' || trim(strtolower($part->disposition)) == 'inline' || isset($part->headers['content-id'])) && $partFetched==false) + { + if (trim(strtolower($part->disposition)) == 'inline' || $part->headers['content-id']) + { + $part->headers['content-id'] = str_replace(array('<','>'),'',$part->headers['content-id']); + $dirname = $this->accountid.'_'.$this->profileID.'_'.$this->sessionData['mailbox'].$part->headers['content-id']; + if (self::$debug) error_log(__METHOD__.' ('.__LINE__.') '.' Dirname:'.$dirname); + $dirname = md5($dirname); + $dir = $GLOBALS['egw_info']['server']['temp_dir']."/fmail_import/$dirname"; + if (self::$debug) error_log(__METHOD__.' ('.__LINE__.') '.' Dir to save attachment to:'.$dir); + if ( !file_exists( "$dir") ) + { + @mkdir( $dir, 0700, true ); + } + $rp = emailadmin_imapbase::getRandomString(); + file_put_contents( "$dir/$rp$filename", $part->body); + + $path = "$dir/$rp$filename"; + $mailObject->AddEmbeddedImage($path, $part->headers['content-id'], $filename, ($part->headers['content-transfer-encoding']?$part->headers['content-transfer-encoding']:'base64'), $part->ctype_primary.'/'.$part->ctype_secondary); + } + else + { + //error_log(__METHOD__.' ('.__LINE__.') '.' Add String '.($part->disposition=='attachment'?'Attachment':'Part').' of type:'.$part->ctype_primary.'/'.$part->ctype_secondary); + $mailObject->AddStringAttachment($part->body, //($part->headers['content-transfer-encoding']?base64_decode($part->body):$part->body), + $filename, + ($part->headers['content-transfer-encoding']?$part->headers['content-transfer-encoding']:'base64'), + $part->ctype_primary.'/'.$part->ctype_secondary + ); + } + } + if (!(trim(strtolower($part->disposition))=='attachment' || trim(strtolower($part->disposition)) == 'inline' || isset($part->headers['content-id'])) && $partFetched==false) + { + //error_log(__METHOD__.' ('.__LINE__.') '.' Add String '.($part->disposition=='attachment'?'Attachment':'Part').' of type:'.$part->ctype_primary.'/'.$part->ctype_secondary.' Body:'.$part->body); + $mailObject->AddStringPart($part->body, //($part->headers['content-transfer-encoding']?base64_decode($part->body):$part->body), + $filename, + ($part->headers['content-transfer-encoding']?$part->headers['content-transfer-encoding']:'base64'), + $part->ctype_primary.'/'.$part->ctype_secondary + ); + } + } + } + if ($alternatebodyneeded == false) $mailObject->AltBody = ''; + } + } + + function sendMDN($uid,$_folder) + { + try + { + $acc = emailadmin_account::read($this->profileID); + //error_log(__METHOD__.__LINE__.array2string($acc)); + $identity = emailadmin_account::read_identity($acc['ident_id'],true); + + //$identity = emailadmin_account::read_identity($this->sessionData['mailaccount'],true); + } + catch (Exception $e) + { + $identity=array(); + } + $headers = $this->getMessageHeader($uid,'',true,true,$_folder); + $send = CreateObject('phpgwapi.send'); + $send->ClearAddresses(); + $send->ClearAttachments(); + $send->IsHTML(False); + $send->IsSMTP(); + + $array_to = explode(",",$headers['TO']); + $send->From = $identity['ident_email']; + $send->FromName = self::generateIdentityString($identity,false); + + if (isset($headers['DISPOSITION-NOTIFICATION-TO'])) { + $toAddr = $headers['DISPOSITION-NOTIFICATION-TO']; + } else if ( isset($headers['RETURN-RECEIPT-TO']) ) { + $toAddr = $headers['RETURN-RECEIPT-TO']; + } else if ( isset($headers['X-CONFIRM-READING-TO']) ) { + $toAddr = $headers['X-CONFIRM-READING-TO']; + } else return false; + $singleAddress = imap_rfc822_parse_adrlist($toAddr,''); + if (self::$debug) error_log(__METHOD__.__LINE__.' To Address:'.$singleAddress[0]->mailbox."@".$singleAddress[0]->host.", ".$singleAddress[0]->personal); + $send->AddAddress($singleAddress[0]->mailbox."@".$singleAddress[0]->host, $singleAddress[0]->personal); + $send->AddCustomHeader('References: '.$headers['MESSAGE-ID']); + $send->Subject = $send->encode_subject( lang('Read')." : ".$headers['SUBJECT'] ); + + $sep = "-----------mdn".$uniq_id = md5(uniqid(time())); + + $body = "--".$sep."\r\n". + "Content-Type: text/plain; charset=ISO-8859-1\r\n". + "Content-Transfer-Encoding: 7bit\r\n\r\n". + $send->EncodeString(lang("Your message to %1 was displayed." ,$send->From),"7bit"). + "\r\n"; + + $body .= "--".$sep."\r\n". + "Content-Type: message/disposition-notification; name=\"MDNPart2.txt\"\r\n" . + "Content-Disposition: inline\r\n". + "Content-Transfer-Encoding: 7bit\r\n\r\n"; + $body.= $send->EncodeString("Reporting-UA: eGroupWare\r\n" . + "Final-Recipient: rfc822;".$send->From."\r\n" . + "Original-Message-ID: ".$headers['MESSAGE-ID']."\r\n". + "Disposition: manual-action/MDN-sent-manually; displayed",'7bit')."\r\n"; + + $body .= "--".$sep."\r\n". + "Content-Type: text/rfc822-headers; name=\"MDNPart3.txt\"\r\n" . + "Content-Transfer-Encoding: 7bit\r\n" . + "Content-Disposition: inline\r\n\r\n"; + $body .= $send->EncodeString($this->getMessageRawHeader($uid,'',$_folder),'7bit')."\r\n"; + $body .= "--".$sep."--"; + + + $header = rtrim($send->CreateHeader())."\r\n"."Content-Type: multipart/report; report-type=disposition-notification;\r\n". + "\tboundary=\"".$sep."\"\r\n\r\n"; + //error_log(__METHOD__.array2string($send)); + $rv = $send->SmtpSend($header,$body); + //error_log(__METHOD__.'#'.array2string($rv).'#'); + return $rv; + } + + /** + * Hook stuff + */ + + /** + * hook to add account + * + * this function is a wrapper function for emailadmin + * + * @param _hookValues contains the hook values as array + * @return nothing + */ + function addAccount($_hookValues) + { + error_log(__METHOD__.' ('.__LINE__.') '.' NOT DONE JET!'); + //$_profile_id=???? + //$icServer = emailadmin_account::read($_profile_id)->imapServer(); + //$ogServer = emailadmin_account::read($_profile_id)->smtpServer(); + +/* + if(($icServer instanceof defaultimap)) { + // if not connected, try opening an admin connection + if (!$icServer->_connected) $this->openConnection($this->profileID,true); + $icServer->addAccount($_hookValues); + if ($icServer->_connected) $this->closeConnection(); // close connection afterwards + } + if(($ogServer instanceof emailadmin_smtp)) { + $ogServer->addAccount($_hookValues); + } +*/ + } + + /** + * hook to delete account + * + * this function is a wrapper function for emailadmin + * + * @param _hookValues contains the hook values as array + * @return nothing + */ + function deleteAccount($_hookValues) + { + error_log(__METHOD__.' ('.__LINE__.') '.' NOT DONE JET!'); + //$_profile_id=???? + //$icServer = emailadmin_account::read($_profile_id)->imapServer(); + //$ogServer = emailadmin_account::read($_profile_id)->smtpServer(); +/* + if(($icServer instanceof defaultimap)) { + //try to connect with admin rights, when not connected + if (!$icServer->_connected) $this->openConnection($this->profileID,true); + $icServer->deleteAccount($_hookValues); + if ($icServer->_connected) $this->closeConnection(); // close connection + } + + if(($ogServer instanceof emailadmin_smtp)) { + $ogServer->deleteAccount($_hookValues); + } +*/ + } + + /** + * hook to update account + * + * this function is a wrapper function for emailadmin + * + * @param _hookValues contains the hook values as array + * @return nothing + */ + function updateAccount($_hookValues) + { + error_log(__METHOD__.' ('.__LINE__.') '.' NOT DONE JET!'); + //$_profile_id=???? + //$icServer = emailadmin_account::read($_profile_id)->imapServer(); + //$ogServer = emailadmin_account::read($_profile_id)->smtpServer(); +/* + if(($icServer instanceof defaultimap)) { + $icServer->updateAccount($_hookValues); + } + + if(($ogServer instanceof emailadmin_smtp)) { + $ogServer->updateAccount($_hookValues); + } +*/ + } +} diff --git a/emailadmin/inc/class.emailadmin_notifications.inc.php b/emailadmin/inc/class.emailadmin_notifications.inc.php new file mode 100644 index 0000000000..7921ffa32c --- /dev/null +++ b/emailadmin/inc/class.emailadmin_notifications.inc.php @@ -0,0 +1,174 @@ + + * @author Stylite AG + * @copyright (c) 2014 by Ralf Becker + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @version $Id$ + */ + +/** + * Mail account folders to notify user about arriving mail + */ +class emailadmin_notifications +{ + const APP = 'emailadmin'; + const TABLE = 'egw_ea_notifications'; + + /** + * Reference to global db object + * + * @var egw_db + */ + static protected $db; + + /** + * Cache for credentials to minimize database access + * + * @var array + */ + protected static $cache = array(); + + /** + * Read credentials for a given mail account + * + * @param int $acc_id + * @param int|array $account_id=null default use current user or all (in that order) + * @param boolean $return_empty_marker=false should we return null + * @return array with values for "notify_folders", "notify_use_default" + */ + public static function read($acc_id, $account_id=null, $return_empty_marker=false) + { + if (is_null($account_id)) + { + $account_id = array(0, $GLOBALS['egw_info']['user']['account_id']); + } + + // check cache, if nothing found, query database + // check assumes always same accounts (eg. 0=all plus own account_id) are asked + if (!isset(self::$cache[$acc_id]) || + !($rows = array_intersect_key(self::$cache[$acc_id], array_flip((array)$account_id)))) + { + $rows = self::$db->select(self::TABLE, '*', array( + 'acc_id' => $acc_id, + 'account_id' => $account_id, + ), __LINE__, __FILE__, false, '', self::APP); + //error_log(__METHOD__."($acc_id, ".array2string($account_id).") nothing in cache"); + } + $account_specific = 0; + foreach($rows as $row) + { + if ($row['account_id']) + { + $account_specific = $row['account_id']; + } + // update cache (only if we have database-iterator) + if (!is_array($rows)) + { + self::$cache[$acc_id][$row['account_id']][] = $row['notif_folder']; + } + } + $folders = (array)self::$cache[$acc_id][$account_specific]; + if (!$return_empty_marker && $folders == array(null)) $folders = array(); + $result = array( + 'notify_folders' => $folders, + 'notify_account_id' => $account_specific, + ); + //error_log(__METHOD__."($acc_id, ".array2string($account_id).") returning ".array2string($result)); + return $result; + } + + /** + * Write notification folders + * + * @param int $acc_id id of account + * @param int $account_id if of user-account for whom folders are or 0 for default + * @param array $folders folders to store + * @return int number of changed rows + */ + public static function write($acc_id, $account_id, array $folders) + { + if (!is_numeric($account_id) || !($account_id >= 0)) + { + throw new egw_exception_wrong_parameter(__METHOD__."($acc_id, ".array2string($account_id).", ...) account_id NOT >= 0!"); + } + + if ($account_id && !$folders && ($default = self::read($acc_id, 0)) && $default['notify_folders']) + { + $folders[] = null; // we need to write a marker, that user wants no notifications! + } + $old = self::read($acc_id, $account_id, true); // true = return empty marker + if ($account_id && !$old['notify_account_id']) $old['notify_folders'] = array(); // ignore returned default + + $changed = 0; + // insert newly added ones + foreach(array_diff($folders, $old['notify_folders']) as $folder) + { + self::$db->insert(self::TABLE, array( + 'acc_id' => $acc_id, + 'account_id' => $account_id, + 'notif_folder' => $folder, + ), false, __LINE__, __FILE__, self::APP); + + $changed += self::$db->affected_rows(); + } + // delete removed ones + if (($to_delete = array_diff($old['notify_folders'], $folders))) + { + self::$db->delete(self::TABLE, array( + 'acc_id' => $acc_id, + 'account_id' => $account_id, + 'notif_folder' => $to_delete, + ), __LINE__, __FILE__, self::APP); + + $changed += self::$db->affected_rows(); + } + // update cache + self::$cache[$acc_id][(int)$account_id] = $folders; + + //error_log(__METHOD__."(acc_id=$acc_id, account_id=".array2string($account_id).", folders=".array2string($folders).") returning $changed"); + return $changed; + } + + /** + * Delete credentials from database + * + * @param int $acc_id + * @param int|array $account_id=null + * @return int number of rows deleted + */ + public static function delete($acc_id, $account_id=null) + { + if (!($acc_id > 0) && !isset($account_id)) + { + throw new egw_exception_wrong_parameter(__METHOD__."() no acc_id AND no account_id parameter!"); + } + $where = array(); + if ($acc_id > 0) $where['acc_id'] = $acc_id; + if (isset($account_id)) $where['account_id'] = $account_id; + + self::$db->delete(self::TABLE, $where, __LINE__, __FILE__, self::APP); + + // invalidate cache: we allways unset everything about an account to simplify cache handling + foreach($acc_id > 0 ? (array)$acc_id : array_keys(self::$cache) as $acc_id) + { + unset(self::$cache[$acc_id]); + } + $ret = self::$db->affected_rows(); + //error_log(__METHOD__."($acc_id, ".array2string($account_id).", $type) affected $ret rows"); + return $ret; + } + + /** + * Init our static properties + */ + static public function init_static() + { + self::$db = isset($GLOBALS['egw_setup']) ? $GLOBALS['egw_setup']->db : $GLOBALS['egw']->db; + } +} +emailadmin_notifications::init_static(); diff --git a/emailadmin/inc/class.emailadmin_script.inc.php b/emailadmin/inc/class.emailadmin_script.inc.php new file mode 100644 index 0000000000..5bdc434723 --- /dev/null +++ b/emailadmin/inc/class.emailadmin_script.inc.php @@ -0,0 +1,603 @@ + + * @author Hadi Nategh + * @copyright 2002 by Stephen Grier + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @version $Id$ + */ + +/** + * Support for Sieve scripts + */ +class emailadmin_script { + + var $name; /* filename of script. */ + var $script; /* full ascii text of script from server. */ + var $size; /* size of script in bytes. */ + var $so; /* boolean: is it safe to overwrite script? + * only safe if we recognise encoding. */ + var $mode; /* basic or advanced. Smartsieve can only read/write basic. */ + var $rules; /* array of sieve rules. */ + var $vacation; /* vacation settings. */ + var $emailNotification; /* email notification settings. */ + var $pcount; /* highest priority value in ruleset. */ + var $errstr; /* error text. */ + /** + * Body transform content types + * + * @static array + */ + static $btransform_ctype_array = array( + '0' => 'Non', + '1' => 'image', + '2' => 'multipart', + '3' => 'text', + '4' => 'media', + '5' => 'message', + '6' => 'application', + '7' => 'audio', + ); + /** + * Switch on some error_log debug messages + * + * @var boolean + */ + var $debug=false; + + // class constructor + function __construct ($scriptname) { + $this->name = $scriptname; + $this->script = ''; + $this->size = 0; + $this->so = true; + $this->mode = ''; + $this->rules = array(); + $this->vacation = array(); + $this->emailNotification = array(); // Added email notifications + $this->pcount = 0; + $this->errstr = ''; + } + + // get sieve script rules for this user + /** + * Retrieve the rules + * + * @param bosieve $connection + * @return boolean true, if script written successfull + */ + function retrieveRules ($connection) { + #global $_SESSION; + $continuebit = 1; + $sizebit = 2; + $anyofbit = 4; + $keepbit = 8; + $regexbit = 128; + + if (!isset($this->name)){ + $this->errstr = 'retrieveRules: no script name specified'; + if ($this->debug) error_log(__CLASS__.'::'.__METHOD__.": no script name specified"); + return false; + } + + if (!is_object($connection)) { + $this->errstr = "retrieveRules: no sieve session open"; + if ($this->debug) error_log(__CLASS__.'::'.__METHOD__.": no sieve session open"); + return false; + } + + // if script doesn't yet exist, nothing to retrieve. + // safe to write to this script file. + #LK if (!AppSession::scriptExists($this->name)) { + #LK $this->so = true; + #LK return true; + #LK } + + #print "



get Script ". $this->name ."
"; + + if(PEAR::isError($script = $connection->getScript($this->name))) { + if ($this->debug) error_log(__CLASS__.'::'.__METHOD__.": error retrieving script: ".$script->getMessage()); + return $script; + } + + #print "
AAA: Script is ". htmlentities($script) ."
"; + $lines = array(); + $lines = preg_split("/\n/",$script); //,PREG_SPLIT_NO_EMPTY); + + $rules = array(); + $vacation = array(); + $emailNotification = array(); // Added email notifications + + /* first line should be the script size. eg: {123}. */ + #$line = array_shift($lines); + #if (!preg_match("/^\{(\d+)\}$/", $line, $bits)){ + # print 'retrieveRules: unexpected value: ' . $line .'
'; + # $this->errstr = 'retrieveRules: unexpected value: ' . $line; + # return false; + #} + #LK $this->size = $bits[1]; + + /* next line should be the recognised encoded head. if not, the script + * is of an unrecognised format, and we should not overwrite it. */ + $line = array_shift($lines); + if (!preg_match("/^# ?Mail(.*)rules for/", $line)){ + $this->errstr = 'retrieveRules: encoding not recognised'; + $this->so = false; + if ($this->debug) error_log(__CLASS__.'::'.__METHOD__.": encoding not recognised"); + return false; + } + $this->so = true; + + $line = array_shift($lines); + + while (isset($line)){ + if (preg_match("/^ *#(#PSEUDO|rule|vacation|mode|notify)/i",$line,$matches)){ + $line = rtrim($line); + switch ($matches[1]){ + case "rule": + $bits = explode("&&", $line); + $rule = array(); + $rule['priority'] = $bits[1]; + $rule['status'] = $bits[2]; + $rule['from'] = stripslashes($bits[3]); + $rule['to'] = stripslashes($bits[4]); + $rule['subject'] = stripslashes($bits[5]); + $rule['action'] = $bits[6]; + $rule['action_arg'] = $bits[7]; + // s will be encoded as \\n. undo this. + $rule['action_arg'] = preg_replace("/\\\\n/","\r\n",$rule['action_arg']); + $rule['action_arg'] = stripslashes($rule['action_arg']); + $rule['flg'] = $bits[8]; // bitwise flag + $rule['field'] = stripslashes($bits[9]); + $rule['field_val'] = stripslashes($bits[10]); + $rule['size'] = $bits[11]; + $rule['continue'] = ($bits[8] & $continuebit); + $rule['gthan'] = ($bits[8] & $sizebit); // use 'greater than' + $rule['anyof'] = ($bits[8] & $anyofbit); + $rule['keep'] = ($bits[8] & $keepbit); + $rule['regexp'] = ($bits[8] & $regexbit); + $rule['bodytransform'] = ($bits[12]); + $rule['field_bodytransform'] = ($bits[13]); + $rule['ctype'] = ($bits[14]); + $rule['field_ctype_val'] = ($bits[15]); + $rule['unconditional'] = 0; + if (!$rule['from'] && !$rule['to'] && !$rule['subject'] && + !$rule['field'] && !$rule['size'] && $rule['action']) { + $rule['unconditional'] = 1; + } + + array_push($rules,$rule); + + if ($rule['priority'] > $this->pcount) { + $this->pcount = $rule['priority']; + } + break; + case "vacation" : + if (preg_match("/^ *#vacation&&(.*)&&(.*)&&(.*)&&(.*)&&(.*)/i",$line,$bits) || + preg_match("/^ *#vacation&&(.*)&&(.*)&&(.*)&&(.*)/i",$line,$bits)) { + $vacation['days'] = $bits[1]; + $vaddresslist = $bits[2]; + $vaddresslist = preg_replace("/\"|\s/","",$vaddresslist); + $vaddresses = array(); + $vaddresses = preg_split("/,/",$vaddresslist); + $vacation['text'] = $bits[3]; + + // s will be encoded as \\n. undo this. + $vacation['text'] = preg_replace("/\\\\n/","\r\n",$vacation['text']); + + if (strpos($bits[4],'-')!== false) + { + $vacation['status'] = 'by_date'; + list($vacation['start_date'],$vacation['end_date']) = explode('-',$bits[4]); + } + else + { + $vacation['status'] = $bits[4]; + } + $vacation['addresses'] = &$vaddresses; + + $vacation['forwards'] = $bits[5]; + } + break; + case "notify": + if (preg_match("/^ *#notify&&(.*)&&(.*)&&(.*)/i",$line,$bits)) { + $emailNotification['status'] = $bits[1]; + $emailNotification['externalEmail'] = $bits[2]; + $emailNotification['displaySubject'] = $bits[3]; + } + break; + case "mode" : + if (preg_match("/^ *#mode&&(.*)/i",$line,$bits)){ + if ($bits[1] == 'basic') + $this->mode = 'basic'; + elseif ($bits[1] == 'advanced') + $this->mode = 'advanced'; + else + $this->mode = 'unknown'; + } + } + } + $line = array_shift($lines); + } + + $this->script = $script; + $this->rules = $rules; + $this->vacation = $vacation; + $this->emailNotification = $emailNotification; // Added email notifications + if ($this->debug) error_log(__CLASS__.'::'.__METHOD__.": Script succesful retrieved: ".print_r($vacation,true)); + + return true; + } + + + // update and save sieve script + function updateScript ($connection) + { + #global $_SESSION,$default,$sieve; + global $default,$sieve; + + $activerules = 0; + $regexused = 0; + $rejectused = 0; + $vacation_active = false; + + $username = $GLOBALS['egw_info']['user']['account_lid']; + $version = $GLOBALS['egw_info']['apps']['felamimail']['version']; + + //include "$default->lib_dir/version.php"; + + if (!is_object($connection)) + { + $this->errstr = "updateScript: no sieve session open"; + return false; + } + + // don't overwrite a file if not created by SmartSieve, + // unless configured to do so. +#LK if (!$this->so && !$default->allow_write_unrecognised_scripts) { +#LK $this->errstr = 'updateScript: encoding not recognised: not safe to overwrite ' . $this->name; +#LK return false; +#LK } + + // lets generate the main body of the script from our rules + + $enotify = $variables= $supportsbody = false; + if (in_array('enotify',$connection->_capability['extensions'])|| in_array('ENOTIFY', $connection->_capability['extensions'])) $enotify = true; + if (in_array('variables',$connection->_capability['extensions'])|| in_array('VARIABLES', $connection->_capability['extensions'])) $variables = true; + if (in_array('body', $connection->_capability['extensions']) || in_array('BODY', $connection->_capability['extensions'])) $supportsbody = true; + $newscriptbody = ""; + $continue = 1; + + foreach ($this->rules as $rule) { + $newruletext = ""; + + // don't print this rule if disabled. + if ($rule['status'] != 'ENABLED') { + } else { + $activerules = 1; + + // conditions + + $anyall = "allof"; + if ($rule['anyof']) $anyall = "anyof"; + if ($rule['regexp']) { + $regexused = 1; + } + $started = 0; + + if (!$rule['unconditional']) { + if (!$continue) $newruletext .= "els"; + $newruletext .= "if " . $anyall . " ("; + if ($rule['from']) { + if (preg_match("/^\s*!/", $rule['from'])){ + $newruletext .= 'not '; + $rule['from'] = preg_replace("/^\s*!/","",$rule['from']); + } + $match = ':contains'; + if (preg_match("/\*|\?/", $rule['from'])) $match = ':matches'; + if ($rule['regexp']) $match = ':regex'; + $newruletext .= "address " . $match . " [\"From\"]"; + $newruletext .= " \"" . addslashes($rule['from']) . "\""; + $started = 1; + } + if ($rule['to']) { + if ($started) $newruletext .= ", "; + if (preg_match("/^\s*!/", $rule['to'])){ + $newruletext .= 'not '; + $rule['to'] = preg_replace("/^\s*!/","",$rule['to']); + } + $match = ':contains'; + if (preg_match("/\*|\?/", $rule['to'])) $match = ':matches'; + if ($rule['regexp']) $match = ':regex'; + $newruletext .= "address " . $match . " [\"To\",\"TO\",\"Cc\",\"CC\"]"; + $newruletext .= " \"" . addslashes($rule['to']) . "\""; + $started = 1; + } + if ($rule['subject']) { + if ($started) $newruletext .= ", "; + if (preg_match("/^\s*!/", $rule['subject'])){ + $newruletext .= 'not '; + $rule['subject'] = preg_replace("/^\s*!/","",$rule['subject']); + } + $match = ':contains'; + if (preg_match("/\*|\?/", $rule['subject'])) $match = ':matches'; + if ($rule['regexp']) $match = ':regex'; + $newruletext .= "header " . $match . " \"subject\""; + $newruletext .= " \"" . addslashes($rule['subject']) . "\""; + $started = 1; + } + if ($rule['field'] && $rule['field_val']) { + if ($started) $newruletext .= ", "; + if (preg_match("/^\s*!/", $rule['field_val'])){ + $newruletext .= 'not '; + $rule['field_val'] = preg_replace("/^\s*!/","",$rule['field_val']); + } + $match = ':contains'; + if (preg_match("/\*|\?/", $rule['field_val'])) $match = ':matches'; + if ($rule['regexp']) $match = ':regex'; + $newruletext .= "header " . $match . " \"" . addslashes($rule['field']) . "\""; + $newruletext .= " \"" . addslashes($rule['field_val']) . "\""; + $started = 1; + } + if ($rule['size']) { + $xthan = " :under "; + if ($rule['gthan']) $xthan = " :over "; + if ($started) $newruletext .= ", "; + $newruletext .= "size " . $xthan . $rule['size'] . "K"; + $started = 1; + } + if ($supportsbody){ + if (!empty($rule['field_bodytransform'])){ + if ($started) $newruletext .= ", "; + $btransform = " :raw "; + $match = ' :contains'; + if ($rule['bodytransform']) $btransform = " :text "; + if (preg_match("/\*|\?/", $rule['field_bodytransform'])) $match = ':matches'; + if ($rule['regexp']) $match = ':regex'; + $newruletext .= "body " . $btransform . $match . " \"" . $rule['field_bodytransform'] . "\""; + $started = 1; + + } + if ($rule['ctype']!= '0' && !empty($rule['ctype'])){ + if ($started) $newruletext .= ", "; + $btransform_ctype = emailadmin_script::$btransform_ctype_array[$rule['ctype']]; + $ctype_subtype = ""; + if ($rule['field_ctype_val']) $ctype_subtype = "/"; + $newruletext .= "body :content " . " \"" . $btransform_ctype . $ctype_subtype . $rule['field_ctype_val'] . "\"" . " :contains \"\""; + $started = 1; + //error_log(__CLASS__."::".__METHOD__.array2string(emailadmin_script::$btransform_ctype_array)); + } + } + } + + // actions + + if (!$rule['unconditional']) $newruletext .= ") {\n\t"; + + if (preg_match("/folder/i",$rule['action'])) { + $newruletext .= "fileinto \"" . $rule['action_arg'] . "\";"; + } + if (preg_match("/reject/i",$rule['action'])) { + $newruletext .= "reject text: \n" . $rule['action_arg'] . "\n.\n;"; + $rejectused = 1; + } + if (preg_match("/address/i",$rule['action'])) { + foreach(preg_split('/, ?/',$rule['action_arg']) as $addr) + { + $newruletext .= "\tredirect \"".trim($addr)."\";\n"; + } + } + if (preg_match("/discard/i",$rule['action'])) { + $newruletext .= "discard;"; + } + if ($rule['keep']) $newruletext .= "\n\tkeep;"; + if (!$rule['unconditional']) $newruletext .= "\n}"; + + $continue = 0; + if ($rule['continue']) $continue = 1; + if ($rule['unconditional']) $continue = 1; + + $newscriptbody .= $newruletext . "\n\n"; + + } // end 'if ! ENABLED' + } + + // vacation rule + + if ($this->vacation) { + $vacation = $this->vacation; + if (!$vacation['days']) $vacation['days'] = ($default->vacation_days ? $default->vacation_days:''); + if (!$vacation['text']) $vacation['text'] = ($default->vacation_text ? $default->vacation_text:''); + if (!$vacation['status']) $vacation['status'] = 'on'; + + // filter out invalid addresses. + $ok_vaddrs = array(); + foreach($vacation['addresses'] as $addr){ + if ($addr != '' && preg_match("/\@/",$addr)) + array_push($ok_vaddrs,$addr); + } + $vacation['addresses'] = $ok_vaddrs; + + if (!$vacation['addresses'][0]){ + $defaultaddr = $sieve->user . '@' . $sieve->maildomain; + array_push($vacation['addresses'],$defaultaddr); + } + if ($vacation['status'] == 'on' || $vacation['status'] == 'by_date' && + $vacation['start_date'] <= time() && time() < $vacation['end_date']+24*3600) // +24*3600 to include the end_date day + { + if (trim($vacation['forwards'])) { + $if = array(); + foreach($vacation['addresses'] as $addr) { + $if[] = 'address :contains ["To","TO","Cc","CC"] "'.trim($addr).'"'; + } + $newscriptbody .= 'if anyof ('.implode(', ',$if).") {\n"; + foreach(preg_split('/, ?/',$vacation['forwards']) as $addr) { + $newscriptbody .= "\tredirect \"".trim($addr)."\";\n"; + } + $newscriptbody .= "\tkeep;\n}\n"; + } + $vacation_active = true; + $newscriptbody .= "if header :contains ".'"X-Spam-Status" '.'"YES"'."{\n\tstop;\n}\n"; //stop vacation reply if it is spam + $newscriptbody .= "vacation :days " . $vacation['days'] . " :addresses ["; + $first = 1; + foreach ($vacation['addresses'] as $vaddress) { + if (!$first) $newscriptbody .= ", "; + $newscriptbody .= "\"" . trim($vaddress) . "\""; + $first = 0; + } + $message = $vacation['text']; + if ($vacation['start_date'] || $vacation['end_date']) + { + $format_date = 'd M Y'; // see to it, that there is always a format, because if it is missing - no date will be output + if (!empty($GLOBALS['egw_info']['user']['preferences']['common']['dateformat'])) $format_date = $GLOBALS['egw_info']['user']['preferences']['common']['dateformat']; + $message = str_replace(array('$$start$$','$$end$$'),array( + date($format_date,$vacation['start_date']), + date($format_date,$vacation['end_date']), + ),$message); + } + $newscriptbody .= "] text:\n" . $message . "\n.\n;\n\n"; + } + + // update with any changes. + $this->vacation = $vacation; + } + + if ($this->emailNotification && $this->emailNotification['status'] == 'on') { + // format notification email header components + $notification_email = $this->emailNotification['externalEmail']; + + // format notification body + $egw_site_title = $GLOBALS['egw_info']['server']['site_title']; + if ($enotify==true) + { + $notification_body = lang("You have received a new message on the")." {$egw_site_title}"; + if ($variables) + { + $notification_body .= ", "; + $notification_body .= 'From: ${from}'; + if ($this->emailNotification['displaySubject']) { + $notification_body .= ', Subject: ${subject}'; + } + //$notification_body .= 'Size: $size$'."\n"; + $newscriptbody .= 'if header :matches "subject" "*" {'."\n\t".'set "subject" "${1}";'."\n".'}'."\n\n"; + $newscriptbody .= 'if header :matches "from" "*" {'."\n\t".'set "from" "${1}";'."\n".'}'."\n\n"; + } + else + { + $notification_body ="[SIEVE] ".$notification_body; + } + $newscriptbody .= 'notify :message "'.$notification_body.'"'."\n\t".'"mailto:'.$notification_email.'";'."\n"; + //$newscriptbody .= 'notify :message "'.$notification_body.'" :method "mailto" :options "'.$notification_email.'?subject='.$notification_subject.'";'."\n"; + } + else + { + $notification_body = lang("You have received a new message on the")." {$egw_site_title}"."\n"; + $notification_body .= "\n"; + $notification_body .= 'From: $from$'."\n"; + if ($this->emailNotification['displaySubject']) { + $notification_body .= 'Subject: $subject$'."\n"; + } + //$notification_body .= 'Size: $size$'."\n"; + + $newscriptbody .= 'notify :message "'.$notification_body.'" :method "mailto" :options "'.$notification_email.'";'."\n"; + //$newscriptbody .= 'notify :message "'.$notification_body.'" :method "mailto" :options "'.$notification_email.'?subject='.$notification_subject.'";'."\n"; + } + $newscriptbody .= 'keep;'."\n\n"; + } + + // generate the script head + + $newscripthead = ""; + $newscripthead .= "#Mail filter rules for " . $username . "\n"; + $newscripthead .= '#Generated by ' . $username . ' using FeLaMiMail ' . $version . ' ' . date($default->script_date_format); + $newscripthead .= "\n"; + + if ($activerules) { + $newscripthead .= "require [\"fileinto\""; + if ($regexused) $newscripthead .= ",\"regex\""; + if ($rejectused) $newscripthead .= ",\"reject\""; + if ($this->vacation && $vacation_active) { + $newscripthead .= ",\"vacation\""; + } + if ($supportsbody) $newscripthead .= ",\"body\""; + if ($this->emailNotification && $this->emailNotification['status'] == 'on') $newscripthead .= ',"'.($enotify?'e':'').'notify"'.($variables?',"variables"':''); // Added email notifications + $newscripthead .= "];\n\n"; + } else { + // no active rules, but might still have an active vacation rule + if ($this->vacation && $vacation_active) + $newscripthead .= "require [\"vacation\"];\n\n"; + if ($this->emailNotification && $this->emailNotification['status'] == 'on') $newscripthead .= "require [\"".($enotify?'e':'')."notify\"".($variables?',"variables"':'')."];\n\n"; // Added email notifications + } + + // generate the encoded script foot + + $newscriptfoot = ""; + $pcount = 1; + $newscriptfoot .= "##PSEUDO script start\n"; + foreach ($this->rules as $rule) { + // only add rule to foot if status != deleted. this is how we delete a rule. + if ($rule['status'] != 'DELETED') { + $rule['action_arg'] = addslashes($rule['action_arg']); + // we need to handle \r\n here. + $rule['action_arg'] = preg_replace("/\r?\n/","\\n",$rule['action_arg']); + /* reset priority value. note: we only do this + * for compatibility with Websieve. */ + $rule['priority'] = $pcount; + $newscriptfoot .= "#rule&&" . $rule['priority'] . "&&" . $rule['status'] . "&&" . + addslashes($rule['from']) . "&&" . addslashes($rule['to']) . "&&" . addslashes($rule['subject']) . "&&" . $rule['action'] . "&&" . + $rule['action_arg'] . "&&" . $rule['flg'] . "&&" . addslashes($rule['field']) . "&&" . addslashes($rule['field_val']) . "&&" . $rule['size']; + if ($supportsbody && (!empty($rule['field_bodytransform']) || ($rule['ctype']!= '0' && !empty($rule['ctype'])))) $newscriptfoot .= "&&" . $rule['bodytransform'] . "&&" . $rule['field_bodytransform']. "&&" . $rule['ctype'] . "&&" . $rule['field_ctype_val']; + $newscriptfoot .= "\n"; + $pcount = $pcount+2; + //error_log(__CLASS__."::".__METHOD__.__LINE__.array2string($newscriptfoot)); + } + } + + if ($this->vacation) + { + $vacation = $this->vacation; + $newscriptfoot .= "#vacation&&" . $vacation['days'] . "&&"; + $first = 1; + foreach ($vacation['addresses'] as $address) { + if (!$first) $newscriptfoot .= ", "; + $newscriptfoot .= "\"" . trim($address) . "\""; + $first = 0; + } + + $vacation['text'] = preg_replace("/\r?\n/","\\n",$vacation['text']); + $newscriptfoot .= "&&" . $vacation['text'] . "&&" . + ($vacation['status']=='by_date' ? $vacation['start_date'].'-'.$vacation['end_date'] : $vacation['status']); + if ($vacation['forwards']) $newscriptfoot .= '&&' . $vacation['forwards']; + $newscriptfoot .= "\n"; + } + if ($this->emailNotification) { + $emailNotification = $this->emailNotification; + $newscriptfoot .= "#notify&&" . $emailNotification['status'] . "&&" . $emailNotification['externalEmail'] . "&&" . $emailNotification['displaySubject'] . "\n"; + } + + $newscriptfoot .= "#mode&&basic\n"; + + $newscript = $newscripthead . $newscriptbody . $newscriptfoot; + $this->script = $newscript; + //error_log(__METHOD__.__LINE__.array2string($newscript)); + //print "
$newscript
"; exit; + $scriptfile = $this->name; + //print "
".htmlentities($newscript)."

"; + $ret = $connection->installScript($this->name, $newscript, true); + if (!$ret || PEAR::isError($ret)) { + $this->errstr = 'updateScript: putscript failed: ' . (PEAR::isError($ret)?$ret->message:$connection->errstr); + error_log(__METHOD__.__LINE__.' # Error: ->'.$this->errstr); + error_log(__METHOD__.__LINE__.' # ScriptName:'.$this->name.' Script:'.$newscript); + error_log(__METHOD__.__LINE__.' # Instance='.$GLOBALS['egw_info']['user']['domain'].', User='.$GLOBALS['egw_info']['user']['account_lid']); + return false; + } + + return true; + } +} diff --git a/emailadmin/inc/class.emailadmin_sieve.inc.php b/emailadmin/inc/class.emailadmin_sieve.inc.php new file mode 100644 index 0000000000..0cd3de0782 --- /dev/null +++ b/emailadmin/inc/class.emailadmin_sieve.inc.php @@ -0,0 +1,480 @@ + + * @author Klaus Leithoff + * @author Lars Kneschke + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @version $Id$ + */ + +include_once('Net/Sieve.php'); + +/** + * Support for Sieve scripts + * + * Class can be switched to use exceptions by calling + * + * PEAR::setErrorHandling(PEAR_ERROR_EXCEPTION); + * + * In which case constructor and setters will throw exceptions for connection, login or other errors. + * + * retriveRules and getters will not throw an exception, if there's no script currently. + * + * Most methods incl. constructor accept a script-name, but by default current active script is used + * and if theres no script emailadmin_sieve::DEFAULT_SCRIPT_NAME. + */ +class emailadmin_sieve extends Net_Sieve +{ + /** + * reference to emailadmin_imap object + * + * @var emailadmin_imap + */ + var $icServer; + + /** + * @var string name of active script queried from Sieve server + */ + var $scriptName; + + /** + * @var $rules containing the rules + */ + var $rules; + + /** + * @var $vacation containing the vacation + */ + var $vacation; + + /** + * @var $emailNotification containing the emailNotification + */ + var $emailNotification; + + /** + * @var object $error the last PEAR error object + */ + var $error; + + /** + * The timeout for the connection to the SIEVE server. + * @var int + */ + var $_timeout = 10; + + /** + * Switch on some error_log debug messages + * + * @var boolean + */ + var $debug = false; + + /** + * Default script name used if no active script found on server + */ + const DEFAULT_SCRIPT_NAME = 'mail'; + + /** + * Constructor + * + * @param emailadmin_imap $_icServer + * @param string $_euser effictive user, if given the Cyrus admin account is used to login on behalf of $euser + * @param string $_scriptName + */ + function __construct(emailadmin_imap $_icServer=null, $_euser='', $_scriptName=null) + { + parent::Net_Sieve(); + + if ($_scriptName) $this->scriptName = $_scriptName; + + // TODO: since we seem to have major problems authenticating via DIGEST-MD5 and CRAM-MD5 in SIEVE, we skip MD5-METHODS for now + if (!is_null($_icServer)) + { + $_icServer->supportedAuthMethods = array('PLAIN' , 'LOGIN'); + } + else + { + $this->supportedAuthMethods = array('PLAIN' , 'LOGIN'); + } + + $this->displayCharset = translation::charset(); + + if (!is_null($_icServer) && $this->_connect($_icServer, $_euser) === 'die') { + die('Sieve not activated'); + } + } + + /** + * Open connection to the sieve server + * + * @param emailadmin_imap $_icServer + * @param string $euser effictive user, if given the Cyrus admin account is used to login on behalf of $euser + * @return mixed 'die' = sieve not enabled, false=connect or login failure, true=success + */ + function _connect(emailadmin_imap $_icServer, $euser='') + { + static $isConError = null; + static $sieveAuthMethods = null; + $_icServerID = $_icServer->acc_id; + if (is_null($isConError)) + { + $isConError = egw_cache::getCache(egw_cache::INSTANCE, 'email', 'icServerSIEVE_connectionError' . trim($GLOBALS['egw_info']['user']['account_id'])); + } + if ( isset($isConError[$_icServerID]) ) + { + $this->error = new PEAR_Error($isConError[$_icServerID]); + return false; + } + + if ($this->debug) + { + error_log(__METHOD__ . array2string($euser)); + } + if($_icServer->acc_sieve_enabled) + { + if ($_icServer->acc_sieve_host) + { + $sieveHost = $_icServer->acc_sieve_host; + } + else + { + $sieveHost = $_icServer->acc_imap_host; + } + //error_log(__METHOD__.__LINE__.'->'.$sieveHost); + $sievePort = $_icServer->acc_sieve_port; + + $useTLS = false; + + switch($_icServer->acc_sieve_ssl) + { + case emailadmin_account::SSL_SSL: + $sieveHost = 'ssl://'.$sieveHost; + break; + case emailadmin_account::SSL_TLS: + $sieveHost = 'tls://'.$sieveHost; + break; + case emailadmin_account::SSL_STARTTLS: + $useTLS = true; + } + if ($euser) + { + $username = $_icServer->acc_imap_admin_username; + $password = $_icServer->acc_imap_admin_password; + } + else + { + $username = $_icServer->acc_imap_username; + $password = $_icServer->acc_imap_password; + } + $this->icServer = $_icServer; + } + else + { + egw_cache::setCache(egw_cache::INSTANCE,'email','icServerSIEVE_connectionError'.trim($GLOBALS['egw_info']['user']['account_id']),$isConError,$expiration=60*15); + return 'die'; + } + $this->_timeout = 10; // socket::connect sets the/this timeout on connection + $timeout = emailadmin_imap::getTimeOut('SIEVE'); + if ($timeout > $this->_timeout) + { + $this->_timeout = $timeout; + } + + if(PEAR::isError($this->error = $this->connect($sieveHost , $sievePort, $options=null, $useTLS) ) ) + { + if ($this->debug) + { + error_log(__METHOD__ . ": error in connect($sieveHost,$sievePort, " . array2string($options) . ", $useTLS): " . $this->error->getMessage()); + } + $isConError[$_icServerID] = $this->error->getMessage(); + egw_cache::setCache(egw_cache::INSTANCE,'email','icServerSIEVE_connectionError'.trim($GLOBALS['egw_info']['user']['account_id']),$isConError,$expiration=60*15); + return false; + } + // we cache the supported AuthMethods during session, to be able to speed up login. + if (is_null($sieveAuthMethods)) + { + $sieveAuthMethods = & egw_cache::getSession('email', 'sieve_supportedAuthMethods'); + } + if (isset($sieveAuthMethods[$_icServerID])) + { + $this->supportedAuthMethods = $sieveAuthMethods[$_icServerID]; + } + + if(PEAR::isError($this->error = $this->login($username, $password, null, $euser) ) ) + { + if ($this->debug) + { + error_log(__METHOD__ . ": error in login($username,$password,null,$euser): " . $this->error->getMessage()); + } + $isConError[$_icServerID] = $this->error->getMessage(); + egw_cache::setCache(egw_cache::INSTANCE,'email','icServerSIEVE_connectionError'.trim($GLOBALS['egw_info']['user']['account_id']),$isConError,$expiration=60*15); + return false; + } + + // query active script from Sieve server + if (empty($this->scriptName)) + { + try { + $this->scriptName = $this->getActive(); + } + catch(Exception $e) { + unset($e); // ignore NOTEXISTS exception + } + if (empty($this->scriptName)) + { + $this->scriptName = self::DEFAULT_SCRIPT_NAME; + } + } + + //error_log(__METHOD__.__LINE__.array2string($this->_capability)); + return true; + } + + /** + * Handles connecting to the server and checks the response validity. + * overwritten function from Net_Sieve to respect timeout + * + * @param string $host Hostname of server. + * @param string $port Port of server. + * @param array $options List of options to pass to + * stream_context_create(). + * @param boolean $useTLS Use TLS if available. + * + * @return boolean True on success, PEAR_Error otherwise. + */ + function connect($host, $port, $options = null, $useTLS = true) + { + if ($this->debug) + { + error_log(__METHOD__ . __LINE__ . "$host, $port, " . array2string($options) . ", $useTLS"); + } + $this->_data['host'] = $host; + $this->_data['port'] = $port; + $this->_useTLS = $useTLS; + if (is_array($options)) { + $this->_options = array_merge((array)$this->_options, $options); + } + + if (NET_SIEVE_STATE_DISCONNECTED != $this->_state) { + return PEAR::raiseError('Not currently in DISCONNECTED state', 1); + } + + if (PEAR::isError($res = $this->_sock->connect($host, $port, false, ($this->_timeout?$this->_timeout:10), $options))) { + return $res; + } + + if ($this->_bypassAuth) { + $this->_state = NET_SIEVE_STATE_TRANSACTION; + } else { + $this->_state = NET_SIEVE_STATE_AUTHORISATION; + if (PEAR::isError($res = $this->_doCmd())) { + return $res; + } + } + + // Explicitly ask for the capabilities in case the connection is + // picked up from an existing connection. + if (PEAR::isError($res = $this->_cmdCapability())) { + return PEAR::raiseError( + 'Failed to connect, server said: ' . $res->getMessage(), 2 + ); + } + + // Check if we can enable TLS via STARTTLS. + if ($useTLS && !empty($this->_capability['starttls']) + && function_exists('stream_socket_enable_crypto') + ) { + if (PEAR::isError($res = $this->_startTLS())) { + return $res; + } + } + + return true; + } + + /** + * Handles the authentication using any known method + * overwritten function from Net_Sieve to support fallback + * + * @param string $uid The userid to authenticate as. + * @param string $pwd The password to authenticate with. + * @param string $userMethod The method to use ( if $userMethod == '' then the class chooses the best method (the stronger is the best ) ) + * @param string $euser The effective uid to authenticate as. + * + * @return mixed string or PEAR_Error + * + * @access private + * @since 1.0 + */ + function _cmdAuthenticate($uid , $pwd , $userMethod = null , $euser = '' ) + { + if ( PEAR::isError( $method = $this->_getBestAuthMethod($userMethod) ) ) { + return $method; + } + //error_log(__METHOD__.__LINE__.' using AuthMethod: '.$method); + switch ($method) { + case 'DIGEST-MD5': + $result = $this->_authDigest_MD5( $uid , $pwd , $euser ); + if (!PEAR::isError($result)) + { + break; + } + $res = $this->_doCmd(); + unset($this->_error); + $this->supportedAuthMethods = array_diff($this->supportedAuthMethods,array($method,'CRAM-MD5')); + return $this->_cmdAuthenticate($uid , $pwd, null, $euser); + case 'CRAM-MD5': + $result = $this->_authCRAM_MD5( $uid , $pwd, $euser); + if (!PEAR::isError($result)) + { + break; + } + $res = $this->_doCmd(); + unset($this->_error); + $this->supportedAuthMethods = array_diff($this->supportedAuthMethods,array($method,'DIGEST-MD5')); + return $this->_cmdAuthenticate($uid , $pwd, null, $euser); + case 'LOGIN': + $result = $this->_authLOGIN( $uid , $pwd , $euser ); + if (!PEAR::isError($result)) + { + break; + } + $res = $this->_doCmd(); + unset($this->_error); + $this->supportedAuthMethods = array_diff($this->supportedAuthMethods,array($method)); + return $this->_cmdAuthenticate($uid , $pwd, null, $euser); + case 'PLAIN': + $result = $this->_authPLAIN( $uid , $pwd , $euser ); + break; + default : + $result = new PEAR_Error( "$method is not a supported authentication method" ); + break; + } + if (PEAR::isError($result)) + { + return $result; + } + if (PEAR::isError($res = $this->_doCmd())) { + return $res; + } + + // Query the server capabilities again now that we are authenticated. + if (PEAR::isError($res = $this->_cmdCapability())) { + return PEAR::raiseError( + 'Failed to connect, server said: ' . $res->getMessage(), 2 + ); + } + + return $result; + } + + function getRules() + { + if (!isset($this->rules)) $this->retrieveRules(); + + return $this->rules; + } + + function getVacation() + { + if (!isset($this->rules)) $this->retrieveRules(); + + return $this->vacation; + } + + function getEmailNotification() + { + if (!isset($this->rules)) $this->retrieveRules(); + + return $this->emailNotification; + } + + /** + * Set email notifications + * + * @param array $_rules + * @param string $_scriptName + */ + function setRules(array $_rules, $_scriptName=null) + { + $script = $this->retrieveRules($_scriptName); + $script->debug = $this->debug; + $script->rules = $_rules; + $ret = $script->updateScript($this); + $this->error = $script->errstr; + return $ret; + } + + /** + * Set email notifications + * + * @param array $_vacation + * @param string $_scriptName + */ + function setVacation(array $_vacation, $_scriptName=null) + { + if ($this->debug) + { + error_log(__METHOD__ . "($_scriptName," . print_r($_vacation, true) . ')'); + } + $script = $this->retrieveRules($_scriptName); + $script->debug = $this->debug; + $script->vacation = $_vacation; + $ret = $script->updateScript($this); + $this->error = $script->errstr; + return $ret; + } + + /** + * Set email notifications + * + * @param array $_emailNotification + * @param string $_scriptName + * @return emailadmin_script + */ + function setEmailNotification(array $_emailNotification, $_scriptName=null) + { + if ($_emailNotification['externalEmail'] == '' || !preg_match("/\@/",$_emailNotification['externalEmail'])) { + $_emailNotification['status'] = 'off'; + $_emailNotification['externalEmail'] = ''; + } + + $script = $this->retrieveRules($_scriptName); + $script->emailNotification = $_emailNotification; + $ret = $script->updateScript($this); + $this->error = $script->errstr; + return $ret; + } + + /** + * Retrive rules, vacation, notifications and return emailadmin_script object to update them + * + * @param string $_scriptName + * @return emailadmin_script + */ + function retrieveRules($_scriptName=null) + { + if (!$_scriptName) + { + $_scriptName = $this->scriptName; + } + $script = new emailadmin_script($_scriptName); + + try { + $script->retrieveRules($this); + } + catch (Exception $e) { + unset($e); // ignore not found script exception + } + $this->rules =& $script->rules; + $this->vacation =& $script->vacation; + $this->emailNotification =& $script->emailNotification; // Added email notifications + + return $script; + } +} diff --git a/emailadmin/inc/class.emailadmin_smtp.inc.php b/emailadmin/inc/class.emailadmin_smtp.inc.php new file mode 100644 index 0000000000..46171c53c6 --- /dev/null +++ b/emailadmin/inc/class.emailadmin_smtp.inc.php @@ -0,0 +1,250 @@ + + * @author Ralf Becker + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License Version 2+ + * @version $Id$ + */ + +/** + * EMailAdmin generic base class for SMTP + */ +class emailadmin_smtp +{ + /** + * Label shown in EMailAdmin + */ + const DESCRIPTION = 'standard SMTP-Server'; + + /** + * Capabilities of this class (pipe-separated): default, forward + */ + const CAPABILITIES = 'default'; + + /** + * Attribute value to enable mail for an account, OR false if existense of attribute is enough to enable account + * + * Logical values uses inside EGroupware, different classes might store different values internally + */ + const MAIL_ENABLED = 'active'; + + /** + * Attribute value to only forward mail + * + * Logical values uses inside EGroupware, different classes might store different values internally + */ + const FORWARD_ONLY = 'forwardOnly'; + + /** + * Reference to global account object + * + * @var accounts + */ + protected $accounts; + + /** + * SmtpServerId + * + * @var int + */ + var $SmtpServerId; + + var $smtpAuth = false; + + var $editForwardingAddress = false; + + var $host; + + var $port; + + var $username; + + var $password; + + var $defaultDomain; + + /** + * Constructor + * + * @param string $defaultDomain=null + */ + function __construct($defaultDomain=null) + { + $this->defaultDomain = $defaultDomain ? $defaultDomain : $GLOBALS['egw_info']['server']['mail_suffix']; + + $this->accounts = $GLOBALS['egw']->accounts; + } + + /** + * Return description for EMailAdmin + * + * @return string + */ + public static function description() + { + return static::DESCRIPTION; + } + + /** + * Hook called on account creation + * + * @param array $_hookValues values for keys 'account_email', 'account_firstname', 'account_lastname', 'account_lid' + * @return boolean true on success, false on error writing to ldap + */ + function addAccount($_hookValues) + { + $mailLocalAddress = $_hookValues['account_email'] ? $_hookValues['account_email'] : + common::email_address($_hookValues['account_firstname'], + $_hookValues['account_lastname'],$_hookValues['account_lid'],$this->defaultDomain); + + $account_id = !empty($_hookValues['account_id']) ? $_hookValues['account_id'] : + $this->accounts->name2id($_hookValues['account_lid'], 'account_lid', 'u'); + + if ($this->accounts->exists($account_id) != 1) + { + throw new egw_exception_assertion_failed("Account #$account_id ({$_hookValues['account_lid']}) does NOT exist!"); + } + return $this->setUserData($account_id, array(), array(), null, self::MAIL_ENABLED, $mailLocalAddress, null); + } + + /** + * Hook called on account deletion + * + * @param array $_hookValues values for keys 'account_lid', 'account_id' + * @return boolean true on success, false on error writing to ldap + */ + function deleteAccount($_hookValues) + { + return true; + } + + /** + * Get all email addresses of an account + * + * @param string $_accountName + * @return array + */ + function getAccountEmailAddress($_accountName) + { + $emailAddresses = array(); + + if (($account_id = $this->accounts->name2id($_accountName, 'account_lid', 'u'))) + { + $realName = trim($GLOBALS['egw_info']['user']['account_firstname'] . (!empty($GLOBALS['egw_info']['user']['account_firstname']) ? ' ' : '') . $GLOBALS['egw_info']['user']['account_lastname']); + $emailAddresses[] = array ( + 'name' => $realName, + 'address' => $this->accounts->id2name($account_id, 'account_email'), + 'type' => 'default', + ); + } + return $emailAddresses; + } + + /** + * Get the data of a given user + * + * @param int|string $user numerical account-id, account-name or email address + * @param boolean $match_uid_at_domain=true true: uid@domain matches, false only an email or alias address matches + * @return array with values for keys 'mailLocalAddress', 'mailAlternateAddress' (array), 'mailForwardingAddress' (array), + * 'accountStatus' ("active"), 'quotaLimit' and 'deliveryMode' ("forwardOnly") + */ + function getUserData($user, $match_uid_at_domain=false) + { + $userData = array(); + + return $userData; + } + + + /** + * Saves the forwarding information + * + * @param int $_accountID + * @param string|array $_forwardingAddress + * @param string $_keepLocalCopy 'yes' + * @return boolean true on success, false on error writing + */ + function saveSMTPForwarding($_accountID, $_forwardingAddress, $_keepLocalCopy) + { + return $this->setUserData($_accountID, array(), + $_forwardingAddress ? (array)$_forwardingAddress : array(), + $_keepLocalCopy != 'yes' ? self::FORWARD_ONLY : null, null, null, null, true); + } + + /** + * Set the data of a given user + * + * @param int $_uidnumber numerical user-id + * @param array $_mailAlternateAddress + * @param array $_mailForwardingAddress + * @param string $_deliveryMode + * @param string $_accountStatus + * @param string $_mailLocalAddress + * @param int $_quota in MB + * @param boolean $_forwarding_only=false true: store only forwarding info, used internally by saveSMTPForwarding + * @param string $_setMailbox=null used only for account migration + * @return boolean true on success, false on error writing to ldap + */ + function setUserData($_uidnumber, array $_mailAlternateAddress, array $_mailForwardingAddress, $_deliveryMode, + $_accountStatus, $_mailLocalAddress, $_quota, $_forwarding_only=false, $_setMailbox=null) + { + return true; + } + + /** + * Hook called on account update + * + * @param array $_hookValues values for keys 'account_email', 'account_firstname', 'account_lastname', 'account_lid', 'account_id' + * @return boolean true on success, false on error writing to ldap + */ + function updateAccount($_hookValues) + { + return true; + } + + /** + * Build mailbox address for given account and mail_addr_type + * + * If $account is an array (with values for keys account_(id|lid|email), it does NOT call accounts class + * + * @param int|array $account account_id or whole account array with values for keys + * @param string $domain=null domain, default use $this->defaultDomain + * @param string $mail_login_type=null standard(uid), vmailmgr(uid@domain), email or uidNumber, + * default use $GLOBALS['egw_info']['server']['mail_login_type'] + * @return string + */ + /*static*/ public function mailbox_addr($account,$domain=null,$mail_login_type=null) + { + if (is_null($domain)) $domain = $this->defaultDomain; + if (is_null($mail_login_type)) $mail_login_type = $GLOBALS['egw_info']['server']['mail_login_type']; + + switch($mail_login_type) + { + case 'email': + $mbox = is_array($account) ? $account['account_email'] : $GLOBALS['egw']->accounts->id2name($account,'account_email'); + break; + + case 'uidNumber': + if (is_array($account)) $account = $account['account_id']; + $mbox = 'u'.$account.'@'.$domain; + break; + + case 'standard': + $mbox = is_array($account) ? $account['account_lid'] : $GLOBALS['egw']->accounts->id2name($account); + break; + + case 'vmailmgr': + default: + $mbox = is_array($account) ? $account['account_lid'] : $GLOBALS['egw']->accounts->id2name($account); + $mbox .= '@'.$domain; + break; + } + //error_log(__METHOD__."(".array2string($account).",'$domain','$mail_login_type') = '$mbox'"); + + return $mbox; + } +} diff --git a/emailadmin/inc/class.emailadmin_smtp_ads.inc.php b/emailadmin/inc/class.emailadmin_smtp_ads.inc.php new file mode 100644 index 0000000000..757ee8478a --- /dev/null +++ b/emailadmin/inc/class.emailadmin_smtp_ads.inc.php @@ -0,0 +1,166 @@ + + * @copyright (c) 2013 by Ralf Becker + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @version $Id$ + */ + +/** + * Postfix using Active Directorys proxyAddresses attribute + * (available without installing Exchange schemas). + * + * This plugin is NOT meant to administrate an Exchange Server using AD! + * + * Aliases, forwards, forward only and quota is stored in + * multivalued attribute proxyAddresses with different prefixes. + * + * Primary mail address is additionally stored in proxyAddresses. + * Disabling mail removes proxyAddresses completly. + * + * @link http://msdn.microsoft.com/en-us/library/ms679424(v=vs.85).aspx + * @link http://www.dovecot.org/list/dovecot/2010-February/046763.html + */ +class emailadmin_smtp_ads extends emailadmin_smtp_ldap +{ + /** + * Label shown in EMailAdmin + */ + const DESCRIPTION = 'Active Directory'; + /** + * Capabilities of this class (pipe-separated): default, forward + */ + const CAPABILITIES = 'default|forward'; + + /** + * Name of schema, has to be in the right case! + */ + const SCHEMA = 'top'; + + /** + * Filter for users + * + * objectCategory is indexed, while objectclass is not! + */ + const USER_FILTER = '(objectCategory=person)'; + + /** + * Name of schema for groups, has to be in the right case! + */ + const GROUP_SCHEMA = 'group'; + + /** + * Attribute to enable mail for an account, OR false if existence of ALIAS_ATTR is enough for mail delivery + */ + const MAIL_ENABLE_ATTR = false; + + /** + * Attribute for aliases OR false to use mail + */ + const ALIAS_ATTR = 'proxyaddresses'; + + /** + * Caseinsensitive prefix for aliases (eg. "smtp:"), aliases get added with it and only aliases with it are reported + */ + const ALIAS_PREFIX = 'smtp:'; + + /** + * Primary mail address required as an alias too: true or false + */ + const REQUIRE_MAIL_AS_ALIAS = true; + + /** + * Attribute for forwards OR false if not possible + */ + const FORWARD_ATTR = 'proxyaddresses'; + + /** + * Caseinsensitive prefix for forwards (eg. "forward:"), forwards get added with it and only forwards with it are reported + */ + const FORWARD_PREFIX = 'forward:'; + + /** + * Attribute to only forward mail, OR false if not available + */ + const FORWARD_ONLY_ATTR = 'proxyaddresses'; + + /** + * Value of forward-only attribute, if not set any value will switch forward only on (checked with =*) + */ + const FORWARD_ONLY_VALUE = 'forwardOnly'; + + /** + * Attribute for mailbox, to which mail gets delivered OR false if not supported + */ + const MAILBOX_ATTR = false; + + /** + * Attribute for quota limit of user in MB + */ + const QUOTA_ATTR = 'proxyaddresses'; + + /** + * Caseinsensitive prefix for quota (eg. "quota:"), quota get added with it and only quota with it are reported + */ + const QUOTA_PREFIX = 'quota:'; + + /** + * Internal quota in MB is multiplicated with this factor before stored in LDAP + */ + const QUOTA_FACTOR = 1048576; + + /** + * Attribute for user name + */ + const USER_ATTR = 'samaccountname'; + + /** + * Attribute for numeric user id (optional) + * + * No single uidNumber attribute, as we use RID (last part of objectSid attribute) for it. + */ + const USERID_ATTR = false; + + /** + * Return LDAP connection + */ + protected function getLdapConnection() + { + static $ldap; + + if (is_null($ldap)) + { + if (!is_a($GLOBALS['egw']->accounts->backend, 'accounts_ads')) + { + throw new egw_exception_wrong_userinput('Postfix with Active Directory requires accounts stored in ADS!'); + } + $ldap = $GLOBALS['egw']->accounts->backend->ldap_connection(); + } + return $ldap; + } + + /** + * Constructor + * + * @param string $defaultDomain=null + */ + function __construct($defaultDomain=null) + { + parent::__construct($defaultDomain); + + $this->setBase($GLOBALS['egw']->accounts->backend->ads_context()); + } + /** + * Return description for EMailAdmin + * + * @return string + */ + public static function description() + { + return static::DESCRIPTION; + } +} diff --git a/emailadmin/inc/class.emailadmin_smtp_ldap.inc.php b/emailadmin/inc/class.emailadmin_smtp_ldap.inc.php new file mode 100644 index 0000000000..ee3b78dc02 --- /dev/null +++ b/emailadmin/inc/class.emailadmin_smtp_ldap.inc.php @@ -0,0 +1,783 @@ + + * @copyright (c) 2010-13 by Ralf Becker + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @version $Id$ + */ + +/** + * Generic base class for SMTP configuration via LDAP + * + * This class uses just inetOrgPerson schema to store primary mail address and aliases + * + * Aliases are stored as aditional mail Attributes. The primary mail address is the first one. + * This schema does NOT support forwarding or disabling of an account for mail. + * + * Aliases, forwards, forward-only and quota attribute can be stored in same multivalued attribute + * with different prefixes. + * + * Please do NOT copy this class! Extend it and set the constants different. + * + * Please note: schema names muse use correct case (eg. "inetOrgPerson"), + * while attribute name muse use lowercase, as LDAP returns them as keys in lowercase! + */ +class emailadmin_smtp_ldap extends emailadmin_smtp +{ + /** + * Name of schema, has to be in the right case! + */ + const SCHEMA = 'inetOrgPerson'; + + /** + * Filter for users + */ + const USER_FILTER = '(objectClass=posixAccount)'; + + /** + * Name of schema for groups, has to be in the right case! + */ + const GROUP_SCHEMA = 'posixGroup'; + + /** + * Attribute to enable mail for an account, OR false if existence of ALIAS_ATTR is enough for mail delivery + */ + const MAIL_ENABLE_ATTR = false; + + /** + * Attribute for aliases OR false to use mail + */ + const ALIAS_ATTR = false; + + /** + * Caseinsensitive prefix for aliases (eg. "smtp:"), aliases get added with it and only aliases with it are reported + */ + const ALIAS_PREFIX = ''; + + /** + * Primary mail address required as an alias too: true or false + */ + const REQUIRE_MAIL_AS_ALIAS = false; + + /** + * Attribute for forwards OR false if not possible + */ + const FORWARD_ATTR = false; + + /** + * Caseinsensitive prefix for forwards (eg. "forward:"), forwards get added with it and only forwards with it are reported + */ + const FORWARD_PREFIX = ''; + + /** + * Attribute to only forward mail, OR false if not available + */ + const FORWARD_ONLY_ATTR = false; + + /** + * Value of forward-only attribute, if empty any value will switch forward only on (checked with =*) + */ + const FORWARD_ONLY = 'forwardOnly'; + + /** + * Attribute for mailbox, to which mail gets delivered OR false if not supported + */ + const MAILBOX_ATTR = false; + + /** + * Attribute for quota limit of user in MB + */ + const QUOTA_ATTR = false; + + /** + * Caseinsensitive prefix for quota (eg. "quota:"), quota get added with it and only quota with it are reported + */ + const QUOTA_PREFIX = ''; + + /** + * Internal quota in MB is multiplicated with this factor before stored in LDAP + */ + const QUOTA_FACTOR = 1048576; + + /** + * Attribute for user name + */ + const USER_ATTR = 'uid'; + + /** + * Attribute for numeric user id (optional) + */ + const USERID_ATTR = 'uidnumber'; + + /** + * Base for all searches, defaults to $GLOBALS['egw_info']['server']['ldap_context'] and can be set via setBase($base) + * + * @var string + */ + protected $search_base; + + /** + * Special search filter for getUserData only + * + * @var string + */ + protected $search_filter; + + /** + * Log all LDAP writes / actions to error_log + */ + var $debug = false; + + /** + * from here on implementation, please do NOT copy but extend it! + */ + + /** + * Constructor + * + * @param string $defaultDomain=null + */ + function __construct($defaultDomain=null) + { + parent::__construct($defaultDomain); + + if (empty($this->search_base)) + { + $this->setBase($GLOBALS['egw_info']['server']['ldap_context']); + } + } + + /** + * Return description for EMailAdmin + * + * @return string + */ + public static function description() + { + return 'LDAP ('.static::SCHEMA.')'; + } + + /** + * Set ldap search filter for aliases and forwards (getUserData) + * + * @param string $filter + */ + function setFilter($filter) + { + $this->search_filter = $filter; + } + + /** + * Set ldap search base, default $GLOBALS['egw_info']['server']['ldap_context'] + * + * @param string $base + */ + function setBase($base) + { + $this->search_base = $base; + } + + /** + * Hook called on account creation + * + * @param array $_hookValues values for keys 'account_email', 'account_firstname', 'account_lastname', 'account_lid' + * @return boolean true on success, false on error writing to ldap + */ + function addAccount($_hookValues) + { + $mailLocalAddress = $_hookValues['account_email'] ? $_hookValues['account_email'] : + common::email_address($_hookValues['account_firstname'], + $_hookValues['account_lastname'],$_hookValues['account_lid'],$this->defaultDomain); + + $ds = $this->getLdapConnection(); + + $filter = static::USER_ATTR."=".ldap::quote($_hookValues['account_lid']); + + if (!($sri = @ldap_search($ds, $this->search_base, $filter))) + { + return false; + } + $allValues = ldap_get_entries($ds, $sri); + $accountDN = $allValues[0]['dn']; + $objectClasses = $allValues[0]['objectclass']; + unset($objectClasses['count']); + + // add our mail schema, if not already set + if(!in_array(static::SCHEMA,$objectClasses) && !in_array(strtolower(static::SCHEMA),$objectClasses)) + { + $objectClasses[] = static::SCHEMA; + } + // the new code for postfix+cyrus+ldap + $newData = array( + 'mail' => $mailLocalAddress, + 'objectclass' => $objectClasses + ); + // does schema have explicit alias attribute AND require mail added as alias too + if (static::ALIAS_ATTR && static::REQUIRE_MAIL_AS_ALIAS) + { + $newData[static::ALIAS_ATTR] = static::ALIAS_PREFIX.$mailLocalAddress; + } + // does schema support enabling/disabling mail via attribute + if (static::MAIL_ENABLE_ATTR) + { + $newData[static::MAIL_ENABLE_ATTR] = static::MAIL_ENABLED; + } + // does schema support an explicit mailbox name --> set it + if (static::MAILBOX_ATTR) + { + $newData[static::MAILBOX_ATTR] = self::mailbox_addr($_hookValues); + } + + if (!($ret = ldap_mod_replace($ds, $accountDN, $newData)) || $this->debug) + { + error_log(__METHOD__.'('.array2string(func_get_args()).") --> ldap_mod_replace(,'$accountDN',". + array2string($newData).') returning '.array2string($ret). + (!$ret?' ('.ldap_error($ds).')':'')); + } + return $ret; + } + + /** + * Get all email addresses of an account + * + * @param string $_accountName + * @return array + */ + function getAccountEmailAddress($_accountName) + { + $emailAddresses = array(); + $ds = $this->getLdapConnection(); + $filter = '(&'.static::USER_FILTER.'('.static::USER_ATTR.'='.ldap::quote($_accountName).'))'; + $attributes = array('dn', 'mail', static::ALIAS_ATTR); + $sri = @ldap_search($ds, $this->search_base, $filter, $attributes); + + if ($sri) + { + $realName = trim($GLOBALS['egw_info']['user']['account_firstname'] . (!empty($GLOBALS['egw_info']['user']['account_firstname']) ? ' ' : '') . $GLOBALS['egw_info']['user']['account_lastname']); + $allValues = ldap_get_entries($ds, $sri); + + if(isset($allValues[0]['mail'])) + { + foreach($allValues[0]['mail'] as $key => $value) + { + if ($key === 'count') continue; + + $emailAddresses[] = array ( + 'name' => $realName, + 'address' => $value, + 'type' => !$key ? 'default' : 'alternate', + ); + } + } + if (static::ALIAS_ATTR && isset($allValues[0][static::ALIAS_ATTR])) + { + foreach(self::getAttributePrefix($allValues[0][static::ALIAS_ATTR], static::ALIAS_PREFIX) as $value) + { + $emailAddresses[] = array( + 'name' => $realName, + 'address' => $value, + 'type' => 'alternate' + ); + } + } + } + if ($this->debug) error_log(__METHOD__."('$_accountName') returning ".array2string($emailAddresses)); + + return $emailAddresses; + } + + /** + * Get the data of a given user + * + * Multiple accounts may match, if an email address is specified. + * In that case only mail routing fields "uid", "mailbox" and "forward" contain values + * from all accounts! + * + * @param int|string $user numerical account-id, account-name or email address + * @param boolean $match_uid_at_domain=true true: uid@domain matches, false only an email or alias address matches + * @return array with values for keys 'mailLocalAddress', 'mailAlternateAddress' (array), 'mailForwardingAddress' (array), + * 'accountStatus' ("active"), 'quotaLimit' and 'deliveryMode' ("forwardOnly") + */ + function getUserData($user, $match_uid_at_domain=false) + { + $userData = array( + 'mailbox' => array(), + 'forward' => array(), + + ); + + $ldap = $this->getLdapConnection(); + + if (is_numeric($user) && static::USERID_ATTR) + { + $filter = '('.static::USERID_ATTR.'='.(int)$user.')'; + } + elseif (strpos($user, '@') === false) + { + if (is_numeric($user)) $user = $GLOBALS['egw']->accounts->id2name($user); + $filter = '(&'.static::USER_FILTER.'('.static::USER_ATTR.'='.ldap::quote($user).'))'; + } + else // email address --> build filter by attributes defined in config + { + list($namepart, $domain) = explode('@', $user); + if (!empty($this->search_filter)) + { + $filter = strtr($this->search_filter, array( + '%s' => ldap::quote($user), + '%u' => ldap::quote($namepart), + '%d' => ldap::quote($domain), + )); + } + else + { + $to_or = array('(mail='.ldap::quote($user).')'); + if ($match_uid_at_domain) $to_or[] = '('.static::USER_ATTR.'='.ldap::quote($namepart).')'; + if (static::ALIAS_ATTR) + { + $to_or[] = '('.static::ALIAS_ATTR.'='.static::ALIAS_PREFIX.ldap::quote($user).')'; + } + $filter = count($to_or) > 1 ? '(|'.explode('', $to_or).')' : $to_or[0]; + + // if an enable attribute is set, only return enabled accounts + if (static::MAIL_ENABLE_ATTR) + { + $filter = '(&('.static::MAIL_ENABLE_ATTR.'='. + (static::MAIL_ENABLED ? static::MAIL_ENABLED : '*').")$filter)"; + } + } + } + $attributes = array_values(array_diff(array( + 'mail', 'objectclass', static::USER_ATTR, static::MAIL_ENABLE_ATTR, static::ALIAS_ATTR, + static::MAILBOX_ATTR, static::FORWARD_ATTR, static::FORWARD_ONLY_ATTR, static::QUOTA_ATTR, + ), array(false, ''))); + + $sri = ldap_search($ldap, $this->search_base, $filter, $attributes); + + if ($sri) + { + $allValues = ldap_get_entries($ldap, $sri); + if ($this->debug) error_log(__METHOD__."('$user') --> ldap_search(, '$this->search_base', '$filter') --> ldap_get_entries=".array2string($allValues[0])); + + foreach($allValues as $key => $values) + { + if ($key === 'count') continue; + + // groups are always active (if they have an email) and allways forwardOnly + if (in_array(static::GROUP_SCHEMA, $values['objectclass'])) + { + $accountStatus = emailadmin_smtp::MAIL_ENABLED; + $deliveryMode = emailadmin_smtp::FORWARD_ONLY; + } + else // for users we have to check the attributes + { + if (static::MAIL_ENABLE_ATTR) + { + $accountStatus = isset($values[static::MAIL_ENABLE_ATTR]) && + (static::MAIL_ENABLED && !strcasecmp($values[static::MAIL_ENABLE_ATTR][0], static::MAIL_ENABLED) || + !static::MAIL_ENABLED && $values[static::ALIAS_ATTR ? static::ALIAS_ATTR : 'mail']['count'] > 0) ? + emailadmin_smtp::MAIL_ENABLED : ''; + } + else + { + $accountStatus = $values[static::ALIAS_ATTR ? static::ALIAS_ATTR : 'mail']['count'] > 0 ? + emailadmin_smtp::MAIL_ENABLED : ''; + } + if (static::FORWARD_ONLY_ATTR) + { + if (static::FORWARD_ONLY) // check caseinsensitiv for existence of that value + { + $deliveryMode = self::getAttributePrefix($values[static::FORWARD_ONLY_ATTR], static::FORWARD_ONLY) ? + emailadmin_smtp::FORWARD_ONLY : ''; + } + else // check for existence of any value + { + $deliveryMode = $values[static::FORWARD_ONLY_ATTR]['count'] > 0 ? + emailadmin_smtp::FORWARD_ONLY : ''; + } + } + else + { + $deliveryMode = ''; + } + } + + // collect mail routing data (can be from multiple (active) accounts and groups!) + if ($accountStatus) + { + // groups never have a mailbox, accounts can have a deliveryMode of "forwardOnly" + if ($deliveryMode != emailadmin_smtp::FORWARD_ONLY) + { + $userData[static::USER_ATTR][] = $values[static::USER_ATTR][0]; + if (static::MAILBOX_ATTR && isset($values[static::MAILBOX_ATTR])) + { + $userData['mailbox'][] = $values[static::MAILBOX_ATTR][0]; + } + } + if (static::FORWARD_ATTR && $values[static::FORWARD_ATTR]) + { + $userData['forward'] = array_merge($userData['forward'], + self::getAttributePrefix($values[static::FORWARD_ATTR], static::FORWARD_PREFIX, false)); + } + } + + // regular user-data can only be from users, NOT groups + if (in_array(static::GROUP_SCHEMA, $values['objectclass'])) continue; + + $userData['mailLocalAddress'] = $values['mail'][0]; + $userData['accountStatus'] = $accountStatus; + + if (static::ALIAS_ATTR) + { + $userData['mailAlternateAddress'] = self::getAttributePrefix($values[static::ALIAS_ATTR], static::ALIAS_PREFIX); + } + else + { + $userData['mailAlternateAddress'] = (array)$values['mail']; + unset($userData['mailAlternateAddress']['count']); + unset($userData['mailAlternateAddress'][0]); + $userData['mailAlternateAddress'] = array_values($userData['mailAlternateAddress']); + } + + if (static::FORWARD_ATTR) + { + $userData['mailForwardingAddress'] = self::getAttributePrefix($values[static::FORWARD_ATTR], static::FORWARD_PREFIX); + } + + if (static::MAILBOX_ATTR) $userData['mailMessageStore'] = $values[static::MAILBOX_ATTR][0]; + + $userData['deliveryMode'] = $deliveryMode; + + // eg. suse stores all email addresses as aliases + if (static::REQUIRE_MAIL_AS_ALIAS && + ($k = array_search($userData['mailLocalAddress'],$userData['mailAlternateAddress'])) !== false) + { + unset($userData['mailAlternateAddress'][$k]); + } + + if (static::QUOTA_ATTR && isset($values[static::QUOTA_ATTR])) + { + $userData['quotaLimit'] = self::getAttributePrefix($values[static::QUOTA_ATTR], static::QUOTA_PREFIX); + $userData['quotaLimit'] = array_shift($userData['quotaLimit']); + $userData['quotaLimit'] = $userData['quotaLimit'] ? $userData['quotaLimit'] / static::QUOTA_FACTOR : null; + } + } + } + if ($this->debug) error_log(__METHOD__."('$user') returning ".array2string($userData)); + + return $userData; + } + + /** + * Set the data of a given user + * + * @param int $_uidnumber numerical user-id + * @param array $_mailAlternateAddress + * @param array $_mailForwardingAddress + * @param string $_deliveryMode + * @param string $_accountStatus + * @param string $_mailLocalAddress + * @param int $_quota in MB + * @param boolean $_forwarding_only=false not used as we have our own addAccount method + * @param string $_setMailbox=null used only for account migration + * @return boolean true on success, false on error writing to ldap + */ + function setUserData($_uidnumber, array $_mailAlternateAddress, array $_mailForwardingAddress, $_deliveryMode, + $_accountStatus, $_mailLocalAddress, $_quota, $_forwarding_only=false, $_setMailbox=null) + { + unset($_forwarding_only); // not used + + if (static::USERID_ATTR) + { + $filter = static::USERID_ATTR.'='.(int)$_uidnumber; + } + else + { + $uid = $GLOBALS['egw']->accounts->id2name($_uidnumber); + $filter = static::USER_ATTR.'='.ldap::quote($uid); + } + $ldap = $this->getLdapConnection(); + + if (!($sri = @ldap_search($ldap, $this->search_base, $filter))) + { + return false; + } + $allValues = ldap_get_entries($ldap, $sri); + + $accountDN = $allValues[0]['dn']; + $uid = $allValues[0][static::USER_ATTR][0]; + $objectClasses = $allValues[0]['objectclass']; + + unset($objectClasses['count']); + + if(!in_array(static::SCHEMA,$objectClasses) && !in_array(strtolower(static::SCHEMA),$objectClasses)) + { + $objectClasses[] = static::SCHEMA; + $newData['objectclass'] = $objectClasses; + } + + sort($_mailAlternateAddress); + sort($_mailForwardingAddress); + + $newData['mail'] = $_mailLocalAddress; + // does schema have explicit alias attribute + if (static::ALIAS_ATTR) + { + self::setAttributePrefix($newData[static::ALIAS_ATTR], $_mailAlternateAddress, static::ALIAS_PREFIX); + + // all email must be stored as alias for suse + if (static::REQUIRE_MAIL_AS_ALIAS && !in_array($_mailLocalAddress,(array)$_mailAlternateAddress)) + { + self::setAttributePrefix($newData[static::ALIAS_ATTR], $_mailLocalAddress, static::ALIAS_PREFIX); + } + } + // or de we add them - if existing - to mail attr + elseif ($_mailAlternateAddress) + { + self::setAttributePrefix($newData['mail'], $_mailAlternateAddress, static::ALIAS_PREFIX); + } + // does schema support to store forwards + if (static::FORWARD_ATTR) + { + self::setAttributePrefix($newData[static::FORWARD_ATTR], $_mailForwardingAddress, static::FORWARD_PREFIX); + } + // does schema support only forwarding incomming mail + if (static::FORWARD_ONLY_ATTR) + { + self::setAttributePrefix($newData[static::FORWARD_ONLY_ATTR], + $_deliveryMode ? (static::FORWARD_ONLY ? static::FORWARD_ONLY : 'forwardOnly') : array()); + } + // does schema support an explicit mailbox name --> set it with $uid@$domain + if (static::MAILBOX_ATTR && empty($allValues[0][static::MAILBOX_ATTR][0])) + { + $newData[static::MAILBOX_ATTR] = $this->mailbox_addr(array( + 'account_id' => $_uidnumber, + 'account_lid' => $uid, + 'account_email' => $_mailLocalAddress, + )); + } + if (static::QUOTA_ATTR) + { + self::setAttributePrefix($newData[static::QUOTA_ATTR], + (int)$_quota > 0 ? (int)$_quota*static::QUOTA_FACTOR : array(), static::QUOTA_PREFIX); + } + // does schema support enabling/disabling mail via attribute + if (static::MAIL_ENABLE_ATTR) + { + $newData[static::MAIL_ENABLE_ATTR] = $_accountStatus ? static::MAIL_ENABLED : array(); + } + // if we have no mail-enabled attribute, but require primary mail in aliases-attr + // we do NOT write aliases, if mail is not enabled + if (!$_accountStatus && !static::MAIL_ENABLE_ATTR && static::REQUIRE_MAIL_AS_ALIAS) + { + $newData[static::ALIAS_ATTR] = array(); + } + // does schema support an explicit mailbox name --> set it, $_setMailbox is given + if (static::MAILBOX_ATTR && $_setMailbox) + { + $newData[static::MAILBOX_ATTR] = $_setMailbox; + } + if ($this->debug) error_log(__METHOD__.'('.array2string(func_get_args()).") --> ldap_mod_replace(,'$accountDN',".array2string($newData).')'); + + return ldap_mod_replace($ldap, $accountDN, $newData); + } + + /** + * Saves the forwarding information + * + * @param int $_accountID + * @param string $_forwardingAddress + * @param string $_keepLocalCopy 'yes' + * @return boolean true on success, false on error writing to ldap + */ + function saveSMTPForwarding($_accountID, $_forwardingAddress, $_keepLocalCopy) + { + $ds = $this->getLdapConnection(); + if (static::USERID_ATTR) + { + $filter = '(&'.static::USER_FILTER.'('.static::USERID_ATTR.'='.(int)$_accountID.'))'; + } + else + { + $uid = $GLOBALS['egw']->accounts->id2name($_accountID); + $filter = '(&'.static::USER_FILTER.'('.static::USER_ATTR.'='.ldap::quote($uid).'))'; + } + $attributes = array('dn', static::FORWARD_ATTR, 'objectclass'); + if (static::FORWARD_ONLY_ATTR) + { + $attributes[] = static::FORWARD_ONLY_ATTR; + } + $sri = ldap_search($ds, $this->search_base, $filter, $attributes); + + if ($sri) + { + $newData = array(); + $allValues = ldap_get_entries($ds, $sri); + $objectClasses = $allValues[0]['objectclass']; + $newData['objectclass'] = $allValues[0]['objectclass']; + + unset($newData['objectclass']['count']); + + if(!in_array(static::SCHEMA,$objectClasses)) + { + $newData['objectclass'][] = static::SCHEMA; + } + if (static::FORWARD_ATTR) + { + // copy all non-forward data (different prefix) to newData, all existing forwards to $forwards + $newData[static::FORWARD_ATTR] = $allValues[0][static::FORWARD_ATTR]; + $forwards = self::getAttributePrefix($newData[static::FORWARD_ATTR], static::FORWARD_PREFIX); + + if(!empty($_forwardingAddress)) + { + if($forwards) + { + if (!is_array($_forwardingAddress)) + { + // replace the first forwarding address (old behavior) + $forwards[0] = $_forwardingAddress; + } + else + { + // replace all forwarding Addresses + $forwards = $_forwardingAddress; + } + } + else + { + $forwards = (array)$_forwardingAddress; + } + if (static::FORWARD_ONLY_ATTR) + { + self::getAttributePrefix($newData[static::FORWARD_ONLY_ATTR], static::FORWARD_ONLY); + self::setAttributePrefix($newData[static::FORWARD_ONLY_ATTR], + $_keepLocalCopy == 'yes' ? array() : static::FORWARD_ONLY); + } + } + else + { + $forwards = array(); + } + // merge in again all new set forwards incl. opt. prefix + self::getAttributePrefix($newData[static::FORWARD_ATTR], $forwards, static::FORWARD_PREFIX); + } + if ($this->debug) error_log(__METHOD__.'('.array2string(func_get_args()).") --> ldap_mod_replace(,'{$allValues[0]['dn']}',".array2string($newData).')'); + + return ldap_modify ($ds, $allValues[0]['dn'], $newData); + } + } + + /** + * Get configured mailboxes of a domain + * + * @param boolean $return_inactive return mailboxes NOT marked as accountStatus=active too + * @return array uid => name-part of mailMessageStore + */ + function getMailboxes($return_inactive) + { + $ds = $this->getLdapConnection(); + $filter = array("(mail=*)"); + $attrs = array(static::USER_ATTR, 'mail'); + if (static::MAILBOX_ATTR) + { + $filter[] = '('.static::MAILBOX_ATTR.'=*)'; + $attrs[] = static::MAILBOX_ATTR; + } + if (!$return_inactive && static::MAIL_ENABLE_ATTR) + { + $filter[] = '('.static::MAIL_ENABLE_ATTR.'='.static::MAIL_ENABLED.')'; + } + if (count($filter) > 1) + { + $filter = '(&'.implode('', $filter).')'; + } + else + { + $filter = $filter[0]; + } + if (!($sr = @ldap_search($ds, $this->search_base, $filter, $attrs))) + { + //error_log("Error ldap_search(\$ds, '$base', '$filter')!"); + return array(); + } + $entries = ldap_get_entries($ds, $sr); + + unset($entries['count']); + + $mailboxes = array(); + foreach($entries as $entry) + { + if ($entry[static::USER_ATTR][0] == 'anonymous') continue; // anonymous is never a mail-user! + list($mailbox) = explode('@', $entry[static::MAILBOX_ATTR ? static::MAILBOX_ATTR : 'mail'][0]); + $mailboxes[$entry[static::USER_ATTR][0]] = $mailbox; + } + return $mailboxes; + } + + /** + * Set values in a given LDAP attribute using an optional prefix + * + * @param array &$attribute on return array with values set and existing values preseved + * @param string|array $values value(s) to set + * @param string $prefix='' prefix to use or '' + */ + protected static function setAttributePrefix(&$attribute, $values, $prefix='') + { + //$attribute_in = $attribute; + if (!isset($attribute)) $attribute = array(); + if (!is_array($attribute)) $attribute = array($attribute); + + foreach((array)$values as $value) + { + $attribute[] = $prefix.$value; + } + //error_log(__METHOD__."(".array2string($attribute_in).", ".array2string($values).", '$prefix') attribute=".array2string($attribute)); + } + + /** + * Get values having an optional prefix from a given LDAP attribute + * + * @param array &$attribute only "count" and prefixed values get removed, get's reindexed, if values have been removed + * @param string $prefix='' prefix to use or '' + * @param boolean $remove=true remove returned values from $attribute + * @return array with values (prefix removed) or array() if nothing found + */ + protected static function getAttributePrefix(&$attribute, $prefix='', $remove=true) + { + //$attribute_in = $attribute; + $values = array(); + + if (isset($attribute)) + { + unset($attribute['count']); + + foreach($attribute as $key => $value) + { + if (!$prefix || stripos($value, $prefix) === 0) + { + if ($remove) unset($attribute[$key]); + $values[] = substr($value, strlen($prefix)); + } + } + // reindex $attribute, if neccessary + if ($values && $attribute) $attribute = array_values($attribute); + } + //error_log(__METHOD__."(".array2string($attribute_in).", '$prefix', $remove) attribute=".array2string($attribute).' returning '.array2string($values)); + return $values; + } + + /** + * Return LDAP connection + */ + protected function getLdapConnection() + { + static $ldap=null; + + if (is_null($ldap)) $ldap = $GLOBALS['egw']->ldap->ldapConnect(); + + return $ldap; + } +} diff --git a/emailadmin/inc/class.emailadmin_smtp_mandriva.inc.php b/emailadmin/inc/class.emailadmin_smtp_mandriva.inc.php new file mode 100644 index 0000000000..dd815830b7 --- /dev/null +++ b/emailadmin/inc/class.emailadmin_smtp_mandriva.inc.php @@ -0,0 +1,84 @@ + + * @copyright (c) 2010-13 by Ralf Becker + * @version $Id$ + */ + +/** + * Postfix with Mandriva mailAccount schema + */ +class emailadmin_smtp_mandriva extends emailadmin_smtp_ldap +{ + /** + * Capabilities of this class (pipe-separated): default, forward + */ + const CAPABILITIES = 'default|forward'; + + /** + * Name of schema, has to be in the right case! + */ + const SCHEMA = 'mailAccount'; + + /** + * Attribute to enable mail for an account, OR false if existence of ALIAS_ATTR is enough for mail delivery + */ + const MAIL_ENABLE_ATTR = 'mailenable'; + /** + * Attribute value to enable mail for an account, OR false if existense of attribute is enough to enable account + */ + const MAIL_ENABLED = 'OK'; + + /** + * Attribute for aliases OR false to use mail + */ + const ALIAS_ATTR = 'mailalias'; + + /** + * Primary mail address required as an alias too: true or false + */ + const REQUIRE_MAIL_AS_ALIAS=false; + + /** + * Attribute for forwards OR false if not possible + */ + const FORWARD_ATTR = 'maildrop'; + + /** + * Attribute to only forward mail, OR false if not available + */ + const FORWARD_ONLY_ATTR = false; + /** + * Attribute value to only forward mail + */ + const FORWARD_ONLY = false; + + /** + * Attribute for mailbox, to which mail gets delivered OR false if not supported + */ + const MAILBOX_ATTR = 'mailbox'; + + /** + * Attribute for quota limit of user in MB + */ + const QUOTA_ATTR = 'mailuserquota'; + + /** + * Log all LDAP writes / actions to error_log + */ + var $debug = false; + + /** + * Return description for EMailAdmin + * + * @return string + */ + public static function description() + { + return 'LDAP (Mandriva '.static::SCHEMA.')'; + } +} diff --git a/emailadmin/inc/class.emailadmin_smtp_qmail.inc.php b/emailadmin/inc/class.emailadmin_smtp_qmail.inc.php new file mode 100644 index 0000000000..383b32befd --- /dev/null +++ b/emailadmin/inc/class.emailadmin_smtp_qmail.inc.php @@ -0,0 +1,76 @@ + + * @copyright (c) 2013 by Ralf Becker + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @version $Id$ + */ + +/** + * Postfix with new qmailUser schema (mailQuotaSize instead of mailQuota) + */ +class emailadmin_smtp_qmail extends emailadmin_smtp_ldap +{ + /** + * Capabilities of this class (pipe-separated): default, forward + */ + const CAPABILITIES = 'default|forward'; + + /** + * Name of schema, has to be in the right case! + */ + const SCHEMA = 'qmailUser'; + + /** + * Attribute to enable mail for an account, OR false if existence of ALIAS_ATTR is enough for mail delivery + */ + const MAIL_ENABLE_ATTR = 'accountstatus'; + /** + * Attribute value to enable mail for an account, OR false if existense of attribute is enough to enable account + */ + const MAIL_ENABLED = 'active'; + + /** + * Attribute for aliases OR false to use mail + */ + const ALIAS_ATTR = 'mailalternateaddress'; + + /** + * Primary mail address required as an alias too: true or false + */ + const REQUIRE_MAIL_AS_ALIAS=false; + + /** + * Attribute for forwards OR false if not possible + */ + const FORWARD_ATTR = 'mailforwardingaddress'; + + /** + * Attribute to only forward mail, OR false if not available + */ + const FORWARD_ONLY_ATTR = 'deliverymode'; + + /** + * Attribute for mailbox, to which mail gets delivered OR false if not supported + */ + const MAILBOX_ATTR = 'mailmessagestore'; + + /** + * Attribute for quota limit of user in MB + */ + const QUOTA_ATTR = 'mailquotasize'; + + /** + * Return description for EMailAdmin + * + * @return string + */ + public static function description() + { + return 'LDAP (new '.static::SCHEMA.')'; + } +} diff --git a/emailadmin/inc/class.emailadmin_smtp_sql.inc.php b/emailadmin/inc/class.emailadmin_smtp_sql.inc.php new file mode 100644 index 0000000000..75fa22be89 --- /dev/null +++ b/emailadmin/inc/class.emailadmin_smtp_sql.inc.php @@ -0,0 +1,376 @@ + + * @copyright (c) 2012-13 by Ralf Becker + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @version $Id$ + */ + +/** + * SMTP configuration / mail accounts via SQL + */ +class emailadmin_smtp_sql extends emailadmin_smtp +{ + /** + * Label shown in EMailAdmin + */ + const DESCRIPTION = 'SQL'; + + /** + * Capabilities of this class (pipe-separated): default, forward + */ + const CAPABILITIES = 'default|forward'; + + /** + * Reference to global db object + * + * @var egw_db + */ + protected $db; + + /** + * Name of table + */ + const TABLE = 'egw_mailaccounts'; + /** + * Name of app our table belongs to + */ + const APP = 'emailadmin'; + /** + * Values for mail_type column + * + * enabled and delivery must have smaller values then alias, forward or mailbox (getUserData depend on it)! + */ + const TYPE_ENABLED = 0; + const TYPE_DELIVERY = 1; + const TYPE_QUOTA = 2; + const TYPE_ALIAS = 3; + const TYPE_FORWARD = 4; + const TYPE_MAILBOX = 5; + + /** + * Constructor + * + * @param string $defaultDomain=null + */ + function __construct($defaultDomain=null) + { + parent::__construct($defaultDomain); + + $this->db = $GLOBALS['egw']->db; + } + + /** + * Get all email addresses of an account + * + * @param string $_accountName + * @return array + */ + function getAccountEmailAddress($_accountName) + { + $emailAddresses = parent::getAccountEmailAddress($_accountName); + + if (($account_id = $this->accounts->name2id($_accountName, 'account_lid', 'u'))) + { + foreach($this->db->select(self::TABLE, 'mail_value', array( + 'account_id' => $account_id, + 'mail_type' => self::TYPE_ALIAS, + ), __LINE__, __FILE__, false, 'ORDER BY mail_value', self::APP) as $row) + { + $emailAddresses[] = array ( + 'name' => $emailAddresses[0]['name'], + 'address' => $row['mail_value'], + 'type' => 'alternate', + ); + } + } + if ($this->debug) error_log(__METHOD__."('$_accountName') returning ".array2string($emailAddresses)); + + return $emailAddresses; + } + + /** + * Get the data of a given user + * + * Multiple accounts may match, if an email address is specified. + * In that case only mail routing fields "uid", "mailbox" and "forward" contain values + * from all accounts! + * + * @param int|string $user numerical account-id, account-name or email address + * @param boolean $match_uid_at_domain=true true: uid@domain matches, false only an email or alias address matches + * @return array with values for keys 'mailLocalAddress', 'mailAlternateAddress' (array), 'mailForwardingAddress' (array), + * 'accountStatus' ("active"), 'quotaLimit' and 'deliveryMode' ("forwardOnly") + */ + function getUserData($user, $match_uid_at_domain=false) + { + $userData = array(); + + if (is_numeric($user) && $this->accounts->exists($user)) + { + $account_id = $user; + } + elseif (strpos($user, '@') === false) + { + $account_id = $this->accounts->name2id($user, 'account_lid', 'u'); + } + else // email address + { + // check with primary email address + if (($account_id = $this->accounts->name2id($user, 'account_email'))) + { + $account_id = array($account_id); + } + else + { + $account_id = array(); + } + // always allow username@domain + list($account_lid) = explode('@', $user); + if ($match_uid_at_domain && ($id = $this->accounts->name2id($account_lid, 'account_lid')) && !in_array($id, $account_id)) + { + $account_id[] = $id; + } + foreach($this->db->select(self::TABLE, 'account_id', array( + 'mail_type' => self::TYPE_ALIAS, + 'mail_value' => $user, + ), __LINE__, __FILE__, false, '', self::APP) as $row) + { + if (!in_array($row['account_id'], $account_id)) $account_id[] = $row['account_id']; + } + //error_log(__METHOD__."('$user') account_id=".array2string($account_id)); + } + if ($account_id) + { + if (!is_array($account_id)) + { + $userData['mailLocalAddress'] = $this->accounts->id2name($account_id, 'account_email'); + } + $enabled = $forwardOnly = array(); + foreach($this->db->select(self::TABLE, '*', array( + 'account_id' => $account_id, + ), __LINE__, __FILE__, false, 'ORDER BY mail_type,mail_value', self::APP) as $row) + { + switch($row['mail_type']) + { + case self::TYPE_ENABLED: + $userData['accountStatus'] = $row['mail_value']; + $enabled[$row['account_id']] = $row['mail_value'] == self::MAIL_ENABLED; + break; + + case self::TYPE_DELIVERY: + $userData['deliveryMode'] = !strcasecmp($row['mail_value'], self::FORWARD_ONLY) ? emailadmin_smtp::FORWARD_ONLY : ''; + $forwardOnly[$row['account_id']] = !strcasecmp($row['mail_value'], self::FORWARD_ONLY); + break; + + case self::TYPE_QUOTA: + $userData['quotaLimit'] = $row['mail_value']; + break; + + case self::TYPE_ALIAS: + $userData['mailAlternateAddress'][] = $row['mail_value']; + break; + + case self::TYPE_FORWARD: + $userData['mailForwardingAddress'][] = $row['mail_value']; + if ($row['account_id'] < 0 || $enabled[$row['account_id']]) + { + $userData['forward'][] = $row['mail_value']; + } + break; + + case self::TYPE_MAILBOX: + $userData['mailMessageStore'] = $row['mail_value']; + //error_log(__METHOD__."('$user') row=".array2string($row).', enabled[$row[account_id]]='.array2string($enabled[$row['account_id']]).', forwardOnly[$row[account_id]]='.array2string($forwardOnly[$row['account_id']])); + if ($row['account_id'] > 0 && $enabled[$row['account_id']] && !$forwardOnly[$row['account_id']]) + { + $userData['uid'][] = $this->accounts->id2name($row['account_id'], 'account_lid'); + $userData['mailbox'][] = $row['mail_value']; + } + break; + } + } + // if query by email-address (not a regular call from fmail) + if (is_array($account_id)) + { + // add group-members for groups as forward (that way we dont need to store&update them) + foreach($account_id as $id) + { + if ($id < 0 && ($members = $this->accounts->members($id, true))) + { + foreach($members as $member) + { + if (($email = $this->accounts->id2name($member, 'account_email')) && !in_array($email, (array)$userData['forward'])) + { + $userData['forward'][] = $email; + } + } + } + } + } + } + if ($this->debug) error_log(__METHOD__."('$user') returning ".array2string($userData)); + + return $userData; + } + + /** + * Set the data of a given user + * + * @param int $_uidnumber numerical user-id + * @param array $_mailAlternateAddress + * @param array $_mailForwardingAddress + * @param string $_deliveryMode + * @param string $_accountStatus + * @param string $_mailLocalAddress + * @param int $_quota in MB + * @param boolean $_forwarding_only=false true: store only forwarding info, used internally by saveSMTPForwarding + * @param string $_setMailbox=null used only for account migration + * @return boolean true on success, false on error writing to ldap + */ + function setUserData($_uidnumber, array $_mailAlternateAddress, array $_mailForwardingAddress, $_deliveryMode, + $_accountStatus, $_mailLocalAddress, $_quota, $_forwarding_only=false, $_setMailbox=null) + { + if ($this->debug) error_log(__METHOD__."($_uidnumber, ".array2string($_mailAlternateAddress).', '.array2string($_mailForwardingAddress).", '$_deliveryMode', '$_accountStatus', '$_mailLocalAddress', $_quota, forwarding_only=".array2string($_forwarding_only).') '.function_backtrace()); + + if (!$_forwarding_only && $this->accounts->id2name($_uidnumber, 'account_email') !== $_mailLocalAddress) + { + $account = $this->accounts->read($_uidnumber); + $account['account_email'] = $_mailLocalAddress; + $this->accounts->save($account); + } + $flags = array( + self::TYPE_DELIVERY => $_deliveryMode, + self::TYPE_ENABLED => $_accountStatus, + self::TYPE_QUOTA => $_quota, + ); + $where = array('account_id' => $_uidnumber); + if ($_forwarding_only) $where['mail_type'] = array(self::TYPE_FORWARD, self::TYPE_DELIVERY); + // find all invalid values: either delete or update them + $delete_ids = array(); + foreach($this->db->select(self::TABLE, '*', $where, __LINE__, __FILE__, false, '', self::APP) as $row) + { + switch($row['mail_type']) + { + case self::TYPE_ALIAS: + $new_addresses =& $_mailAlternateAddress; + // fall-throught + case self::TYPE_FORWARD: + if ($row['mail_type'] == self::TYPE_FORWARD) $new_addresses =& $_mailForwardingAddress; + if (($key = array_search($row['mail_value'], $new_addresses)) === false) + { + $delete_ids[] = $row['mail_id']; + } + else + { + unset($new_addresses[$key]); // no need to store + } + break; + + case self::TYPE_MAILBOX: + $mailbox = $row['mail_value']; + break; + + case self::TYPE_QUOTA: + case self::TYPE_DELIVERY: + case self::TYPE_ENABLED: + //error_log(__METHOD__.": ".__LINE__." row=".array2string($row).", flags['$row[mail_type]']=".array2string($flags[$row['mail_type']])); + if ($row['mail_value'] != $flags[$row['mail_type']]) + { + if ($flags[$row['mail_type']]) + { + $this->db->update(self::TABLE, array( + 'mail_value' => $flags[$row['mail_type']], + ), array( + 'mail_id' => $row['mail_id'], + ), __LINE__, __FILE__, self::APP); + } + else + { + $delete_ids[] = $row['mail_id']; + } + } + unset($flags[$row['mail_type']]); + break; + } + } + if ($delete_ids) + { + $this->db->delete(self::TABLE, array('mail_id' => $delete_ids), __LINE__, __FILE__, self::APP); + } + // set mailbox address, if explicitly requested by $_setMailbox parameter + if ($_setMailbox) + { + $flags[self::TYPE_MAILBOX] = $_setMailbox; + } + // set mailbox address, if not yet set + elseif (!$_forwarding_only && empty($mailbox)) + { + $flags[self::TYPE_MAILBOX] = $this->mailbox_addr(array( + 'account_id' => $_uidnumber, + 'account_lid' => $this->accounts->id2name($_uidnumber, 'account_lid'), + 'account_email' => $_mailLocalAddress, + )); + } + // store all new values + foreach($flags+array( + self::TYPE_ALIAS => $_mailAlternateAddress, + self::TYPE_FORWARD => $_mailForwardingAddress, + ) as $type => $values) + { + if ($values && (!$_forwarding_only || in_array($type, array(self::TYPE_FORWARD, self::TYPE_DELIVERY)))) + { + foreach((array)$values as $value) + { + $this->db->insert(self::TABLE, array( + 'account_id' => $_uidnumber, + 'mail_type' => $type, + 'mail_value' => $value, + ), false, __LINE__, __FILE__, self::APP); + } + } + } + return true; + } + + /** + * Get configured mailboxes of a domain + * + * @param boolean $return_inactive return mailboxes NOT marked as accountStatus=active too + * @return array uid => name-part of mailMessageStore + */ + function getMailboxes($return_inactive) + { + $join = 'JOIN '.accounts_sql::TABLE.' ON '.self::TABLE.'.account_id='.accounts_sql::TABLE.'.account_id'; + if (!$return_inactive) + { + $join .= ' JOIN '.self::TABLE.' active ON active.account_id='.self::TABLE.'.account_id AND active.mail_type='.self::TYPE_ENABLED; + } + $mailboxes = array(); + foreach($this->db->select(self::TABLE, 'account_lid AS uid,'.self::TABLE.'.mail_value AS mailbox', + self::TABLE.'.mail_type='.self::TYPE_MAILBOX, + __LINE__, __FILE__, false, 'ORDER BY account_lid', self::APP, 0, $join) as $row) + { + if ($row['uid'] == 'anonymous') continue; // anonymous is never a mail-user! + list($mailbox) = explode('@', $row['mailbox']); + $mailboxes[$row['uid']] = $mailbox; + } + return $mailboxes; + } + + /** + * Hook called on account deletion + * + * @param array $_hookValues values for keys 'account_lid', 'account_id' + * @return boolean true on success, false on error + */ + function deleteAccount($_hookValues) + { + $this->db->delete(self::TABLE, array('account_id' => $_hookValues['account_id']), __LINE__, __FILE__); + + return true; + } +} diff --git a/emailadmin/inc/class.emailadmin_smtp_suse.inc.php b/emailadmin/inc/class.emailadmin_smtp_suse.inc.php new file mode 100644 index 0000000000..9c5efcc57a --- /dev/null +++ b/emailadmin/inc/class.emailadmin_smtp_suse.inc.php @@ -0,0 +1,72 @@ + + * @copyright (c) 2009-13 by Ralf Becker + * @version $Id$ + */ + +/** + * Support for Postfix with suse-mailserver schemas + * + * Used in SLES and openSUSE 10+ + */ +class emailadmin_smtp_suse extends emailadmin_smtp_ldap +{ + /** + * Capabilities of this class (pipe-separated): default, forward + */ + const CAPABILITIES = 'default|forward'; + + /** + * Name of schema, has to be in the right case + */ + const SCHEMA = 'suseMailRecipient'; + + /** + * Attribute to enable mail for an account, OR false if existence of ALIAS_ATTR is enough for mail delivery + */ + const MAIL_ENABLE_ATTR = false; + /** + * Attribute value to enable mail for an account, OR false if existense of attribute is enough to enable account + */ + const MAIL_ENABLED = false; + + /** + * Attribute for aliases OR false to use mail + */ + const ALIAS_ATTR = 'susemailacceptaddress'; + + /** + * Primary mail address required as an alias too: true or false + */ + const REQUIRE_MAIL_AS_ALIAS = true; + + /** + * Attribute for forwards OR false if not possible + */ + const FORWARD_ATTR = 'susemailforwardaddress'; + + /** + * Attribute to only forward mail, OR false if not available + */ + const FORWARD_ONLY_ATTR = false; + /** + * Attribute value to only forward mail + */ + const FORWARD_ONLY = false; + + /** + * Attribute for mailbox, to which mail gets delivered OR false if not supported + */ + const MAILBOX_ATTR = false; + + /** + * Log all LDAP writes / actions to error_log + */ + var $debug = false; +} diff --git a/emailadmin/inc/class.emailadmin_wizard.inc.php b/emailadmin/inc/class.emailadmin_wizard.inc.php new file mode 100644 index 0000000000..aa46112b31 --- /dev/null +++ b/emailadmin/inc/class.emailadmin_wizard.inc.php @@ -0,0 +1,1398 @@ + + * @copyright (c) 2013-14 by Ralf Becker + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @version $Id$ + */ + +/** + * Wizard to create mail accounts + * + * Wizard uses follow heuristic to search for IMAP accounts: + * 1. query Mozilla ISPDB for domain from email (perfering SSL over STARTTLS over insecure connection) + * 2. guessing and verifying in DNS server-names based on domain from email: + * - (imap|smtp).$domain, mail.$domain + * - MX for $domain + * - replace host in MX with (imap|smtp) or mail + */ +class emailadmin_wizard +{ + /** + * Enable logging of IMAP communication to given path, eg. /tmp/autoconfig.log + */ + const DEBUG_LOG = null; + /** + * Connection timeout in seconds used in autoconfig, can and should be really short! + */ + const TIMEOUT = 2; + /** + * Prefix for callback names + * + * Used as static::APP_CLASS in etemplate::exec(), to allow mail app extending this class. + */ + const APP_CLASS = 'emailadmin.emailadmin_wizard.'; + + /** + * 0: No SSL + */ + const SSL_NONE = emailadmin_account::SSL_NONE; + /** + * 1: STARTTLS on regular tcp connection/port + */ + const SSL_STARTTLS = emailadmin_account::SSL_STARTTLS; + /** + * 3: SSL (inferior to TLS!) + */ + const SSL_SSL = emailadmin_account::SSL_SSL; + /** + * 2: require TLS version 1+, no SSL version 2 or 3 + */ + const SSL_TLS = emailadmin_account::SSL_TLS; + /** + * 8: if set, verify certifcate (currently not implemented in Horde_Imap_Client!) + */ + const SSL_VERIFY = emailadmin_account::SSL_VERIFY; + + /** + * Log exception including trace to error-log, instead of just displaying the message. + * + * @var boolean + */ + public static $debug = false; + + /** + * Methods callable via menuaction + * + * @var array + */ + public $public_functions = array( + 'add' => true, + 'edit' => true, + ); + + /** + * Supported ssl types including none + * + * @var array + */ + public static $ssl_types = array( + self::SSL_TLS => 'TLS', // SSL with minimum TLS (no SSL v.2 or v.3), requires Horde_Imap_Client-2.16.0/Horde_Socket_Client-1.1.0 + self::SSL_SSL => 'SSL', + self::SSL_STARTTLS => 'STARTTLS', + 'no' => 'no', + ); + /** + * Convert ssl-type to Horde secure parameter + * + * @var array + */ + public static $ssl2secure = array( + 'SSL' => 'ssl', + 'STARTTLS' => 'tls', + 'TLS' => 'tlsv1', // SSL with minimum TLS (no SSL v.2 or v.3), requires Horde_Imap_Client-2.16.0/Horde_Socket_Client-1.1.0 + ); + /** + * Convert ssl-type to eMailAdmin acc_(imap|sieve|smtp)_ssl integer value + * + * @var array + */ + public static $ssl2type = array( + 'TLS' => self::SSL_TLS, + 'SSL' => self::SSL_SSL, + 'STARTTLS' => self::SSL_STARTTLS, + 'no' => self::SSL_NONE, + ); + + /** + * Available IMAP login types + * + * @var array + */ + public static $login_types = array( + '' => 'Username specified below for all', + 'standard' => 'username from account', + 'vmailmgr' => 'username@domainname', + //'admin' => 'Username/Password defined by admin', + 'uidNumber' => 'UserId@domain eg. u1234@domain', + 'email' => 'EMail-address from account', + ); + + /** + * List of domains know to not support Sieve + * + * Used to switch Sieve off by default, thought users can allways try switching it on. + * Testing not existing Sieve with google takes a long time, as ports are open, + * but not answering ... + * + * @var array + */ + public static $no_sieve_blacklist = array('gmail.com', 'googlemail.com'); + + /** + * Is current use a mail administrator / has run rights for EMailAdmin + * + * @var boolean + */ + protected $is_admin = false; + + /** + * Constructor + */ + public function __construct() + { + $this->is_admin = isset($GLOBALS['egw_info']['user']['apps']['emailadmin']); + + // for some reason most translation for account-wizard are in mail + translation::add_app('mail'); + + // Horde use locale for translation of error messages + common::setlocale(LC_MESSAGES); + } + + /** + * Step 1: IMAP account + * + * @param array $content + * @param type $msg + */ + public function add(array $content=array(), $msg='', $msg_type='success') + { + // otherwise we cant switch to ckeditor in edit + egw_ckeditor_config::set_csp_script_src_attrs(); + + $tpl = new etemplate_new('emailadmin.wizard'); + if (empty($content['account_id'])) + { + $content['account_id'] = $GLOBALS['egw_info']['user']['account_id']; + } + // add some defaults if not already set (+= does not overwrite existing values!) + $content += array( + 'ident_realname' => $GLOBALS['egw']->accounts->id2name($content['account_id'], 'account_fullname'), + 'ident_email' => $GLOBALS['egw']->accounts->id2name($content['account_id'], 'account_email'), + 'acc_imap_port' => 993, + 'manual_class' => 'emailadmin_manual', + ); + egw_framework::message($msg ? $msg : (string)$_GET['msg'], $msg_type); + + if (!empty($content['acc_imap_host']) || !empty($content['acc_imap_username'])) + { + $readonlys['button[manual]'] = true; + unset($content['manual_class']); + } + $tpl->exec(static::APP_CLASS.'autoconfig', $content, array( + 'acc_imap_ssl' => self::$ssl_types, + ), $readonlys, $content, 2); + } + + /** + * Try to autoconfig an account + * + * @param array $content + */ + public function autoconfig(array $content) + { + // user pressed [Skip IMAP] --> jump to SMTP config + if ($content['button'] && key($content['button']) == 'skip_imap') + { + unset($content['button']); + if (!isset($content['acc_smtp_host'])) $content['acc_smtp_host'] = ''; // do manual mode right away + return $this->smtp($content, lang('Skipping IMAP configuration!')); + } + $content['output'] = ''; + $sel_options = $readonlys = array(); + + $content['connected'] = $connected = false; + if (empty($content['acc_imap_username'])) + { + $content['acc_imap_username'] = $content['ident_email']; + } + if (!empty($content['acc_imap_host'])) + { + $hosts = array($content['acc_imap_host'] => true); + if ($content['acc_imap_port'] > 0 && !in_array($content['acc_imap_port'], array(143,993))) + { + $ssl_type = (string)array_search($content['acc_imap_ssl'], self::$ssl2type); + if ($ssl_type === '') $ssl_type = 'insecure'; + $hosts[$content['acc_imap_host']] = array( + $ssl_type => $content['acc_imap_port'], + ); + } + } + elseif (($ispdb = self::mozilla_ispdb($content['ident_email'])) && count($ispdb['imap'])) + { + $content['ispdb'] = $ispdb; + $content['output'] .= lang('Using data from Mozilla ISPDB for provider %1', $ispdb['displayName'])."\n"; + $hosts = array(); + foreach($ispdb['imap'] as $server) + { + if (!isset($hosts[$server['hostname']])) + { + $hosts[$server['hostname']] = array('username' => $server['username']); + } + if (strtoupper($server['socketType']) == 'SSL') // try TLS first + { + $hosts[$server['hostname']]['TLS'] = $server['port']; + } + $hosts[$server['hostname']][strtoupper($server['socketType'])] = $server['port']; + // make sure we prefer SSL over STARTTLS over insecure + if (count($hosts[$server['hostname']]) > 2) + { + $hosts[$server['hostname']] = self::fix_ssl_order($hosts[$server['hostname']]); + } + } + } + else + { + $hosts = $this->guess_hosts($content['ident_email'], 'imap'); + } + + // iterate over all hosts and try to connect + foreach($hosts as $host => $data) + { + $content['acc_imap_host'] = $host; + // by default we check SSL, STARTTLS and at last an insecure connection + if (!is_array($data)) $data = array('TLS' => 993, 'SSL' => 993, 'STARTTLS' => 143, 'insecure' => 143); + + foreach($data as $ssl => $port) + { + if ($ssl === 'username') continue; + + $content['acc_imap_ssl'] = (int)self::$ssl2type[$ssl]; + + $e = null; + try { + $content['output'] .= "\n".egw_time::to('now', 'H:i:s').": Trying $ssl connection to $host:$port ...\n"; + $content['acc_imap_port'] = $port; + + $imap = self::imap_client($content, self::TIMEOUT); + + //$content['output'] .= array2string($imap->capability()); + $imap->login(); + $content['output'] .= "\n".lang('Successful connected to %1 server%2.', 'IMAP', ' '.lang('and logged in'))."\n"; + if (!$imap->isSecureConnection()) + { + $content['output'] .= lang('Connection is NOT secure! Everyone can read eg. your credentials.')."\n"; + $content['acc_imap_ssl'] = 'no'; + } + //$content['output'] .= "\n\n".array2string($imap->capability()); + $content['connected'] = $connected = true; + break 2; + } + catch(Horde_Imap_Client_Exception $e) + { + switch($e->getCode()) + { + case Horde_Imap_Client_Exception::LOGIN_AUTHENTICATIONFAILED: + $content['output'] .= "\n".$e->getMessage()."\n"; + break 3; // no need to try other SSL or non-SSL connections, if auth failed + + case Horde_Imap_Client_Exception::SERVER_CONNECT: + $content['output'] .= "\n".$e->getMessage()."\n"; + if ($ssl == 'STARTTLS') break 2; // no need to try insecure connection on same port + break; + + default: + $content['output'] .= "\n".get_class($e).': '.$e->getMessage().' ('.$e->getCode().')'."\n"; + //$content['output'] .= $e->getTraceAsString()."\n"; + } + if (self::$debug) _egw_log_exception($e); + } + catch(Exception $e) { + $content['output'] .= "\n".get_class($e).': '.$e->getMessage().' ('.$e->getCode().')'."\n"; + //$content['output'] .= $e->getTraceAsString()."\n"; + if (self::$debug) _egw_log_exception($e); + } + } + } + if ($connected) // continue with next wizard step: define folders + { + unset($content['button']); + return $this->folder($content, lang('Successful connected to %1 server%2.', 'IMAP', ' '.lang('and logged in')). + ($imap->isSecureConnection() ? '' : "\n".lang('Connection is NOT secure! Everyone can read eg. your credentials.'))); + } + // add validation error, if we can identify a field + if (!$connected && $e instanceof Horde_Imap_Client_Exception) + { + switch($e->getCode()) + { + case Horde_Imap_Client_Exception::LOGIN_AUTHENTICATIONFAILED: + etemplate_new::set_validation_error('acc_imap_username', lang($e->getMessage())); + etemplate_new::set_validation_error('acc_imap_password', lang($e->getMessage())); + break; + + case Horde_Imap_Client_Exception::SERVER_CONNECT: + etemplate_new::set_validation_error('acc_imap_host', lang($e->getMessage())); + break; + } + } + $readonlys['button[manual]'] = true; + unset($content['manual_class']); + $sel_options['acc_imap_ssl'] = self::$ssl_types; + $tpl = new etemplate_new('emailadmin.wizard'); + $tpl->exec(static::APP_CLASS.'autoconfig', $content, $sel_options, $readonlys, $content, 2); + } + + /** + * Step 2: Folder - let user select trash, sent, drafs and template folder + * + * @param array $content + * @param string $msg='' + * @param Horde_Imap_Client_Socket $imap=null + */ + public function folder(array $content, $msg='', Horde_Imap_Client_Socket $imap=null) + { + if (isset($content['button'])) + { + list($button) = each($content['button']); + unset($content['button']); + switch($button) + { + case 'back': + return $this->add($content); + + case 'continue': + return $this->sieve($content); + } + } + $content['msg'] = $msg; + if (!isset($imap)) $imap = self::imap_client ($content); + + try { + //_debug_array($content); + $sel_options['acc_folder_sent'] = $sel_options['acc_folder_trash'] = + $sel_options['acc_folder_draft'] = $sel_options['acc_folder_template'] = + $sel_options['acc_folder_junk'] = self::mailboxes($imap, $content); + } + catch(Exception $e) { + $content['msg'] = $e->getMessage(); + if (self::$debug) _egw_log_exception($e); + } + + $tpl = new etemplate_new('emailadmin.wizard.folder'); + $tpl->exec(static::APP_CLASS.'folder', $content, $sel_options, array(), $content); + } + + /** + * Query mailboxes and (optional) detect special folders + * + * @param Horde_Imap_Client_Socket $imap + * @param array &$content=null on return values for acc_folder_(sent|trash|draft|template) + * @return array with folders as key AND value + * @throws Horde_Imap_Client_Exception + */ + public static function mailboxes(Horde_Imap_Client_Socket $imap, array &$content=null) + { + // query all subscribed mailboxes + $mailboxes = $imap->listMailboxes('*', Horde_Imap_Client::MBOX_SUBSCRIBED, array( + 'special_use' => true, + 'attributes' => true, // otherwise special_use is only queried, but not returned ;-) + 'delimiter' => true, + )); + //_debug_array($mailboxes); + // list mailboxes by special-use attributes + $folders = $attributes = $all = array(); + foreach($mailboxes as $mailbox => $data) + { + foreach($data['attributes'] as $attribute) + { + $attributes[$attribute][] = $mailbox; + } + $folders[$mailbox] = $mailbox.': '.implode(', ', $data['attributes']); + } + // pre-select send, trash, ... folder for user, by checking special-use attributes or common name(s) + foreach(array( + 'acc_folder_sent' => array('\\sent', 'sent'), + 'acc_folder_trash' => array('\\trash', 'trash'), + 'acc_folder_draft' => array('\\drafts', 'drafts'), + 'acc_folder_template' => array('', 'templates'), + 'acc_folder_junk' => array('\\junk', 'junk', 'spam'), + ) as $name => $common_names) + { + // first check special-use attributes + if (($special_use = array_shift($common_names))) + { + foreach((array)$attributes[$special_use] as $mailbox) + { + if (empty($content[$name]) || strlen($mailbox) < strlen($content[$name])) + { + $content[$name] = $mailbox; + } + } + } + // no special use folder found, try common names + if (empty($content[$name])) + { + foreach($mailboxes as $mailbox => $data) + { + $delimiter = !empty($data['delimiter']) ? $data['delimiter'] : '.'; + $name_parts = explode($delimiter, strtolower($mailbox)); + if (array_intersect($name_parts, $common_names) && + (empty($content[$name]) || strlen($mailbox) < strlen($content[$name]) && substr($content[$name], 0, 6) != 'INBOX'.$delimiter)) + { + //error_log(__METHOD__."() $mailbox --> ".substr($name, 11).' folder'); + $content[$name] = $mailbox; + } + //else error_log(__METHOD__."() $mailbox does NOT match array_intersect(".array2string($name_parts).', '.array2string($common_names).')='.array2string(array_intersect($name_parts, $common_names))); + } + } + $folders[$content[$name]] .= ' --> '.substr($name, 11).' folder'; + } + // uncomment for infos about selection process + //$content['folder_output'] = implode("\n", $folders); + + return array_combine(array_keys($mailboxes), array_keys($mailboxes)); + } + + /** + * Step 3: Sieve + * + * @param array $content + * @param string $msg='' + */ + public function sieve(array $content, $msg='') + { + static $sieve_ssl2port = array( + self::SSL_TLS => 5190, + self::SSL_SSL => 5190, + self::SSL_STARTTLS => array(4190, 2000), + self::SSL_NONE => array(4190, 2000), + ); + $content['msg'] = $msg; + + if (isset($content['button'])) + { + list($button) = each($content['button']); + unset($content['button']); + switch($button) + { + case 'back': + return $this->folder($content); + + case 'continue': + if (!$content['acc_sieve_enabled']) + { + return $this->smtp($content); + } + break; + } + } + // first try: hide manual config + if (!isset($content['acc_sieve_enabled'])) + { + list(, $domain) = explode('@', $content['acc_imap_username']); + $content['acc_sieve_enabled'] = (int)!in_array($domain, self::$no_sieve_blacklist); + $content['manual_class'] = 'emailadmin_manual'; + } + else + { + unset($content['manual_class']); + $readonlys['button[manual]'] = true; + } + // set default ssl and port + if (!isset($content['acc_sieve_ssl'])) list($content['acc_sieve_ssl']) = each(self::$ssl_types); + if (empty($content['acc_sieve_port'])) $content['acc_sieve_port'] = $sieve_ssl2port[$content['acc_sieve_ssl']]; + + // check smtp connection + if ($button == 'continue') + { + $content['sieve_connected'] = false; + $content['sieve_output'] = ''; + unset($content['manual_class']); + + if (empty($content['acc_sieve_host'])) + { + $content['acc_sieve_host'] = $content['acc_imap_host']; + } + // if use set non-standard port, use it + if (!in_array($content['acc_sieve_port'], (array)$sieve_ssl2port[$content['acc_sieve_ssl']])) + { + $data = array($content['acc_sieve_ssl'] => $content['acc_sieve_port']); + } + else // otherwise try all standard ports + { + $data = $sieve_ssl2port; + } + foreach($data as $ssl => $ports) + { + foreach((array)$ports as $port) + { + $content['acc_sieve_ssl'] = $ssl; + $ssl_label = self::$ssl_types[$ssl]; + + $e = null; + try { + $content['sieve_output'] .= "\n".egw_time::to('now', 'H:i:s').": Trying $ssl_label connection to $content[acc_sieve_host]:$port ...\n"; + $content['acc_sieve_port'] = $port; + $useTLS = false; + $host = $content['acc_sieve_host']; + switch($content['acc_sieve_ssl']) + { + case self::SSL_SSL: + $host = 'ssl://'.$host; + break; + case self::SSL_TLS: + $host = 'tls://'.$host; + break; + case self::SSL_STARTTLS: + $useTLS = true; + } + + PEAR::setErrorHandling(PEAR_ERROR_EXCEPTION); + $sieve = new Net_Sieve(); + if (self::DEBUG_LOG) + { + $sieve->setDebug(true, function($sieve, $_msg) //use (&$content) + { + unset($sieve); // not used here + //$content['sieve_output'] .= "\n".$_msg; + if (($fp = fopen(self::DEBUG_LOG, 'a'))) + { + fwrite($fp, $_msg."\n"); + fclose($fp); + } + }); + } + // connect to sieve server + $sieve->connect($host, $port, $options=null, $useTLS); + $content['sieve_output'] .= "\n".lang('Successful connected to %1 server%2.', 'Sieve',''); + // and log in + $sieve->login($content['acc_imap_username'], $content['acc_imap_password']); + $content['sieve_output'] .= ' '.lang('and logged in')."\n"; + $content['sieve_connected'] = true; + + unset($content['button']); + return $this->smtp($content, lang('Successful connected to %1 server%2.', 'Sieve', + ' '.lang('and logged in'))); + } + // PEAR::setErrorHandling(PEAR_ERROR_EXCEPTION) throws just Exception + catch(Exception $e) { + switch($e->getCode()) + { + case 61: // connection refused + case 60: // connection timed out + case 65: // no route ot host (imap.googlemail.com returns that for ssl/5190) + $content['sieve_output'] .= "\n".$e->getMessage()."\n"; + break; + default: + $content['sieve_output'] .= "\n".$e->getMessage().' ('.$e->getCode().')'."\n"; + $content['sieve_output'] .= $e->getTraceAsString()."\n"; + break; + } + if (self::$debug) _egw_log_exception($e); + } + } + } + // not connected, and default ssl/port --> reset again to secure settings + if ($data == $sieve_ssl2port) + { + list($content['acc_sieve_ssl']) = each(self::$ssl_types); + $content['acc_sieve_port'] = $sieve_ssl2port[$content['acc_sieve_ssl']]; + } + } + // add validation error, if we can identify a field + if (!$content['sieve_connected'] && $e instanceof Exception) + { + switch($e->getCode()) + { + case 61: // connection refused + case 60: // connection timed out (imap.googlemail.com returns that for none-ssl/4190/2000) + case 65: // no route ot host (imap.googlemail.com returns that for ssl/5190) + etemplate_new::set_validation_error('acc_sieve_host', lang($e->getMessage())); + etemplate_new::set_validation_error('acc_sieve_port', lang($e->getMessage())); + break; + } + $content['msg'] = lang('No sieve support detected, either fix configuration manually or leave it switched off.'); + $content['acc_sieve_enabled'] = 0; + } + $sel_options['acc_sieve_ssl'] = self::$ssl_types; + $tpl = new etemplate_new('emailadmin.wizard.sieve'); + $tpl->exec(static::APP_CLASS.'sieve', $content, $sel_options, $readonlys, $content, 2); + } + + /** + * Step 4: SMTP + * + * @param array $content + * @param string $msg='' + */ + public function smtp(array $content, $msg='') + { + static $smtp_ssl2port = array( + self::SSL_NONE => 25, + self::SSL_SSL => 465, + self::SSL_TLS => 465, + self::SSL_STARTTLS => 587, + ); + $content['msg'] = $msg; + + if (isset($content['button'])) + { + list($button) = each($content['button']); + unset($content['button']); + switch($button) + { + case 'back': + return $this->sieve($content); + } + } + // first try: hide manual config + if (!isset($content['acc_smtp_host'])) + { + $content['manual_class'] = 'emailadmin_manual'; + } + else + { + unset($content['manual_class']); + $readonlys['button[manual]'] = true; + } + // copy username/password from imap + if (!isset($content['acc_smtp_username'])) $content['acc_smtp_username'] = $content['acc_imap_username']; + if (!isset($content['acc_smtp_password'])) $content['acc_smtp_password'] = $content['acc_imap_password']; + // set default ssl + if (!isset($content['acc_smtp_ssl'])) list($content['acc_smtp_ssl']) = each(self::$ssl_types); + if (empty($content['acc_smtp_port'])) $content['acc_smtp_port'] = $smtp_ssl2port[$content['acc_smtp_ssl']]; + + // check smtp connection + if ($button == 'continue') + { + $content['smtp_connected'] = false; + $content['smtp_output'] = ''; + unset($content['manual_class']); + + if (!empty($content['acc_smtp_host'])) + { + $hosts = array($content['acc_smtp_host'] => true); + if ((string)$content['acc_smtp_ssl'] !== (string)self::SSL_TLS || $content['acc_smtp_port'] != $smtp_ssl2port[$content['acc_smtp_ssl']]) + { + $ssl_type = (string)array_search($content['acc_smtp_ssl'], self::$ssl2type); + $hosts[$content['acc_smtp_host']] = array( + $ssl_type => $content['acc_smtp_port'], + ); + } + } + elseif($content['ispdb'] && !empty($content['ispdb']['smtp'])) + { + $content['smtp_output'] .= lang('Using data from Mozilla ISPDB for provider %1', $content['ispdb']['displayName'])."\n"; + $hosts = array(); + foreach($content['ispdb']['smtp'] as $server) + { + if (!isset($hosts[$server['hostname']])) + { + $hosts[$server['hostname']] = array('username' => $server['username']); + } + if (strtoupper($server['socketType']) == 'SSL') // try TLS first + { + $hosts[$server['hostname']]['TLS'] = $server['port']; + } + $hosts[$server['hostname']][strtoupper($server['socketType'])] = $server['port']; + // make sure we prefer SSL over STARTTLS over insecure + if (count($hosts[$server['hostname']]) > 2) + { + $hosts[$server['hostname']] = self::fix_ssl_order($hosts[$server['hostname']]); + } + } + } + else + { + $hosts = $this->guess_hosts($content['ident_email'], 'smtp'); + } + foreach($hosts as $host => $data) + { + $content['acc_smtp_host'] = $host; + if (!is_array($data)) + { + $data = array('TLS' => 465, 'SSL' => 465, 'STARTTLS' => 587, '' => 25); + } + foreach($data as $ssl => $port) + { + if ($ssl === 'username') continue; + + $content['acc_smtp_ssl'] = (int)self::$ssl2type[$ssl]; + + $e = null; + try { + $content['smtp_output'] .= "\n".egw_time::to('now', 'H:i:s').": Trying $ssl connection to $host:$port ...\n"; + $content['acc_smtp_port'] = $port; + + $mail = new Horde_Mail_Transport_Smtphorde($params=array( + 'username' => $content['acc_smtp_username'], + 'password' => $content['acc_smtp_password'], + 'host' => $content['acc_smtp_host'], + 'port' => $content['acc_smtp_port'], + 'secure' => self::$ssl2secure[(string)array_search($content['acc_smtp_ssl'], self::$ssl2type)], + 'timeout' => self::TIMEOUT, + 'debug' => self::DEBUG_LOG, + )); + // create smtp connection and authenticate, if credentials given + $smtp = $mail->getSMTPObject(); + $content['smtp_output'] .= "\n".lang('Successful connected to %1 server%2.', 'SMTP', + (!empty($content['acc_smtp_username']) ? ' '.lang('and logged in') : ''))."\n"; + if (!$smtp->isSecureConnection()) + { + if (!empty($content['acc_smtp_username'])) + { + $content['smtp_output'] .= lang('Connection is NOT secure! Everyone can read eg. your credentials.')."\n"; + } + $content['acc_smtp_ssl'] = 'no'; + } + // Horde_Smtp always try to use STARTTLS, adjust our ssl-parameter if successful + elseif (!($content['acc_smtp_ssl'] > self::SSL_NONE)) + { + //error_log(__METHOD__."() new Horde_Mail_Transport_Smtphorde(".array2string($params).")->getSMTPObject()->isSecureConnection()=".array2string($smtp->isSecureConnection())); + $content['acc_smtp_ssl'] = self::SSL_STARTTLS; + } + // try sending a mail to a different domain, if not authenticated, to see if that's required + if (empty($content['acc_smtp_username'])) + { + $smtp->send($content['ident_email'], 'noreply@example.com', ''); + $content['smtp_output'] .= "\n".lang('Relay access checked')."\n"; + } + $content['smtp_connected'] = true; + unset($content['button']); + return $this->edit($content, lang('Successful connected to %1 server%2.', 'SMTP', + empty($content['acc_smtp_username']) ? ' - '.lang('Relay access checked') : ' '.lang('and logged in'))); + } + // unfortunately LOGIN_AUTHENTICATIONFAILED and SERVER_CONNECT are thrown as Horde_Mail_Exception + // while others are thrown as Horde_Smtp_Exception --> using common base Horde_Exception_Wrapped + catch(Horde_Exception_Wrapped $e) + { + switch($e->getCode()) + { + case Horde_Smtp_Exception::LOGIN_AUTHENTICATIONFAILED: + case Horde_Smtp_Exception::LOGIN_REQUIREAUTHENTICATION: + case Horde_Smtp_Exception::UNSPECIFIED: + $content['smtp_output'] .= "\n".$e->getMessage()."\n"; + break; + case Horde_Smtp_Exception::SERVER_CONNECT: + $content['smtp_output'] .= "\n".$e->getMessage()."\n"; + break; + default: + $content['smtp_output'] .= "\n".$e->getMessage().' ('.$e->getCode().')'."\n"; + break; + } + if (self::$debug) _egw_log_exception($e); + } + catch(Horde_Smtp_Exception $e) + { + // prever $e->details over $e->getMessage() as it contains original message from SMTP server (eg. relay access denied) + $content['smtp_output'] .= "\n".(empty($e->details) ? $e->getMessage().' ('.$e->getCode().')' : $e->details)."\n"; + //$content['smtp_output'] .= $e->getTraceAsString()."\n"; + if (self::$debug) _egw_log_exception($e); + } + catch(Exception $e) { + $content['smtp_output'] .= "\n".get_class($e).': '.$e->getMessage().' ('.$e->getCode().')'."\n"; + //$content['smtp_output'] .= $e->getTraceAsString()."\n"; + if (self::$debug) _egw_log_exception($e); + } + } + } + } + // add validation error, if we can identify a field + if (!$content['smtp_connected'] && $e instanceof Horde_Exception_Wrapped) + { + switch($e->getCode()) + { + case Horde_Smtp_Exception::LOGIN_AUTHENTICATIONFAILED: + case Horde_Smtp_Exception::LOGIN_REQUIREAUTHENTICATION: + case Horde_Smtp_Exception::UNSPECIFIED: + etemplate_new::set_validation_error('acc_smtp_username', lang($e->getMessage())); + etemplate_new::set_validation_error('acc_smtp_password', lang($e->getMessage())); + break; + + case Horde_Smtp_Exception::SERVER_CONNECT: + etemplate_new::set_validation_error('acc_smtp_host', lang($e->getMessage())); + etemplate_new::set_validation_error('acc_smtp_port', lang($e->getMessage())); + break; + } + } + $sel_options['acc_smtp_ssl'] = self::$ssl_types; + $tpl = new etemplate_new('emailadmin.wizard.smtp'); + $tpl->exec(static::APP_CLASS.'smtp', $content, $sel_options, $readonlys, $content, 2); + } + + /** + * Edit mail account(s) + * + * Gets either called with GET parameter: + * + * a) account_id from admin >> Manage users to edit / add mail accounts for a user + * --> shows selectbox to switch between different mail accounts of user and "create new account" + * + * b) via mail_wizard proxy class by regular mail user to edit (acc_id GET parameter) or create new mail account + * + * @param array $content=null + * @param string $msg='' + * @param string $msg_type='success' + */ + public function edit(array $content=null, $msg='', $msg_type='success') + { + // app is trying to tell something, while redirecting to wizard + if (empty($content) && $_GET['acc_id'] && empty($msg) && !empty( $_GET['msg'])) + { + if (stripos($_GET['msg'],'fatal error:')!==false) $msg_type = 'error'; + } + if ($content['acc_id'] || (isset($_GET['acc_id']) && (int)$_GET['acc_id'] > 0) ) emailadmin_imapbase::unsetCachedObjects($content['acc_id']?$content['acc_id']:$_GET['acc_id']); + $tpl = new etemplate_new('emailadmin.account'); + + if (!is_array($content) || !empty($content['acc_id']) && $content['acc_id'] != $content['old_acc_id']) + { + if (!is_array($content)) $content = array(); + if ($this->is_admin && isset($_GET['account_id'])) + { + $content['called_for'] = (int)$_GET['account_id']; + $content['accounts'] = iterator_to_array(emailadmin_account::search($content['called_for'])); + if ($content['accounts']) + { + list($content['acc_id']) = each($content['accounts']); + } + if (!$content['accounts']) // no email account, call wizard + { + return $this->add(array('account_id' => (int)$_GET['account_id'])); + } + $content['accounts']['new'] = lang('Create new account'); + } + if (isset($_GET['acc_id']) && (int)$_GET['acc_id'] > 0) + { + $content['acc_id'] = (int)$_GET['acc_id']; + } + // clear current account-data, as account has changed and we going to read selected one + $content = array_intersect_key($content, array_flip(array('called_for', 'accounts', 'acc_id', 'tabs'))); + + if ($content['acc_id'] > 0) + { + try { + $account = emailadmin_account::read($content['acc_id'], $this->is_admin && $content['called_for'] ? + $content['called_for'] : $GLOBALS['egw_info']['user']['account_id']); + $account->getUserData(); // quota, aliases, forwards etc. + $content += $account->params; + $content['acc_sieve_enabled'] = (string)($content['acc_sieve_enabled']); + $content['notify_use_default'] = !$content['notify_account_id']; + self::fix_account_id_0($content['account_id']); + + // read identities (of current user) and mark std identity + $content['identities'] = iterator_to_array($account->identities()); + $content['std_ident_id'] = $content['ident_id']; + $content['identities'][$content['std_ident_id']] = lang('Standard identity'); + // change self::SSL_NONE (=0) to "no" used in sel_options + foreach(array('imap','smtp','sieve') as $type) + { + if (!$content['acc_'.$type.'_ssl']) $content['acc_'.$type.'_ssl'] = 'no'; + } + } + catch(egw_exception_not_found $e) { + if (self::$debug) _egw_log_exception($e); + egw_framework::window_close(lang('Account not found!')); + } + catch(Exception $e) { + if (self::$debug) _egw_log_exception($e); + egw_framework::window_close($e->getMessage().' ('.get_class($e).': '.$e->getCode().')'); + } + } + elseif ($content['acc_id'] === 'new') + { + $content['account_id'] = $content['called_for']; + $content['old_acc_id'] = $content['acc_id']; // to not call add/wizard, if we return from to + unset($content['tabs']); + return $this->add($content); + } + } + // some defaults for new accounts + if (!isset($content['account_id']) || empty($content['acc_id']) || $content['acc_id'] === 'new') + { + if (!isset($content['account_id'])) $content['account_id'] = array($GLOBALS['egw_info']['user']['account_id']); + $content['acc_user_editable'] = $content['acc_further_identities'] = true; + $readonlys['ident_id'] = true; // need to create standard identity first + } + if (empty($content['acc_name'])) + { + $content['acc_name'] = $content['ident_email']; + } + // disable some stuff for non-emailadmins (all values are preserved!) + if (!$this->is_admin) + { + $readonlys = array( + 'account_id' => true, 'button[multiple]' => true, 'acc_user_editable' => true, + 'acc_further_identities' => true, + 'acc_imap_type' => true, 'acc_imap_logintype' => true, 'acc_domain' => true, + 'acc_imap_admin_username' => true, 'acc_imap_admin_password' => true, + 'acc_smtp_type' => true, 'acc_smtp_auth_session' => true, + ); + } + // ensure correct values for single user mail accounts (we only hide them client-side) + if (!($is_multiple = emailadmin_account::is_multiple($content))) + { + $content['acc_imap_type'] = 'emailadmin_imap'; + unset($content['acc_imap_login_type']); + $content['acc_smtp_type'] = 'emailadmin_smtp'; + unset($content['acc_smtp_auth_session']); + } + $edit_access = emailadmin_account::check_access(EGW_ACL_EDIT, $content); + + // disable notification save-default and use-default, if only one account or no edit-rights + $tpl->disableElement('notify_save_default', !$is_multiple || !$edit_access); + $tpl->disableElement('notify_use_default', !$is_multiple); + + if (isset($content['button'])) + { + list($button) = each($content['button']); + unset($content['button']); + switch($button) + { + case 'wizard': + // if we just came from wizard, go back to last page/step + if (isset($content['smtp_connected'])) + { + return $this->smtp($content); + } + // otherwise start with first step + return $this->autoconfig($content); + + case 'delete_identity': + // delete none-standard identity of current user + if ($content['acc_further_identities'] && $content['ident_id'] > 0 && + $content['std_ident_id'] != $content['ident_id']) + { + emailadmin_account::delete_identity($content['ident_id']); + $msg = lang('Identity deleted'); + unset($content['identities'][$content['ident_id']]); + $content['ident_id'] = $content['std_ident_id']; + } + break; + + case 'save': + case 'apply': + try { + // if admin username/password given, check if it is valid + $account = new emailadmin_account($content); + $imap = $account->imapServer(); + if ($imap) $imap->checkAdminConnection(); + // save none-standard identity for current user + if ($content['acc_id'] && $content['acc_id'] !== 'new' && + $content['acc_further_identities'] && + $content['std_ident_id'] != $content['ident_id']) + { + $content['ident_id'] = emailadmin_account::save_identity(array( + 'account_id' => $GLOBALS['egw_info']['user']['account_id'], + )+$content); + $content['identities'][$content['ident_id']] = emailadmin_account::identity_name($content); + $msg = lang('Identity saved.'); + if ($edit_access) $msg .= ' '.lang('Switch back to standard identity to save account.'); + } + elseif ($edit_access) + { + $new_account = !($content['acc_id'] > 0); + // check for deliveryMode="forwardOnly", if a forwarding-address is given + if ($content['acc_smtp_type'] != 'emailadmin_smtp' && + $content['deliveryMode'] == emailadmin_smtp::FORWARD_ONLY && + empty($content['mailForwardingAddress'])) + { + etemplate_new::set_validation_error('mailForwardingAddress', lang('Field must not be empty !!!')); + throw new egw_exception_wrong_userinput(lang('You need to specify a forwarding address, when checking "%1"!', lang('Forward only'))); + } + // set notifications to store according to checkboxes + if ($content['notify_save_default']) + { + $content['notify_account_id'] = 0; + } + elseif (!$content['notify_use_default']) + { + $content['notify_account_id'] = $content['called_for'] ? + $content['called_for'] : $GLOBALS['egw_info']['user']['account_id']; + } + self::fix_account_id_0($content['account_id'], true); + $content = emailadmin_account::write($content, $content['called_for'] || !$this->is_admin ? + $content['called_for'] : $GLOBALS['egw_info']['user']['account_id']); + self::fix_account_id_0($content['account_id']); + $msg = lang('Account saved.'); + // user wants default notifications + if ($content['acc_id'] && $content['notify_use_default']) + { + // delete own ones + emailadmin_notifications::delete($content['acc_id'], $content['called_for'] ? + $content['called_for'] : $GLOBALS['egw_info']['user']['account_id']); + // load default ones + $content = array_merge($content, emailadmin_notifications::read($content['acc_id'], 0)); + } + // add new std identity entry + if ($new_account) + { + $content['std_ident_id'] = $content['ident_id']; + $content['identities'] = array( + $content['std_ident_id'] => lang('Standard identity')); + } + if (isset($content['accounts'])) + { + if (!isset($content['accounts'][$content['acc_id']])) // insert new account as top, not bottom + { + $content['accounts'] = array($content['acc_id'] => '') + $content['accounts']; + } + $content['accounts'][$content['acc_id']] = emailadmin_account::identity_name($content, false); + } + } + else + { + if ($content['notify_use_default'] && $content['notify_account_id']) + { + // delete own ones + if (emailadmin_notifications::delete($content['acc_id'], $content['called_for'] ? + $content['called_for'] : $GLOBALS['egw_info']['user']['account_id'])) + { + $msg = lang('Notification folders updated.'); + } + // load default ones + $content = array_merge($content, emailadmin_notifications::read($content['acc_id'], 0)); + } + if (!$content['notify_use_default']) + { + $content['notify_account_id'] = $content['called_for'] ? + $content['called_for'] : $GLOBALS['egw_info']['user']['account_id']; + if (emailadmin_notifications::write($content['acc_id'], $content['notify_account_id'], + $content['notify_folders'])) + { + $msg = lang('Notification folders updated.'); + } + } + } + } + catch (Horde_Imap_Client_Exception $e) + { + _egw_log_exception($e); + $tpl->set_validation_error('acc_imap_admin_username', $msg=lang($e->getMessage())); + $msg_type = 'error'; + $content['tabs'] = 'emailadmin.account.imap'; // should happen automatic + break; + } + catch (Exception $e) { + $msg = lang('Error saving account!')."\n".$e->getMessage(); + $button = 'apply'; + $msg_type = 'error'; + } + if ($content['acc_id']) emailadmin_imapbase::unsetCachedObjects($content['acc_id']); + if (stripos($msg,'fatal error:')!==false) $msg_type = 'error'; + egw_framework::refresh_opener($msg, 'emailadmin', $content['acc_id'], $new_account ? 'add' : 'update', null, null, null, $msg_type); + if ($button == 'save') egw_framework::window_close(); + break; + + case 'delete': + if (!emailadmin_account::check_access(EGW_ACL_DELETE, $content)) + { + $msg = lang('Permission denied!'); + $msg_type = 'error'; + } + elseif (emailadmin_account::delete($content['acc_id']) > 0) + { + if ($content['acc_id']) emailadmin_imapbase::unsetCachedObjects($content['acc_id']); + egw_framework::refresh_opener(lang('Account deleted.'), 'emailadmin', $content['acc_id'], 'delete'); + egw_framework::window_close(); + } + else + { + $msg = lang('Failed to delete account!'); + $msg_type = 'error'; + } + } + } + + // disable delete button for new, not yet saved entries, if no delete rights or a non-standard identity selected + $readonlys['button[delete]'] = empty($content['acc_id']) || + !emailadmin_account::check_access(EGW_ACL_DELETE, $content) || + $content['ident_id'] != $content['std_ident_id']; + + // if account is for multiple user, change delete confirmation to reflect that + if (emailadmin_account::is_multiple($content)) + { + $tpl->setElementAttribute('button[delete]', 'onclick', "et2_dialog.confirm(widget,'This is NOT a personal mail account!\\n\\nAccount will be deleted for ALL users!\\n\\nAre you really sure you want to do that?','Delete this account')"); + } + + // if no edit access, make whole dialog readonly + if (!$edit_access) + { + $readonlys['__ALL__'] = true; + $readonlys['button[cancel]'] = false; + // allow to edit notification-folders + $readonlys['button[save]'] = $readonlys['button[apply]'] = + $readonlys['notify_folders'] = $readonlys['notify_use_default'] = false; + } + + $sel_options['acc_imap_ssl'] = $sel_options['acc_sieve_ssl'] = + $sel_options['acc_smtp_ssl'] = self::$ssl_types; + + // admin access to account with no credentials available + if ($this->is_admin && (empty($content['acc_imap_username']) || empty($content['acc_imap_host']))) + { + // cant connection to imap --> allow free entries in taglists + foreach(array('acc_folder_sent', 'acc_folder_trash', 'acc_folder_draft', 'acc_folder_template', 'acc_folder_junk') as $folder) + { + $tpl->setElementAttribute($folder, 'allowFreeEntries', true); + } + } + else + { + try { + $sel_options['acc_folder_sent'] = $sel_options['acc_folder_trash'] = + $sel_options['acc_folder_draft'] = $sel_options['acc_folder_template'] = + $sel_options['acc_folder_junk'] = $sel_options['notify_folders'] = + self::mailboxes(self::imap_client ($content)); + } + // call wizard, if we have a connection error: Horde_Imap_Client_Exception + catch(Horde_Imap_Client_Exception $e) { + _egw_log_exception($e); + // if we are not comming from wizard --> try it + if (!$content['output']) + { + return $this->add($content, $e->getMessage(), 'error'); + } + // we already been in wizard, wont get better, let admin try fixing it + egw_framework::message($e->getMessage(), 'error'); + // cant connection to imap --> allow free entries in taglists + foreach(array('acc_folder_sent', 'acc_folder_trash', 'acc_folder_draft', 'acc_folder_template') as $folder) + { + $tpl->setElementAttribute($folder, 'allowFreeEntries', true); + } + } + // call wizard, if we have missing credentials: InvalidArgumentException + catch(InvalidArgumentException $e) { + _egw_log_exception($e); + return $this->add($content, $e->getMessage()); + } + // and for the rest also ... + catch(Exception $e) { + _egw_log_exception($e); + return $this->add($content, $e->getMessage().' ('.get_class($e).': '.$e->getCode().')'); + } + } + $sel_options['acc_imap_type'] = emailadmin_base::getIMAPServerTypes(false); + $sel_options['acc_smtp_type'] = emailadmin_base::getSMTPServerTypes(false); + $sel_options['acc_imap_logintype'] = self::$login_types; + $sel_options['ident_id'] = $content['identities']; + $sel_options['acc_id'] = $content['accounts']; + + // user is allowed to create or edit further identities + if ($edit_access || $content['acc_further_identities']) + { + $sel_options['ident_id']['new'] = lang('Create new identity'); + $readonlys['ident_id'] = false; + + // if no edit-access and identity is not standard identity --> allow to edit identity + if (!$edit_access && $content['ident_id'] != $content['std_ident_id']) + { + $readonlys += array( + 'button[save]' => false, 'button[apply]' => false, + 'button[placeholders]' => false, + 'ident_name' => false, + 'ident_realname' => false, 'ident_email' => false, + 'ident_org' => false, 'ident_signature' => false, + ); + } + if ($content['ident_id'] != $content['old_ident_id'] && + ($content['old_ident_id'] || $content['ident_id'] != $content['std_ident_id'])) + { + if ($content['ident_id'] > 0) + { + $identity = emailadmin_account::read_identity($content['ident_id']); + unset($identity['account_id']); + $content = array_merge($content, $identity); + } + else + { + $content['ident_name'] = $content['ident_realname'] = $content['ident_email'] = + $content['ident_org'] = $content['ident_signature'] = ''; + } + if (empty($msg) && $edit_access && $content['ident_id'] && $content['ident_id'] != $content['std_ident_id']) + { + $msg = lang('Switch back to standard identity to save other account data.'); + $msg_type = 'help'; + } + $content['old_ident_id'] = $content['ident_id']; + } + } + $content['old_acc_id'] = $content['acc_id']; + + // only allow to delete further identities, not a standard identity + $readonlys['button[delete_identity]'] = !($content['ident_id'] > 0 && $content['ident_id'] != $content['std_ident_id']); + + // disable aliases tab for default smtp class emailadmin_smtp + $readonlys['tabs']['emailadmin.account.aliases'] = !$content['acc_smtp_type'] || + $content['acc_smtp_type'] == 'emailadmin_smtp'; + + // allow imap classes to disable certain tabs or fields + if (($class = emailadmin_account::getIcClass($content['acc_imap_type'])) && class_exists($class) && + ($imap_ro = call_user_func(array($class, 'getUIreadonlys')))) + { + $readonlys = array_merge($readonlys, $imap_ro, array( + 'tabs' => array_merge((array)$readonlys['tabs'], (array)$imap_ro['tabs']), + )); + } + egw_framework::message($msg ? $msg : (string)$_GET['msg'], $msg_type); + + if (count($content['account_id']) > 1) + { + $tpl->setElementAttribute('account_id', 'multiple', true); + $readonlys['button[multiple]'] = true; + } + $tpl->exec(static::APP_CLASS.'edit', $content, $sel_options, $readonlys, $content, 2); + } + + /** + * Replace 0 with '' or back + * + * @param string|array &$account_id on return always array + * @param boolean $back=false + */ + private static function fix_account_id_0(&$account_id=null, $back=false) + { + if (!isset($account_id)) return; + + if (!is_array($account_id)) + { + $account_id = explode(',', $account_id); + } + if (($k = array_search($back?'':'0', $account_id)) !== false) + { + $account_id[$k] = $back ? '0' : ''; + } + } + + /** + * Instanciate imap-client + * + * @param array $content + * @param int $timeout=null default use value returned by emailadmin_imap::getTimeOut() + * @return Horde_Imap_Client_Socket + */ + protected static function imap_client(array $content, $timeout=null) + { + return new Horde_Imap_Client_Socket(array( + 'username' => $content['acc_imap_username'], + 'password' => $content['acc_imap_password'], + 'hostspec' => $content['acc_imap_host'], + 'port' => $content['acc_imap_port'], + 'secure' => self::$ssl2secure[(string)array_search($content['acc_imap_ssl'], self::$ssl2type)], + 'timeout' => $timeout > 0 ? $timeout : emailadmin_imap::getTimeOut(), + 'debug' => self::DEBUG_LOG, + )); + } + + /** + * Reorder SSL types to make sure we start with TLS, SSL, STARTTLS and insecure last + * + * @param array $data ssl => port pairs plus other data like value for 'username' + * @return array + */ + protected static function fix_ssl_order($data) + { + $ordered = array(); + foreach(array_merge(array('TLS', 'SSL', 'STARTTLS'), array_keys($data)) as $key) + { + if (array_key_exists($key, $data)) $ordered[$key] = $data[$key]; + } + return $ordered; + } + + /** + * Query Mozilla's ISPDB + * + * Some providers eg. 1-and-1 do not report their hosted domains to ISPDB, + * therefore we try it with the found MX and it's domain-part (host-name removed). + * + * @param string $domain domain or email + * @param boolean $try_mx=true if domain itself is not found, try mx or domain-part (host removed) of mx + * @return array with values for keys 'displayName', 'imap', 'smtp', 'pop3', which each contain + * array of arrays with values for keys 'hostname', 'port', 'socketType'=(SSL|STARTTLS), 'username'=%EMAILADDRESS% + */ + protected static function mozilla_ispdb($domain, $try_mx=true) + { + if (strpos($domain, '@') !== false) list(,$domain) = explode('@', $domain); + + $url = 'https://autoconfig.thunderbird.net/v1.1/'.$domain; + try { + $xml = @simplexml_load_file($url); + if (!$xml->emailProvider) throw new egw_exception_not_found(); + $provider = array( + 'displayName' => (string)$xml->emailProvider->displayName, + ); + foreach($xml->emailProvider->children() as $tag => $server) + { + if (!in_array($tag, array('incomingServer', 'outgoingServer'))) continue; + foreach($server->attributes() as $name => $value) + { + if ($name == 'type') $type = (string)$value; + } + $data = array(); + foreach($server as $name => $value) + { + foreach($value->children() as $tag => $val) + { + $data[$name][$tag] = (string)$val; + } + if (!isset($data[$name])) $data[$name] = (string)$value; + } + $provider[$type][] = $data; + } + } + catch(Exception $e) { + // ignore own not-found exception or xml parsing execptions + unset($e); + + if ($try_mx && ($dns = dns_get_record($domain, DNS_MX))) + { + $domain = $dns[0]['target']; + if (!($provider = self::mozilla_ispdb($domain, false))) + { + list(,$domain) = explode('.', $domain, 2); + $provider = self::mozilla_ispdb($domain, false); + } + } + else + { + $provider = array(); + } + } + //error_log(__METHOD__."('$email') returning ".array2string($provider)); + return $provider; + } + + /** + * Guess possible server hostnames from email address: + * - $type.$domain, mail.$domain + * - replace host in MX with imap or mail + * - MX for $domain + * + * @param string $email email address + * @param string $type='imap' 'imap' or 'smtp', used as hostname beside 'mail' + * @return array of hostname => true pairs + */ + protected function guess_hosts($email, $type='imap') + { + list(,$domain) = explode('@', $email); + + $hosts = array(); + + // try usuall names + $hosts[$type.'.'.$domain] = true; + $hosts['mail.'.$domain] = true; + if ($type == 'smtp') $hosts['send.'.$domain] = true; + + if (($dns = dns_get_record($domain, DNS_MX))) + { + //error_log(__METHOD__."('$email') dns_get_record('$domain', DNS_MX) returned ".array2string($dns)); + $hosts[preg_replace('/^[^.]+/', $type, $dns[0]['target'])] = true; + $hosts[preg_replace('/^[^.]+/', 'mail', $dns[0]['target'])] = true; + if ($type == 'smtp') $hosts[preg_replace('/^[^.]+/', 'send', $dns[0]['target'])] = true; + $hosts[$dns[0]['target']] = true; + } + + // verify hosts in dns + foreach(array_keys($hosts) as $host) + { + if (!dns_get_record($host, DNS_A)) unset($hosts[$host]); + } + //error_log(__METHOD__."('$email') returning ".array2string($hosts)); + return $hosts; + } +} diff --git a/emailadmin/inc/class.postfixdbmailuser.inc.php b/emailadmin/inc/class.postfixdbmailuser.inc.php new file mode 100755 index 0000000000..c2a40a2d94 --- /dev/null +++ b/emailadmin/inc/class.postfixdbmailuser.inc.php @@ -0,0 +1,64 @@ + + * @copyright (c) 2010-13 by Ralf Becker + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @version $Id$ + */ + +/** + * Postfix with dbmailUser schema + */ +class postfixdbmailuser extends emailadmin_smtp_ldap +//class emailadmin_smtp_dbmailuser extends emailadmin_smtp_ldap +{ + /** + * Capabilities of this class (pipe-separated): default, forward + */ + const CAPABILITIES = 'default|forward'; + + /** + * Name of schema, has to be the correct case! + */ + const SCHEMA = 'dbmailUser'; + + /** + * Attribute to enable mail for an account, OR false if existence of ALIAS_ATTR is enough for mail delivery + */ + const MAIL_ENABLE_ATTR = 'accountstatus'; + /** + * Attribute value to enable mail for an account, OR false if existense of attribute is enough to enable account + */ + const MAIL_ENABLED = 'active'; + + /** + * Attribute for aliases OR false to use mail + */ + const ALIAS_ATTR = 'mailalternateaddress'; + + /** + * Primary mail address required as an alias too: true or false + */ + const REQUIRE_MAIL_AS_ALIAS=false; + + /** + * Attribute for forwards OR false if not possible + */ + const FORWARD_ATTR = 'mailforwardingaddress'; + + /** + * Attribute to only forward mail, OR false if not available + */ + const FORWARD_ONLY_ATTR = 'deliverymode'; + + /** + * Attribute for mailbox, to which mail gets delivered OR false if not supported + */ + //const MAILBOX_ATTR = 'deliveryprogrampath'; + //const MAILBOX_ATTR = 'dbmailuid'; + const MAILBOX_ATTR = false; +} diff --git a/emailadmin/inc/class.postfixinetorgperson.inc.php b/emailadmin/inc/class.postfixinetorgperson.inc.php new file mode 100644 index 0000000000..39f66f2dd0 --- /dev/null +++ b/emailadmin/inc/class.postfixinetorgperson.inc.php @@ -0,0 +1,16 @@ + + * @copyright (c) 2010-13 by Ralf Becker + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @version $Id$ + */ + +/** + * Postfix with old qmailUser schema + */ +class postfixldap extends emailadmin_smtp_ldap +//class emailadmin_smtp_qmailuser extends emailadmin_smtp_ldap +{ + /** + * Capabilities of this class (pipe-separated): default, forward + */ + const CAPABILITIES = 'default|forward'; + + /** + * Name of schema, has to be in the right case! + */ + const SCHEMA = 'qmailUser'; + + /** + * Attribute to enable mail for an account, OR false if existence of ALIAS_ATTR is enough for mail delivery + */ + const MAIL_ENABLE_ATTR = 'accountstatus'; + /** + * Attribute value to enable mail for an account, OR false if existense of attribute is enough to enable account + */ + const MAIL_ENABLED = 'active'; + + /** + * Attribute for aliases OR false to use mail + */ + const ALIAS_ATTR = 'mailalternateaddress'; + + /** + * Primary mail address required as an alias too: true or false + */ + const REQUIRE_MAIL_AS_ALIAS=false; + + /** + * Attribute for forwards OR false if not possible + */ + const FORWARD_ATTR = 'mailforwardingaddress'; + + /** + * Attribute to only forward mail, OR false if not available + */ + const FORWARD_ONLY_ATTR = 'deliverymode'; + + /** + * Attribute for mailbox, to which mail gets delivered OR false if not supported + */ + const MAILBOX_ATTR = 'mailmessagestore'; + + /** + * Attribute for quota limit of user in MB + */ + const QUOTA_ATTR = 'mailquota'; +} diff --git a/emailadmin/index.php b/emailadmin/index.php new file mode 100644 index 0000000000..ca0c5b8181 --- /dev/null +++ b/emailadmin/index.php @@ -0,0 +1,13 @@ + + * @copyright (c) 2009-10 by Klaus Leithoff + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @version $Id$ + \***************************************************************************/ + +header('Location: ../index.php?menuaction=emailadmin.emailadmin_ui.index'. + (isset($_GET['sessionid']) ? '&sessionid='.$_GET['sessionid'].'&kp3='.$_GET['kp3'] : '')); diff --git a/emailadmin/js/app.js b/emailadmin/js/app.js new file mode 100644 index 0000000000..d5a1fdada5 --- /dev/null +++ b/emailadmin/js/app.js @@ -0,0 +1,318 @@ +/** + * EGroupware emailadmin static javascript code + * + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @package emailadmin + * @link http://www.egroupware.org + * @author Klaus Leithoff + * @author Ralf Becker + * @version $Id$ + */ + +/** + * UI for emailadmin + * + * @augments AppJS + */ +app.classes.emailadmin = AppJS.extend( +{ + appname: 'emailadmin', + /** + * No SSL + */ + SSL_NONE: 0, + /** + * STARTTLS on regular tcp connection/port + */ + SSL_STARTTLS: 1, + /** + * SSL (inferior to TLS!) + */ + SSL_SSL: 3, + /** + * require TLS version 1+, no SSL version 2 or 3 + */ + SSL_TLS: 2, + /** + * if set, verify certifcate (currently not implemented in Horde_Imap_Client!) + */ + SSL_VERIFY: 8, + + /** + * Constructor + * + * @memberOf app.emailadmin + */ + init: function() + { + // call parent + this._super.apply(this, arguments); + }, + + /** + * Destructor + */ + destroy: function() + { + // call parent + this._super.apply(this, arguments); + }, + + /** + * This function is called when the etemplate2 object is loaded + * and ready. If you must store a reference to the et2 object, + * make sure to clean it up in destroy(). + * + * @param et2 etemplate2 Newly ready object + */ + et2_ready: function(et2) + { + // call parent + this._super.apply(this, arguments); + + for (var t in et2.templates) + { + //alert(t); // as we iterate through this more than once, ... we separate trigger and action + switch (t) + { + case 'emailadmin.account': + this.account_hide_not_applying(); + break; + } + } + }, + + /** + * Switch account wizard to manual entry + */ + wizard_manual: function() + { + jQuery('.emailadmin_manual').fadeToggle();// not sure how to to this et2-isch + }, + + /** + * onclick for continue button to show progress animation + * + * @param {object} _event event-object or information about event + * @param {et2_baseWidget} _widget widget causing the event + */ + wizard_detect: function(_event, _widget) + { + // we need to do a manual asynchronious submit to show progress animation + // default synchronious submit stops animation! + if (this.et2._inst.submit('button[continue]', true)) // true = async submit + { + var sieve_enabled = this.et2.getWidgetById('acc_sieve_enabled'); + if (!sieve_enabled || sieve_enabled.get_value()) + { + jQuery('td.emailadmin_progress').show(); + } + } + return false; + }, + + /** + * Set default port, if imap ssl-type changes + * + * @param {object} _event event-object or information about event + * @param {et2_baseWidget} _widget widget causing the event + */ + wizard_imap_ssl_onchange: function(_event, _widget) + { + var ssl_type = _widget.get_value(); + this.et2.getWidgetById('acc_imap_port').set_value( + ssl_type == this.SSL_SSL || ssl_type == this.SSL_TLS ? 993 : 143); + }, + + /** + * Set default port, if imap ssl-type changes + * + * @param {object} _event event-object or information about event + * @param {et2_baseWidget} _widget widget causing the event + */ + wizard_smtp_ssl_onchange: function(_event, _widget) + { + var ssl_type = _widget.get_value(); + this.et2.getWidgetById('acc_smtp_port').set_value( + ssl_type == 'no' ? 25 : (ssl_type == this.SSL_SSL || ssl_type == this.SSL_TLS ? 465 : 587)); + }, + + /** + * Set default port, if imap ssl-type changes + * + * @param {object} _event event-object or information about event + * @param {et2_baseWidget} _widget widget causing the event + */ + wizard_sieve_ssl_onchange: function(_event, _widget) + { + var ssl_type = _widget.get_value(); + this.et2.getWidgetById('acc_sieve_port').set_value( + ssl_type == this.SSL_SSL || ssl_type == this.SSL_TLS ? 5190 : 4190); + this.wizard_sieve_onchange(_event, _widget); + }, + + /** + * Enable sieve, if user changes some setting + * + * @param {object} _event event-object or information about event + * @param {et2_baseWidget} _widget widget causing the event + */ + wizard_sieve_onchange: function(_event, _widget) + { + this.et2.getWidgetById('acc_sieve_enabled').set_value(1); + }, + + /** + * Switch to select multiple accounts + * + * @param {object} _event event-object or information about event + * @param {et2_baseWidget} _widget widget causing the event + */ + edit_multiple: function(_event, _widget) + { + // hide multiple button + _widget.set_disabled(true); + + // switch account-selection to multiple + var account_id = this.et2.getWidgetById('account_id'); + account_id.set_multiple(true); + }, + + /** + * Hide not applying fields, used as: + * - onchange handler on account_id + * - called from et2_ready for emailadmin.account template + * + * @param {object} _event event-object or information about event + * @param {et2_baseWidget} _widget widget causing the event + */ + account_hide_not_applying: function(_event, _widget) + { + var account_id = this.et2.getWidgetById('account_id'); + var ids = account_id && account_id.get_value ? account_id.get_value() : []; + if (typeof ids == 'string') ids = ids.split(','); + + var multiple = ids.length >= 2 || ids[0] === '' || ids[0] < 0; + //alert('multiple='+(multiple?'true':'false')+': '+ids.join(',')); + + // initial call + if (typeof _widget == 'undefined') + { + if (!multiple) + { + jQuery('.emailadmin_no_single').hide(); + } + if (!this.egw.user('apps').emailadmin) + { + jQuery('.emailadmin_no_user,#button\\[multiple\\]').hide(); + } + if (ids.length == 1) + { + // switch back to single selectbox + account_id.set_multiple(false); + this.et2.getWidgetById('button[multiple]').set_disabled(false); + } + } + // switched to single user + else if (!multiple) + { + jQuery('.emailadmin_no_single').fadeOut(); + // switch back to single selectbox + account_id.set_multiple(false); + this.et2.getWidgetById('button[multiple]').set_disabled(false); + } + // switched to multiple user + else + { + jQuery('.emailadmin_no_single').fadeIn(); + } + if (_event && _event.stopPropagation) _event.stopPropagation(); + return false; + }, + + /** + * Callback if user changed account selction + * + * @param {object} _event event-object or information about event + * @param {et2_baseWidget} _widget widget causing the event + */ + change_account: function(_event, _widget) + { + // todo check dirty and query user to a) save changes, b) discard changes, c) cancel selection + _widget.getInstanceManager().submit(); + } +}); + +function disableGroupSelector() +{ + //alert('Group'+document.getElementById('exec[ea_group]').value+' User'+document.getElementById('eT_accountsel_exec_ea_user').value); + if (document.getElementById('eT_accountsel_exec_ea_user').value != '') + { + if (document.getElementById('exec[ea_group]').value != '') document.getElementById('exec[ea_group]').value = ''; + document.getElementById('exec[ea_group]').disabled = true; + } + else + { + document.getElementById('exec[ea_group]').disabled = false; + } +} + +function addRow(_selectBoxName, _prompt) { + result = prompt(_prompt, ''); + + if((result == '') || (result == null)) { + return false; + } + + var newOption = new Option(result, result); + + selectBox = document.getElementById(_selectBoxName); + var length = selectBox.length; + + selectBox.options[length] = newOption; + selectBox.selectedIndex = length; +} + +function editRow(_selectBoxName, _prompt) { + selectBox = document.getElementById(_selectBoxName); + + selectedItem = selectBox.selectedIndex; + + if(selectedItem != null && selectedItem != -1) { + value = selectBox.options[selectedItem].text; + result = prompt(_prompt, value); + + if((result == '') || (result == null)) { + return false; + } + + var newOption = new Option(result, result); + + selectBox.options[selectedItem] = newOption; + selectBox.selectedIndex = selectedItem; + } +} + +function removeRow(_selectBoxName) { + selectBox = document.getElementById(_selectBoxName); + + selectedItem = selectBox.selectedIndex; + if(selectedItem != null) { + selectBox.options[selectedItem] = null; + } + selectedItem--; + if(selectedItem >= 0) { + selectBox.selectedIndex = selectedItem; + } else if (selectBox.length > 0) { + selectBox.selectedIndex = 0; + } +} + +function selectAllOptions(_selectBoxName) { + selectBox = document.getElementById(_selectBoxName); + + for(var i=0;i request ignored emailadmin cs Plesk nemůže pÅ™ejmenovávat uživatele --> požadavek ignorován +plesk imap server (courier) emailadmin cs Plesk IMAP server (Courier) +plesk mail script '%1' not found !!! emailadmin cs Plesk poÅ¡tovní skript '%1' nebyl nalezen !!! +plesk requires passwords to have at least 5 characters and not contain the account-name --> password not set!!! emailadmin cs Plesk vyžaduje, aby mÄ›la hesla nejménÄ› 5 znaků a neobsahovala název úÄtu --> heslo nebylo nastaveno!!! +plesk smtp-server (qmail) emailadmin cs Plesk SMTP server (Qmail) +pop3 server hostname or ip address emailadmin cs DNS jméno nebo IP adresa POP3 serveru +pop3 server port emailadmin cs Port POP3 serveru +port emailadmin cs port +postfix with ldap emailadmin cs Postfix s LDAP +profile access rights emailadmin cs přístupová práva profilu +profile is active emailadmin cs Profil je aktivní +profile list emailadmin cs Seznam profilů +profile name emailadmin cs Název profilu +qmaildotmode emailadmin cs TeÄkový režim Qmail +quota settings emailadmin cs Nastavení kvóty +quota size in mbyte emailadmin cs velikost kvóty v MBytech +remove emailadmin cs Odstranit +reset filter emailadmin cs vyresetovat filtr +select type of imap server emailadmin cs Vyberte typ IMAP serveru +select type of imap/pop3 server emailadmin cs Vyberte typ IMAP/POP3 serveru +select type of smtp server emailadmin cs Vyberte typ SMTP serveru +send using this email-address emailadmin cs Odeslat s touto e-mailovou adresou +server settings emailadmin cs Nastavení serveru +sieve server hostname or ip address emailadmin cs DNS jméno nebo IP adresa Sieve serveru +sieve server port emailadmin cs Port Sieve serveru +sieve settings emailadmin cs Nastavení sieve +smtp authentication emailadmin cs SMTP autentikace +smtp options emailadmin cs Volby SMTP +smtp server name emailadmin cs Jméno SMTP serveru +smtp settings emailadmin cs Nastavení SMTP +smtp-server hostname or ip address emailadmin cs DNS jméno nebo IP adresa SMTP serveru +smtp-server port emailadmin cs Port SMTP serveru +standard emailadmin cs Standardní +standard imap server emailadmin cs Standardní IMAP server +standard pop3 server emailadmin cs Standardní POP3 server +standard smtp-server emailadmin cs Standardní SMTP server +stationery emailadmin cs Å ablony +the imap server does not appear to support the authentication method selected. please contact your system administrator. emailadmin cs Vypadá to, že IMAP server nepodporuje vybranou autentikaÄní metodu. Zkontaktujte prosím VaÅ¡eho systémového administrátora. +this php has no imap support compiled in!! emailadmin cs Toto PHP nemá zkompilovanou podporu IMAPu. +to use a tls connection, you must be running a version of php 5.1.0 or higher. emailadmin cs Pro použití TLS pÅ™ipojení musíte provozovat verzi PHP 5.1.0 nebo vyšší. +unexpected response from server to authenticate command. emailadmin cs NeoÄekávaná odpovÄ›Ä serveru na příkaz AUTHENTICATE. +unexpected response from server to digest-md5 response. emailadmin cs NeoÄekávaná odpovÄ›Ä serveru na Digest-MD5 odpovÄ›Ä. +unexpected response from server to login command. emailadmin cs NeoÄekávaná odpovÄ›Ä serveru na příkaz LOGIN. +unknown imap response from the server. server responded: %s emailadmin cs Neznámá IMAP odpovÄ›Ä server. OdpovÄ›dÄ›l: %s +unsupported action '%1' !!! emailadmin cs Nepodporovaná akce '%1' !!! +update current email address: emailadmin cs Aktualizovat souÄasnou e-mailovou adresu: +use ldap defaults emailadmin cs Použít výchozí hodnoty LDAP +use predefined username and password defined below emailadmin cs Použít pÅ™eddefinované uživatelské jméno a heslo uvedené níže +use smtp auth emailadmin cs Použít SMTP autentikaci +use tls authentication emailadmin cs Použít TLS autentikaci +use tls encryption emailadmin cs Použít TLS Å¡ifrování +user can edit forwarding address emailadmin cs Uživatel smí editovat adresu pro pÅ™eposílání +username (standard) emailadmin cs uživatelské jméno (standardní) +username/password defined by admin emailadmin cs Uživatelské jméno/Heslo definované administrátorem +username@domainname (virtual mail manager) emailadmin cs uživatelskéjméno@doména (Virtuální správce poÅ¡ty) +users can define their own emailaccounts emailadmin cs Uživatelé smí definovat vlastní poÅ¡tovní úÄty +users can define their own identities emailadmin cs Uživatelé smí definovat své vlastní identity +users can define their own signatures emailadmin cs Uživatelé smí definovat své vlastní podpisy +users can utilize these stationery templates emailadmin cs Uživatelé mohou využívat tyto Å¡ablony dopisů +virtual mail manager emailadmin cs Virtuální správce poÅ¡ty +you have received a new message on the emailadmin cs PÅ™iÅ¡la Vám nová zpráva na +your name emailadmin cs VaÅ¡e jméno diff --git a/emailadmin/lang/egw_da.lang b/emailadmin/lang/egw_da.lang new file mode 100644 index 0000000000..7cadef8e1e --- /dev/null +++ b/emailadmin/lang/egw_da.lang @@ -0,0 +1,72 @@ +add profile emailadmin da Tilføj Profil +admin dn emailadmin da admin dn +admin password emailadmin da admin adgangskode +admin username emailadmin da admin brugernavn +advanced options emailadmin da avanceret indstillinger +alternate email address emailadmin da alternativ e-mail adresse +cyrus imap server emailadmin da Cyrus IMAP Server +cyrus imap server administration emailadmin da Cyrus IMAP server administration +default emailadmin da standart +deliver extern emailadmin da lever ekstern +do you really want to delete this profile emailadmin da Vil du virkelig slette denne profil +domainname emailadmin da domæne navn +edit email settings emailadmin da redigere e-mail indstillingerne +email account active emailadmin da e-mail konto aktiv +email address emailadmin da e-mail adresse +enable cyrus imap server administration emailadmin da aktivere Cyrus IMAP server administration +enable sieve emailadmin da aktiver Sieve +enter your default mail domain (from: user@domain) emailadmin da Indtast dit standart post domæne (fra: bruger@domæne) +forward also to emailadmin da videresend ogsÃ¥ til +forward email's to emailadmin da videresend e-mails til +forward only emailadmin da videresend kun +imap admin password admin da IMAP admin adgangskode +imap admin user admin da IMAP admin bruger +imap c-client version < 2001 emailadmin da IMAP C-Klient Version < 2001 +imap server emailadmin da IMAP Server +imap server hostname or ip address emailadmin da IMAP server domænenavn eller IP adresse +imap server logintyp emailadmin da IMAP server login type +imap server port emailadmin da IMAP server port +imap/pop3 server name emailadmin da IMAP/POP3 server navn +in mbyte emailadmin da i Megabytes +ldap basedn emailadmin da LDAP basedn +ldap server emailadmin da LDAP server +ldap server accounts dn emailadmin da LDAP server konto DN +ldap server admin dn emailadmin da LDAP server admin DN +ldap server admin password emailadmin da LDAP server admin adgangskode +ldap server hostname or ip address emailadmin da LDAP server domænenavn eller IP adresse +ldap settings emailadmin da LDAP indstillinger +leave empty for no quota emailadmin da efterlad tom hvis ingen citat +mail settings admin da Post indstillinger +name of organisation emailadmin da Navn pÃ¥ organisation +no alternate email address emailadmin da ingen alternativ e-mail adresse +no forwarding email address emailadmin da ingen viderestillet e-mail adresse +order emailadmin da Rækkefølge +organisation emailadmin da Organisation +pop3 server hostname or ip address emailadmin da POP3 server domænenavn eller IP adresse +pop3 server port emailadmin da POP server port +postfix with ldap emailadmin da Postfix med LDAP +profile list emailadmin da Profil liste +profile name emailadmin da Profil navn +qmaildotmode emailadmin da qmaildotmode +quota settings emailadmin da quota indstillinger +remove emailadmin da fjern +select type of imap/pop3 server emailadmin da Vælg type IMAP/POP3 server +select type of smtp server emailadmin da Vælg time POP3 server +sieve server hostname or ip address emailadmin da Sieve server domænenavn eller IP adresse +sieve server port emailadmin da Sieve server port +sieve settings emailadmin da Sieve indstillinger +smtp server name emailadmin da SMTP server navn +smtp settings emailadmin da SMTP indstillinger +smtp-server hostname or ip address emailadmin da SMTP server domænenavn eller IP adresse +smtp-server port emailadmin da SMTP server port +standard emailadmin da Standart +standard imap server emailadmin da Standart IMAP server +standard pop3 server emailadmin da Standart POP3 server +standard smtp-server emailadmin da Standart SMTP server +this php has no imap support compiled in!! emailadmin da Der er ikke understøttelse for IMAP i din PHP kode. +use ldap defaults emailadmin da brug LDAP standarter +use smtp auth emailadmin da Brug SMTP autorisation +use tls authentication emailadmin da Brug TLS autorisation +use tls encryption emailadmin da Brug TLS kryptering +users can define their own emailaccounts emailadmin da Brugere kan selv definere deres egne e-mail kontoer +virtual mail manager emailadmin da Virtuel post hÃ¥ndtering diff --git a/emailadmin/lang/egw_de.lang b/emailadmin/lang/egw_de.lang new file mode 100644 index 0000000000..a39d25588f --- /dev/null +++ b/emailadmin/lang/egw_de.lang @@ -0,0 +1,208 @@ +%1 entries deleted. emailadmin de %1 Einträge gelöscht +(imapclass must support this feature by querying the corresponding config value and pass it as defaultquota to the imapserver) emailadmin de (Die IMAP Klasse muss dieses Verfahren unterstützen indem es den entsprechenden Konfigurationswert der Instanz ausliest und als Default Quota an den IMAP Server meldet.) +(no subject) emailadmin de (Kein Betreff) +account '%1' not found !!! emailadmin de Mailkonto '%1' nicht gefunden ! +account deleted. emailadmin de Mailkonto gelöscht. +account not found! common de Mailkonto nicht gefunden! +account saved. emailadmin de Mailkonto gespeichert. +active templates emailadmin de Aktive Vorlagen +add new email address: emailadmin de Neue E-Mailadresse hinzufügen +add profile emailadmin de Profil hinzufügen +admin dn emailadmin de Admin DN +admin password emailadmin de Admin Passwort +admin username emailadmin de Administrator Benutzername +advanced options emailadmin de erweiterte Einstellungen +alternate email address emailadmin de zusätzliche E-Mail-Adressen +and logged in emailadmin de und eingeloggt +any application emailadmin de jede Anwendung +any group emailadmin de jede Gruppe +any user emailadmin de jeder Benutzer +back to admin/grouplist emailadmin de Zurück zu: Admin / Gruppenverwaltung +back to admin/userlist emailadmin de Zurück zu: Admin / Benutzerverwaltung +bad login name or password. emailadmin de Falscher Benutzername oder Passwort. +bad or malformed request. server responded: %s emailadmin de Falsche oder ungültige Anfrage. Server antwortet: %s +bad request: %s emailadmin de Falsche Anfrage: %s +can be used by application emailadmin de Kann von folgender Anwendung verwendet werden +can be used by group emailadmin de Kann von folgender Gruppe verwendet werden +can be used by user emailadmin de Kann von folgendem Benutzer verwendet werden +connection dropped by imap server. emailadmin de Verbindung von IMAP-Server beendet. +connection is not secure! everyone can read eg. your credentials. emailadmin de Die Verbindung ist NICHT sicher! Jeder kann zB. Ihr Passwort lesen. +continue emailadmin de Weiter +could not append message: emailadmin de Diese Mail lässt sich nicht anzeigen +could not complete request. reason given: %s emailadmin de Konnte Anfrage nicht beenden. Grund: %s +could not open secure connection to the imap server. %s : %s. emailadmin de Konnte keine sichere Verbindung zum IMAP Server aufbauen. %s: %s. +cram-md5 or digest-md5 requires the auth_sasl package to be installed. emailadmin de CRAM-MD5 oder DIGEST-MD5 erfordert, das das Auth_SASL Packet installiert ist. +create new account emailadmin de Neues Mailkonto erstellen +create new identity emailadmin de Neue Identität erstellen +cyrus imap server emailadmin de Cyrus IMAP-Server +cyrus imap server administration emailadmin de Cyrus IMAP-Server Administration +default emailadmin de Vorgabe +delete identity emailadmin de Identität löschen +delete this account emailadmin de Diese Konto löschen +deliver extern emailadmin de extern ausliefern +displaying html messages is disabled emailadmin de Das Anzeigen von HTML Nachrichten ist deaktiviert +displaying plain messages is disabled emailadmin de Das Anzeigen von Text Nachrichten ist deaktiviert +do not validate certificate emailadmin de Zertifikat nicht überprüfen +do you really want to delete this profile emailadmin de Wollen Sie dieses Profil wirklich löschen +do you really want to reset the filter for the profile listing emailadmin de Möchten Sie den Filter für die Profilliste wirklich zurücksetzen? +domainname emailadmin de Domänenname +edit email settings emailadmin de E-Mail-Einstellungen +email account active emailadmin de E-Mail-Konto aktiv +email address emailadmin de E-Mail-Adresse +email settings common de E-Mail-Konto +emailadmin emailadmin de E-Mail-Admin +emailadmin: group assigned profile common de E-Mail-Admin: Vordefiniertes Gruppenprofil +emailadmin: user assigned profile common de E-Mail-Admin: Vordefiniertes Benutzerprofil +enable cyrus imap server administration emailadmin de Cyrus IMAP-Server Administration aktivieren +enable sieve emailadmin de Sieve aktivieren +encrypted connection emailadmin de verschlüsselte Verbindung +encryption settings emailadmin de Verschlüsselungseinstellungen +enter your default mail domain (from: user@domain) emailadmin de Standard E-Mail-Domain (Von: benutzer@domain) +entry saved emailadmin de Eintrag gespeichert +error connecting to imap server. %s : %s. emailadmin de Fehler beim Verbinden mit dem IMAP Server. %s : %s. +error connecting to imap server: [%s] %s. emailadmin de Fehler beim Verbinden mit dem IMAP Server. [%s] %s. +error deleting entry! emailadmin de Fehler beim Löschen des Eintrags +error saving account! emailadmin de Fehler beim Speichern des Mailkontos! +error saving the entry!!! emailadmin de Fehler beim Speichern ! +error, no username! emailadmin de Fehler, kein Benutzername! +event details follow emailadmin de Hier die Details des Termins +failed to delete account! emailadmin de Fehler beim Löschen des Mailkontos! +file rejected, no %2. is:%1 emailadmin de Datei wurde abgewiesen, kein %2. ist:%1 +filtered by account emailadmin de Suche nach Benutzerprofilen +filtered by group emailadmin de Suche nach Gruppenprofilen +forward also to emailadmin de zusätzlich weiterleiten +forward email's to emailadmin de E-Mails weiterleiten an +forward only emailadmin de nur weiterleiten +forward only disables imap mailbox / storing of mails and just forwards them to given address. emailadmin de Nur weiterleiten schaltet die IMAP Mailbox / das Speichern der Mails aus und leitet diese an die angegebene Adresse weiter. +global options emailadmin de Globale Optionen +hostname or ip emailadmin de Hostname oder IP +how username get constructed emailadmin de Wie wird der Benutzername gebildet +identity deleted emailadmin de Identität gelöscht. +identity saved. emailadmin de Identität gespeichert. +if different from email address emailadmin de falls unterschiedlich zu E-Mail-Adresse +if using ssl or tls, you must have the php openssl extension loaded. emailadmin de Wenn Sie SSL oder TLS benutzen, müssen Sie die openssl PHP Erweiterung geladen haben. +if you specify port 5190 as sieve server port, you enforce ssl for sieve (server must support that) emailadmin de Wenn Sie als SIEVE Server Port 5190 eintragen, wird für die Kommunikation mit dem SIEVE-Server eine SSL-Verbindung verwendet (der Server muss das natürlich unterstützen) +imap admin password admin de IMAP Administrator Passwort +imap admin user admin de IMAP Administrator Benutzer +imap c-client version < 2001 emailadmin de IMAP C-Client Version < 2001 +imap server emailadmin de IMAP Server +imap server closed the connection. emailadmin de IMAP Server hat die Verbindung beendet. +imap server closed the connection. server responded: %s emailadmin de IMAP Server hat die Verbindung beendet. Server Antwort: %s +imap server hostname or ip address emailadmin de IMAP-Server Hostname oder IP-Adresse +imap server logintyp emailadmin de IMAP-Server Loginverfahren +imap server name emailadmin de IMAP-Server Name +imap server port emailadmin de IMAP-Server Port +imap/pop3 server name emailadmin de IMAP/POP3-Server Name +importance emailadmin de wichtig +in mbyte emailadmin de in MByte +inactive emailadmin de inaktiv +ldap basedn emailadmin de LDAP BaseDN +ldap server emailadmin de LDAP Server +ldap server accounts dn emailadmin de LDAP-Server Benutzerkonten DN +ldap server admin dn emailadmin de LDAP-Server Administrator DN +ldap server admin password emailadmin de LDAP-Server Administrator-Passwort +ldap server hostname or ip address emailadmin de LDAP-Server Hostname oder IP-Adresse +ldap settings emailadmin de LDAP-Einstellungen +leave empty for no quota emailadmin de leer lassen um Quota zu deaktivieren +mail settings admin de E-Mail-Einstellungen +manage stationery templates emailadmin de Briefpapiervorlagen verwalten +manual entry emailadmin de Manuelle Eingabe +mb used emailadmin de MB belegt +name of organisation emailadmin de Name der Organisation +no alternate email address emailadmin de keine zusätzlichen E-Mail-Adressen +no encryption emailadmin de keine Verschlüsselung +no forwarding email address emailadmin de keine Weiterleitungsadresse definiert +no message returned. emailadmin de Keine Nachricht zurückgeliefert. +no plain text part found emailadmin de Kein Nachrichten Text-Teil gefunden +no sieve support detected, either fix configuration manually or leave it switched off. emailadmin de Keine Sieve-Unterstützung gefunden. Konfiguration entweder manuell anpassen oder Sieve ausgeschalten lassen. +no supported imap authentication method could be found. emailadmin de Keine unterstützte IMAP-Authentifizierungsmethode gefunden. +order emailadmin de Reihenfolge +organisation emailadmin de Organisation +plesk can't rename users --> request ignored emailadmin de Plesk kann keine Benutzer umbenennen --> Anforderung ignoriert +plesk imap server (courier) emailadmin de Plesk IMAP Server (Courier) +plesk mail script '%1' not found !!! emailadmin de Plesk Mail Skript '%1' nicht gefunden !!! +plesk requires passwords to have at least 5 characters and not contain the account-name --> password not set!!! emailadmin de Plesk verlangt, dass Passwörter mindestens 5 Zeichen lang sind und nicht den Benutzernamen enthalten --> Passwort nicht gesetzt!!! +plesk smtp-server (qmail) emailadmin de Plesk SMTP-Server (Qmail) +pop3 server hostname or ip address emailadmin de POP3-Server Hostname oder IP-Adresse +pop3 server port emailadmin de POP3-Server Port +port emailadmin de Port +postfix with ldap emailadmin de Postfix mit LDAP +processing of file %1 failed. failed to meet basic restrictions. emailadmin de Die Verarbeitung der Datei %1 fehlgeschlagen. Die Basis Voraussetzungen wurden nicht erfülltt +profile access rights emailadmin de Profilzugriffsrechte +profile is active emailadmin de Profil ist aktiv +profile list emailadmin de Profilliste +profile name emailadmin de Profilname +qmaildotmode emailadmin de qmaildotmode +quota settings emailadmin de Quota Einstellungen +quota size in mbyte emailadmin de Quota Größe in MByte +relay access checked emailadmin de nicht angemeldetes Senden überprüft +remove emailadmin de Entfernen +required pear class mail/mimedecode.php not found. emailadmin de Die benötigte Classe PEAR (Mail/mimeDecode.php) wurde nicht gefunden. +reset filter emailadmin de Filter zurücksetzen +save of message %1 failed. could not save message to folder %2 due to: %3 emailadmin de Das Speichern der Nachricht %1 ist fehlgeschlagen. Die Nachricht konnte nicht in das Verzeichniss %2 bzw.: %3 +saving of message %1 failed. destination folder %2 does not exist. emailadmin de Das Speichern der Nachricht %1 ist fehlgeschlagen. Der Ordner %2 ist nicht vorhanden. +secure connection emailadmin de Sichere Verbindung +select type of imap server emailadmin de IMAP-Server Typ auswählen +select type of imap/pop3 server emailadmin de IMAP/POP3-Server Typ auswählen +select type of smtp server emailadmin de SMTP-Server Typ auswählen +send using this email-address emailadmin de zum Versenden wird diese E-Mail Adresse benutzt +server settings emailadmin de Server-Einstellungen +sieve server hostname or ip address emailadmin de Sieve-Server Hostname oder IP-Adresse +sieve server port emailadmin de Sieve-Server Port +sieve settings emailadmin de Sieve Einstellungen +skip imap emailadmin de IMAP auslassen +skipping imap configuration! emailadmin de IMAP Konfiguration ausgelassen! +smtp authentication emailadmin de SMTP Anmeldung +smtp options emailadmin de SMTP Optionen +smtp server emailadmin de SMTP Server +smtp server name emailadmin de SMTP Server Name +smtp settings emailadmin de SMTP Einstellungen +smtp-server hostname or ip address emailadmin de SMTP Server Hostname oder IP-Adresse +smtp-server port emailadmin de SMTP Server Port +standard emailadmin de Vorgabe +standard identity emailadmin de Standard Identität +standard imap server emailadmin de Standard IMAP-Server +standard pop3 server emailadmin de Standard POP3-Server +standard smtp-server emailadmin de Standard SMTP-Server +starts with emailadmin de startet mit +stationery emailadmin de Briefpapier +successful connected to %1 server%2. emailadmin de Erfolgreich zu %1 Server verbunden%2. +switch back to standard identity to save account. emailadmin de Kehren Sie zur Standard-Identität zurück um das Konto zu speichern. +switch back to standard identity to save other account data. emailadmin de Kehren Sie zur Standard-Identität zurück um andere Kontendaten zu speichern. +templates emailadmin de Templates +the imap server does not appear to support the authentication method selected. please contact your system administrator. emailadmin de Der IMAP Server scheint die eingestellte Authentifizierungsmethode nicht zu unterstützen. Bitte fragen Sie Ihren Systemadministrator. +the mimeparser can not parse this message. emailadmin de Der Mimiparser versteht diese Nachricht nicht +this is not a personal mail account!\n\naccount will be deleted for all users!\n\nare you really sure you want to do that? emailadmin de Das ist KEIN persönliches Mailkonto!\n\nDas Konto wird für ALLE Benutzer gelöscht!\n\nSind Sie wirklich sicher, dass Sie das wollen? +this php has no imap support compiled in!! emailadmin de Dieses PHP hat keine IMAP Unterstützung!!! +to use a tls connection, you must be running a version of php 5.1.0 or higher. emailadmin de Um eine TLS Verbindung zu verwenden, benötigen Sie PHP 5.1.0 oder aktueller. +unexpected response from server to authenticate command. emailadmin de Unerwartete Antwort des Servers auf das AUTHENTICATE Kommando. +unexpected response from server to digest-md5 response. emailadmin de Unerwartete Antwort des Servers auf die Digest-MD5 Antwort. +unexpected response from server to login command. emailadmin de Unerwartete Antwort des Servers auf das LOGIN Kommando. +unknown imap response from the server. server responded: %s emailadmin de Unbekannte IMAP Antwort vom Server. Server antwortet: %s +unsupported action '%1' !!! emailadmin de Nicht unterstützte Aktion '%1' !!! +update current email address: emailadmin de Aktualisiere aktuelle E-Mailadresse +use ldap defaults emailadmin de LDAP Standardeinstellungen benutzen +use predefined username and password defined below emailadmin de Verwende den unten vordefinierten Benutzernamen und Passwort +use smtp auth emailadmin de SMTP Authentifizierung benutzen +use tls authentication emailadmin de TLS Authentifizierung benutzen +use tls encryption emailadmin de TLS Verschlüsselung benutzen +use users email-address (as seen in useraccount) emailadmin de Benutzt E-Mail Adresse des Benutzers (Die unter seinem Mailkonto angezeigt wird) +user can edit forwarding address emailadmin de Anwender können ihre Weiterleitungsadresse bearbeiten +userid@domain eg. u1234@domain emailadmin de UserId@domain z.B. u1234@domain +username (standard) emailadmin de Benutzername (Standard) +username specified below for all emailadmin de Unter angegebener Benutzername für alle +username/password defined by admin emailadmin de Benutzername / Passwort vordefiniert +username@domainname (virtual mail manager) emailadmin de Benutzername@Domänenname (Virtual MAIL ManaGeR) +users can define their own emailaccounts emailadmin de Anwender können ihre eigenen Konten definieren +users can define their own identities emailadmin de Anwender können ihre eigenen Identitäten definieren +users can define their own signatures emailadmin de Anwender können ihre eigenen Signaturen definieren +users can utilize these stationery templates emailadmin de Benutzer können diese Briefpapiervorlagen verwenden +using data from mozilla ispdb for provider %1 emailadmin de Benutzer Mozilla ISPDB für Provider %1 +vacation messages with start- and end-date require an admin account to be set emailadmin de Abwesenheitsnotizen mit Start- und Enddatum benötigen einen gesetzten Administrator Benutzer! +virtual mail manager emailadmin de Virtual MAIL ManaGeR +yes, use credentials below only for alarms and notifications, otherwise use credentials of current user emailadmin de Ja, die Daten darunter nur für Alarme und Benachrichtigungen verwenden, ansonsten die Daten des aktiven Benutzers. +yes, use credentials of current user or if given credentials below emailadmin de Ja, benutze Daten des aktuellen Benutzers oder wenn angegeben die Daten darunter +you have received a new message on the emailadmin de Sie haben eine neue Nachricht erhalten. +you need to specify a forwarding address, when checking "%1"! emailadmin de Sie müssen eine Weiterleitungsadresse angeben, wenn "%1" abgehackt ist! +your message to %1 was displayed. emailadmin de Ihre Nachricht %1 wurde angezeigt +your name emailadmin de Ihr Name diff --git a/emailadmin/lang/egw_el.lang b/emailadmin/lang/egw_el.lang new file mode 100644 index 0000000000..b6a38f8a07 --- /dev/null +++ b/emailadmin/lang/egw_el.lang @@ -0,0 +1,51 @@ +advanced options emailadmin el επιλογές για Ï€ÏοχωÏημένους +alternate email address emailadmin el εναλλακτική διεÏθυνση email +bad login name or password. emailadmin el Λάθος όνομα ή κωδικός Ï€Ïόσβασης +bad request: %s emailadmin el ΆκυÏη απαίτηση: % +connection dropped by imap server. emailadmin el Έπεσε η σÏνδεση από τον IMAP server +could not complete request. reason given: %s emailadmin el Δεν ολοκληÏώθηκε το αίτημα. Αιτία: %s +could not open secure connection to the imap server. %s : %s. emailadmin el Δεν έγινε ασφαλής σÏνδεση με τον IMAP server. %s : %s. +cram-md5 or digest-md5 requires the auth_sasl package to be installed. emailadmin el CRAM-MD5 ή DIGEST-MD5 απαιτεί το Auth_SASL πακέτο να εγκατασταθεί. +default emailadmin el Ï€ÏοκαθοÏισμένο +deliver extern emailadmin el παÏάδοση ÎµÎ¾Ï‰Ï„ÎµÏ +do not validate certificate emailadmin el μην επικυÏώνετε το πιστοποιητικό +edit email settings emailadmin el επεξεÏγασία Ïυθμίσεων email +email account active emailadmin el ενεÏγός κατάλογος email +email address emailadmin el διεÏθυνση email +encrypted connection emailadmin el αποκÏυπτογÏαφημένη σÏνδεση +error connecting to imap server. %s : %s. emailadmin el Σφάλμα κατά τη σÏνδεση με τον IMAP server. %s : %s. +error connecting to imap server: [%s] %s. emailadmin el Σφάλμα κατά τη σÏνδεση με τον IMAP server. [%s] : %s. +forward also to emailadmin el Ï€Ïοώθηση επίσης στο +forward only emailadmin el μόνο Ï€Ïοώθηση +if using ssl or tls, you must have the php openssl extension loaded. emailadmin el Αν χÏησιμοποιείται SSL ή TLS, Ï€Ïέπει να έχετε την PHP openssl επέκταση φοÏτωμένη. +imap server emailadmin el IMAP Server +imap server closed the connection. emailadmin el Ο IMAP Server έκλεισε τη σÏνδεση +imap server closed the connection. server responded: %s emailadmin el Ο IMAP Server έκλεισε τη σÏνδεση. Ο Server απάντησε: %s +in mbyte emailadmin el σε MByte +leave empty for no quota emailadmin el να μείνει κενό για μη ποσοστόσεις +mail settings admin el Ρυθμίσεις μηνυμάτων +no alternate email address emailadmin el δεν υπάÏχει εναλλακτική διεÏθυνση email +no encryption emailadmin el Καμία κÏυπτογÏάφηση +no message returned. emailadmin el Δεν επεστÏάφη κάποιο μήνυμα. +no supported imap authentication method could be found. emailadmin el Δεν βÏέθηκε καμία υποστηÏιζόμενη IMAP επικυÏωμένη μέθοδος. +order emailadmin el Ταξινόμηση +organisation emailadmin el οÏγανισμός +port emailadmin el θÏÏα +quota settings emailadmin el Ïυθμίσεις ποσοστών +quota size in mbyte emailadmin el μέγεθος ποσοστών σε MByte +remove emailadmin el αφαίÏεση +sieve settings emailadmin el Ρυθμίσεις Sieve +smtp settings emailadmin el Ρυθμίσεις SMTP +standard emailadmin el συνήθης +standard imap server emailadmin el συνήθης IMAP server +standard pop3 server emailadmin el συνήθης POP3 server +standard smtp-server emailadmin el συνήθης SMTP-server +the imap server does not appear to support the authentication method selected. please contact your system administrator. emailadmin el Ο IMAP server δεν υποστηÏίζει την επιλεγμένη μέθοδο αυθεντικότητας.ΠαÏακαλώ επικοινωνήστε με τον διαχειÏιστή σας. +this php has no imap support compiled in!! emailadmin el Αυτό το PHP δεν έχει καθόλου IMAP υποστήÏιξη μεταφÏασμένη!! +to use a tls connection, you must be running a version of php 5.1.0 or higher. emailadmin el Για τη χÏησιμοποίηση TLS σÏνδεσης, Ï€Ïέπει να έχετε PHP 5.1.0 ή υψηλότεÏη έκδοση. +unexpected response from server to authenticate command. emailadmin el ΑπÏόσμενη απάντηση από τον server στην ΕΞΑΚΡΙΒΩΣΗ ΓÎΗΣΙΟΤΗΤΑΣ εντολής. +unexpected response from server to digest-md5 response. emailadmin el ΑπÏόσμενη απάντηση από τον server στην Digest-MD5 απάντηση. +unexpected response from server to login command. emailadmin el ΑπÏόσμενη απάντηση από τον server στην εντολή ΕΙΣΟΔΟΥ +unknown imap response from the server. server responded: %s emailadmin el Άγνωστη IMAP απάντηση από τον server.Απάντηση Server:%s +use smtp auth emailadmin el ΧÏήση SMTP αυθ +users can define their own emailaccounts emailadmin el Οι χÏήστες μποÏοÏν να οÏίσουν τους δικοÏÏ‚ τους email λογαÏιασμοÏÏ‚ diff --git a/emailadmin/lang/egw_en.lang b/emailadmin/lang/egw_en.lang new file mode 100755 index 0000000000..019ca41b78 --- /dev/null +++ b/emailadmin/lang/egw_en.lang @@ -0,0 +1,208 @@ +%1 entries deleted. emailadmin en %1 entries deleted. +(imapclass must support this feature by querying the corresponding config value and pass it as defaultquota to the imapserver) emailadmin en (imapclass must support this feature by querying the corresponding config value and pass it as defaultquota to the imapserver) +(no subject) emailadmin en (no subject) +account '%1' not found !!! emailadmin en Account '%1' not found! +account deleted. emailadmin en Account deleted. +account not found! common en Account not found! +account saved. emailadmin en Account saved. +active templates emailadmin en Active templates +add new email address: emailadmin en Add new email address: +add profile emailadmin en Add profile +admin dn emailadmin en Admin dn +admin password emailadmin en Admin password +admin username emailadmin en Admin user name +advanced options emailadmin en Advanced options +alternate email address emailadmin en Alternate email address +and logged in emailadmin en and logged in +any application emailadmin en Any application +any group emailadmin en Any group +any user emailadmin en Any user +back to admin/grouplist emailadmin en Back to Admin / Group list +back to admin/userlist emailadmin en Back to Admin / User list +bad login name or password. emailadmin en Bad login name or password. +bad or malformed request. server responded: %s emailadmin en Bad or malformed request. %s +bad request: %s emailadmin en Bad request: %s +can be used by application emailadmin en Can be used by application +can be used by group emailadmin en Can be used by group +can be used by user emailadmin en Can be used by user +connection dropped by imap server. emailadmin en Connection dropped by IMAP server. +connection is not secure! everyone can read eg. your credentials. emailadmin en Connection is NOT secure! Everyone can read eg. your credentials. +continue emailadmin en Continue +could not append message: emailadmin en Could not append Message: +could not complete request. reason given: %s emailadmin en Could not complete request. %s +could not open secure connection to the imap server. %s : %s. emailadmin en Could not open secure connection to the IMAP server. %s : %s. +cram-md5 or digest-md5 requires the auth_sasl package to be installed. emailadmin en CRAM-MD5 or DIGEST-MD5 requires the Auth_SASL package to be installed. +create new account emailadmin en Create new account +create new identity emailadmin en Create new identity +cyrus imap server emailadmin en Cyrus IMAP server +cyrus imap server administration emailadmin en Cyrus IMAP server administration +default emailadmin en Default +delete identity emailadmin en Delete identity +delete this account emailadmin en Delete this account +deliver extern emailadmin en Deliver extern +displaying html messages is disabled emailadmin en displaying html messages is disabled +displaying plain messages is disabled emailadmin en displaying plain messages is disabled +do not validate certificate emailadmin en Do not validate certificate +do you really want to delete this profile emailadmin en Do you really want to delete this profile? +do you really want to reset the filter for the profile listing emailadmin en Do you really want to reset the filter for the profile listing? +domainname emailadmin en Domain name +edit email settings emailadmin en Edit email settings +email account active emailadmin en Email account active +email address emailadmin en Email address +email settings common en Email settings +emailadmin emailadmin en eMailAdmin +emailadmin: group assigned profile common en eMailAdmin: Group assigned profile +emailadmin: user assigned profile common en eMailAdmin: User assigned profile +enable cyrus imap server administration emailadmin en Enable Cyrus IMAP server administration +enable sieve emailadmin en Enable Sieve +encrypted connection emailadmin en Encrypted connection +encryption settings emailadmin en Encryption settings +enter your default mail domain (from: user@domain) emailadmin en Enter your default mail domain from: user@domain +entry saved emailadmin en Entry saved +error connecting to imap server. %s : %s. emailadmin en Error connecting to IMAP server. %s : %s. +error connecting to imap server: [%s] %s. emailadmin en Error connecting to IMAP server: [%s] %s. +error deleting entry! emailadmin en Error deleting entry! +error saving account! emailadmin en Error saving account! +error saving the entry!!! emailadmin en Error saving the entry! +error, no username! emailadmin en Error, no username! +event details follow emailadmin en Event Details follow +failed to delete account! emailadmin en Failed to delete account! +file rejected, no %2. is:%1 emailadmin en File rejected, no %2. Is:%1 +filtered by account emailadmin en Filtered by account +filtered by group emailadmin en Filtered by group +forward also to emailadmin en Forward also to +forward email's to emailadmin en Forward email's to +forward only emailadmin en Forward only +forward only disables imap mailbox / storing of mails and just forwards them to given address. emailadmin en Forward only disables IMAP mailbox / storing of mails and just forwards them to given address. +global options emailadmin en Global options +hostname or ip emailadmin en Hostname or IP +how username get constructed emailadmin en How username get constructed +identity deleted emailadmin en Identity deleted +identity saved. emailadmin en Identity saved. +if different from email address emailadmin en if different from EMail address +if using ssl or tls, you must have the php openssl extension loaded. emailadmin en If using SSL or TLS, you must have the PHP openssl extension loaded. +if you specify port 5190 as sieve server port, you enforce ssl for sieve (server must support that) emailadmin en if you specify port 5190 as sieve server port, you enforce ssl for sieve (server must support that) +imap admin password admin en IMAP admin password +imap admin user admin en IMAP admin user +imap c-client version < 2001 emailadmin en IMAP C-Client Version < 2001 +imap server emailadmin en IMAP server +imap server closed the connection. emailadmin en IMAP server closed the connection. +imap server closed the connection. server responded: %s emailadmin en IMAP Server closed the connection. Server Responded: %s +imap server hostname or ip address emailadmin en IMAP server hostname or ip address +imap server logintyp emailadmin en IMAP server login type +imap server name emailadmin en IMAP server name +imap server port emailadmin en IMAP server port +imap/pop3 server name emailadmin en IMAP/POP3 server name +importance emailadmin en importance +in mbyte emailadmin en in MByte +inactive emailadmin en Inactive +ldap basedn emailadmin en LDAP basedn +ldap server emailadmin en LDAP server +ldap server accounts dn emailadmin en LDAP server accounts DN +ldap server admin dn emailadmin en LDAP server admin DN +ldap server admin password emailadmin en LDAP server admin password +ldap server hostname or ip address emailadmin en LDAP server host name or IP address +ldap settings emailadmin en LDAP settings +leave empty for no quota emailadmin en Leave empty for no quota +mail settings admin en Mail settings +manage stationery templates emailadmin en Manage stationery templates +manual entry emailadmin en Manual entry +mb used emailadmin en MB used +name of organisation emailadmin en Name of organization +no alternate email address emailadmin en No alternate email address +no encryption emailadmin en No encryption +no forwarding email address emailadmin en No forwarding email address +no message returned. emailadmin en No message returned +no plain text part found emailadmin en no plain text part found +no sieve support detected, either fix configuration manually or leave it switched off. emailadmin en No sieve support detected, either fix configuration manually or leave it switched off. +no supported imap authentication method could be found. emailadmin en No supported IMAP authentication method could be found. +order emailadmin en Order +organisation emailadmin en Organisation +plesk can't rename users --> request ignored emailadmin en Plesk can't rename users --> request ignored +plesk imap server (courier) emailadmin en Plesk IMAP Server (Courier) +plesk mail script '%1' not found !!! emailadmin en Plesk mail script '%1' not found! +plesk requires passwords to have at least 5 characters and not contain the account-name --> password not set!!! emailadmin en Plesk requires passwords to have at least 5 characters and not contain the account name --> password NOT set! +plesk smtp-server (qmail) emailadmin en Plesk SMTP-Server (Qmail) +pop3 server hostname or ip address emailadmin en POP3 server hostname or IP address +pop3 server port emailadmin en POP3 server port +port emailadmin en Port +postfix with ldap emailadmin en Postfix with LDAP +processing of file %1 failed. failed to meet basic restrictions. emailadmin en Processing of file %1 failed. Failed to meet basic restrictions. +profile access rights emailadmin en Profile access rights +profile is active emailadmin en Profile is active +profile list emailadmin en Profile list +profile name emailadmin en Profile name +qmaildotmode emailadmin en qmaildotmode +quota settings emailadmin en Quota settings +quota size in mbyte emailadmin en Quota size in MByte +relay access checked emailadmin en Relay access checked +remove emailadmin en Remove +required pear class mail/mimedecode.php not found. emailadmin en Required PEAR class Mail/mimeDecode.php not found. +reset filter emailadmin en Reset filter +save of message %1 failed. could not save message to folder %2 due to: %3 emailadmin en Save of message %1 failed. Could not save message to folder %2 due to: %3 +saving of message %1 failed. destination folder %2 does not exist. emailadmin en Saving of message %1 failed. Destination Folder %2 does not exist. +secure connection emailadmin en Secure connection +select type of imap server emailadmin en Select type of IMAP server +select type of imap/pop3 server emailadmin en Select type of IMAP/POP3 server +select type of smtp server emailadmin en Select type of SMTP server +send using this email-address emailadmin en Send using this email address +server settings emailadmin en Server settings +sieve server hostname or ip address emailadmin en Sieve server hostname or IP address +sieve server port emailadmin en Sieve server port +sieve settings emailadmin en Sieve settings +skip imap emailadmin en Skip IMAP +skipping imap configuration! emailadmin en Skipping IMAP configuration! +smtp authentication emailadmin en SMTP authentication +smtp options emailadmin en SMTP options +smtp server emailadmin en SMTP server +smtp server name emailadmin en SMTP server name +smtp settings emailadmin en SMTP settings +smtp-server hostname or ip address emailadmin en SMTP server hostname or IP address +smtp-server port emailadmin en SMTP server port +standard emailadmin en Standard +standard identity emailadmin en Standard identity +standard imap server emailadmin en Standard IMAP server +standard pop3 server emailadmin en Standard POP3 server +standard smtp-server emailadmin en Standard SMTP server +starts with emailadmin en Starts with +stationery emailadmin en Stationery +successful connected to %1 server%2. emailadmin en Successful connected to %1 server%2. +switch back to standard identity to save account. emailadmin en Switch back to standard identity to save account. +switch back to standard identity to save other account data. emailadmin en Switch back to standard identity to save other account data. +templates emailadmin en Templates +the imap server does not appear to support the authentication method selected. please contact your system administrator. emailadmin en The IMAP server does not appear to support the authentication method selected. Contact your system administrator. +the mimeparser can not parse this message. emailadmin en The mimeparser can not parse this message. +this is not a personal mail account!\n\naccount will be deleted for all users!\n\nare you really sure you want to do that? emailadmin en This is NOT a personal mail account!\n\nAccount will be deleted for ALL users!\n\nAre you really sure you want to do that? +this php has no imap support compiled in!! emailadmin en This PHP has no IMAP support compiled in!! +to use a tls connection, you must be running a version of php 5.1.0 or higher. emailadmin en To use a TLS connection, you must be running a version of PHP 5.1.0 or higher. +unexpected response from server to authenticate command. emailadmin en Unexpected response from server to AUTHENTICATE command. +unexpected response from server to digest-md5 response. emailadmin en Unexpected response from server to Digest-MD5 response. +unexpected response from server to login command. emailadmin en Unexpected response from server to LOGIN command. +unknown imap response from the server. server responded: %s emailadmin en Unknown IMAP response from the server. %s +unsupported action '%1' !!! emailadmin en Unsupported action '%1' ! +update current email address: emailadmin en Update current email address: +use ldap defaults emailadmin en Use LDAP defaults +use predefined username and password defined below emailadmin en Use predefined username and password defined below +use smtp auth emailadmin en Use SMTP authentication +use tls authentication emailadmin en Use TLS authentication +use tls encryption emailadmin en Use TLS encryption +use users email-address (as seen in useraccount) emailadmin en Use users email address, as set in user account +user can edit forwarding address emailadmin en User can edit forwarding address +userid@domain eg. u1234@domain emailadmin en UserId@domain eg. u1234@domain +username (standard) emailadmin en Username (standard) +username specified below for all emailadmin en Username specified below for all +username/password defined by admin emailadmin en Username / Password defined by admin +username@domainname (virtual mail manager) emailadmin en username@domainname (Virtual MAIL ManaGeR) +users can define their own emailaccounts emailadmin en Users can define their own email accounts +users can define their own identities emailadmin en Users can define their own identities +users can define their own signatures emailadmin en Users can define their own signatures +users can utilize these stationery templates emailadmin en Users can utilize these stationery templates +using data from mozilla ispdb for provider %1 emailadmin en Using data from Mozilla ISPDB for provider %1 +vacation messages with start- and end-date require an admin account to be set emailadmin en Vacation messages with start and end date require an admin account to be set! +virtual mail manager emailadmin en Virtual MAIL ManaGeR +yes, use credentials below only for alarms and notifications, otherwise use credentials of current user emailadmin en Yes, use credentials below only for alarms and notifications, otherwise use credentials of current user +yes, use credentials of current user or if given credentials below emailadmin en Yes, use credentials of current user or if given credentials below +you have received a new message on the emailadmin en You have received a new message on the +you need to specify a forwarding address, when checking "%1"! emailadmin en You need to specify a forwarding address, when checking "%1"! +your message to %1 was displayed. emailadmin en Your message to %1 was displayed. +your name emailadmin en Your name diff --git a/emailadmin/lang/egw_es-es.lang b/emailadmin/lang/egw_es-es.lang new file mode 100644 index 0000000000..5dc04c1cda --- /dev/null +++ b/emailadmin/lang/egw_es-es.lang @@ -0,0 +1,149 @@ +account '%1' not found !!! emailadmin es-es ¡No se encontró la cuenta '%1'! +active templates emailadmin es-es Plantillas activas +add new email address: emailadmin es-es Añadir nueva dirección de correo +add profile emailadmin es-es Añadir perfil +admin dn emailadmin es-es dn del administrador +admin password emailadmin es-es contraseña del administrador +admin username emailadmin es-es usuario del administrador +advanced options emailadmin es-es opciones avanzadas +alternate email address emailadmin es-es dirección de correo alternativa +any application emailadmin es-es cualquier aplicación +any group emailadmin es-es cualquier grupo +any user emailadmin es-es cualquier usuario +back to admin/grouplist emailadmin es-es Volver a Administración/Lista de grupos +back to admin/userlist emailadmin es-es Volver a Administración/Lista de usuarios +bad login name or password. emailadmin es-es Nombre de usuario o contraseña incorrectos +bad or malformed request. server responded: %s emailadmin es-es Petición errónea o mal formado. El servidor respondió: %s +bad request: %s emailadmin es-es Petición errónea: %s +can be used by application emailadmin es-es puede usarse por la aplicación +can be used by group emailadmin es-es puede usarse por el grupo +can be used by user emailadmin es-es puede usarse por el usuario +connection dropped by imap server. emailadmin es-es El servidor IMAP ha interrumpido la conexión +continue emailadmin es-es Continuar +could not complete request. reason given: %s emailadmin es-es No se pudo completar la solicitud. Motivo: %s +could not open secure connection to the imap server. %s : %s. emailadmin es-es No se pudo abrir una conexión segura con el servidor IMAP. %s: %s. +cram-md5 or digest-md5 requires the auth_sasl package to be installed. emailadmin es-es CRAM-MD5 o DIGEST-MD5 necesitan el paquete Auth_SASL para estar instalado. +cyrus imap server emailadmin es-es Servidor IMAP Cyrus +cyrus imap server administration emailadmin es-es Administración del servidor IMAP Cyrus +default emailadmin es-es predeterminada +deliver extern emailadmin es-es entrega externa +do not validate certificate emailadmin es-es No validar el certificado +do you really want to delete this profile emailadmin es-es ¿Realmente desea borrar este perfil? +do you really want to reset the filter for the profile listing emailadmin es-es Realmente desea restablecer el filtro para la lista de perfiles +domainname emailadmin es-es nombre del dominio +edit email settings emailadmin es-es editar configuración de la cuenta +email account active emailadmin es-es cuenta de correo electrónico activa +email address emailadmin es-es dirección de correo electrónico +email settings common es-es Configuración del correo electrónico +emailadmin emailadmin es-es Administración del correo electrónico +emailadmin: group assigned profile common es-es eMailAdmin: perfil asignado al grupo +emailadmin: user assigned profile common es-es eMailAdmin: perfil asignado al usuario +enable cyrus imap server administration emailadmin es-es activar administración del servidor Cyrus IMAP +enable sieve emailadmin es-es activar Sieve +encrypted connection emailadmin es-es conexión cifrada +encryption settings emailadmin es-es configuración del cifrado +enter your default mail domain (from: user@domain) emailadmin es-es introduzca el dominio predeterminado (de usuario@dominio) +entry saved emailadmin es-es La entrada ha sido guardada +error connecting to imap server. %s : %s. emailadmin es-es Error al conectar con el servidor IMAP. %s: %s. +error connecting to imap server: [%s] %s. emailadmin es-es Error al conectar con el servidor IMAP: [%s] %s. +error saving the entry!!! emailadmin es-es Error guardando el elemento!!! +filtered by account emailadmin es-es filtrado por cuenta +filtered by group emailadmin es-es filtrado por grupo +forward also to emailadmin es-es reenviar también a +forward email's to emailadmin es-es reenviar correos a +forward only emailadmin es-es sólo reenviar +global options emailadmin es-es opciones globales +if using ssl or tls, you must have the php openssl extension loaded. emailadmin es-es Si usa SSL o TLS, debe tener cargada la extensión openssl de PHP. +imap admin password admin es-es contraseña del administrador IMAP +imap admin user admin es-es usuario administrador IMAP +imap c-client version < 2001 emailadmin es-es Versión C-Cliente IMAP < 2001 +imap server emailadmin es-es Servidor IMAP +imap server closed the connection. emailadmin es-es El servidor IMAP cerró la conexión. +imap server closed the connection. server responded: %s emailadmin es-es El servidor IMAP cerró la conexión. El servidor respondió: %s +imap server hostname or ip address emailadmin es-es Servidor IMAP o dirección IP +imap server logintyp emailadmin es-es Tipo de sesión del servidor IMAP +imap server name emailadmin es-es Nombre del servidor IMAP +imap server port emailadmin es-es Puerto del servidor IMAP +imap/pop3 server name emailadmin es-es Nombre del servidor POP/IMAP +in mbyte emailadmin es-es en MBytes +inactive emailadmin es-es inactivo +ldap basedn emailadmin es-es basedn para LDAP +ldap server emailadmin es-es servidor LDAP +ldap server accounts dn emailadmin es-es DN para cuentas del servidor LDAP +ldap server admin dn emailadmin es-es DN del administrador del servidor LDAP +ldap server admin password emailadmin es-es contraseña del administrador del servidor LDAP +ldap server hostname or ip address emailadmin es-es Nombre del servidor LDAP o dirección IP +ldap settings emailadmin es-es Configuración LDAP +leave empty for no quota emailadmin es-es Dejar en blanco para no poner cuota +mail settings admin es-es Configuración del correo. +manage stationery templates emailadmin es-es Gestionar plantillas preimpresas +name of organisation emailadmin es-es Nombre de la organización +no alternate email address emailadmin es-es Sin dirección de correo alternativa +no encryption emailadmin es-es Sin cifrar +no forwarding email address emailadmin es-es Sin dirección de correo para reenviar +no message returned. emailadmin es-es No se devolvió ningún mensaje. +no supported imap authentication method could be found. emailadmin es-es No se pudo encontrar ningún método soportado de identificación IMAP. +order emailadmin es-es orden +organisation emailadmin es-es organización +plesk can't rename users --> request ignored emailadmin es-es Plesk no puede renombrar usuarios --> Se ignora la solicitud +plesk imap server (courier) emailadmin es-es Servidor IMAP Plesk (Courier) +plesk mail script '%1' not found !!! emailadmin es-es ¡No se encontró el script de correo de Plesk '%1'! +plesk requires passwords to have at least 5 characters and not contain the account-name --> password not set!!! emailadmin es-es Plesk requiere que las contraseñas tengan al menos 5 caracteres y no contengan el nombre de la cuenta --> NO se establece la contraseña +plesk smtp-server (qmail) emailadmin es-es Servidor SMTP de Plesk (Qmail) +pop3 server hostname or ip address emailadmin es-es Nombre del servidor POP3 o dirección IP +pop3 server port emailadmin es-es Puerto del servidor POP3 +port emailadmin es-es puerto +postfix with ldap emailadmin es-es Postfix con LDAP +profile access rights emailadmin es-es Derechos de acceso del perfil +profile is active emailadmin es-es el perfil está activo +profile list emailadmin es-es Lista de perfiles +profile name emailadmin es-es Nombre del perfil +qmaildotmode emailadmin es-es Modo de punto de qmail +quota settings emailadmin es-es Configuración de las cuotas +quota size in mbyte emailadmin es-es tamaño de la cuota en MBytes +remove emailadmin es-es borrar +reset filter emailadmin es-es restablecer filtro +select type of imap server emailadmin es-es Seleccione el tipo de servidor IMAP +select type of imap/pop3 server emailadmin es-es Seleccione el tipo de servidor IMAP/POP3 +select type of smtp server emailadmin es-es Seleccione el tipo de servidor SMTP +send using this email-address emailadmin es-es enviar usando esta dirección de correo electrónico +server settings emailadmin es-es configuración del servidor +sieve server hostname or ip address emailadmin es-es Nombre del servidor Sieve o dirección IP +sieve server port emailadmin es-es Puerto del servidor Sieve +sieve settings emailadmin es-es Configuración de Sieve +smtp authentication emailadmin es-es identificación SMTP +smtp options emailadmin es-es opciones SMTP +smtp server name emailadmin es-es Nombre del servidor SMTP +smtp settings emailadmin es-es configuración SMTP +smtp-server hostname or ip address emailadmin es-es Nombre del servidor SMTP o dirección IP +smtp-server port emailadmin es-es Puerto del servidor SMTP +standard emailadmin es-es Estándar +standard imap server emailadmin es-es Servidor IMAP estándar +standard pop3 server emailadmin es-es Servidor POP3 estándar +standard smtp-server emailadmin es-es Servidor SMTP estándar +stationery emailadmin es-es material preimpreso +the imap server does not appear to support the authentication method selected. please contact your system administrator. emailadmin es-es El servidor IMAP no parece soportar el método de identificación seleccionado. Por favor, póngase en contacto el administrador de su sistema. +this php has no imap support compiled in!! emailadmin es-es ¡¡Esta instalación de PHP no tiene soporte IMAP!! +to use a tls connection, you must be running a version of php 5.1.0 or higher. emailadmin es-es Para usar una conexión TLS, debe ejecutar una versión de PHP 5.1.0 o superior. +unexpected response from server to authenticate command. emailadmin es-es Respuesta inesperada del servidor al comando AUTHENTICATE. +unexpected response from server to digest-md5 response. emailadmin es-es Respuesta inesperada del servidor a la respuesta Digest-MD5. +unexpected response from server to login command. emailadmin es-es Respuesta inesperada del servidor al comando LOGIN. +unknown imap response from the server. server responded: %s emailadmin es-es Respuesta IMAP desconocida del servidor. El servidor respondió: %s +unsupported action '%1' !!! emailadmin es-es ¡La acción '%1' no está soportada! +update current email address: emailadmin es-es Actualizar la dirección de correo actual: +use ldap defaults emailadmin es-es usar las opciones predeterminadas para LDAP +use predefined username and password defined below emailadmin es-es Usar el usuario predefinido y las contraseñas definidas debajo +use smtp auth emailadmin es-es Usar identificación SMTP +use tls authentication emailadmin es-es Usar identificación TLS +use tls encryption emailadmin es-es Usar cifrado TLS +user can edit forwarding address emailadmin es-es El usuario puede editar la dirección de reenvío +username (standard) emailadmin es-es usuario (estándar) +username/password defined by admin emailadmin es-es Usuario/contraseña definida por el administrador +username@domainname (virtual mail manager) emailadmin es-es usuario@dominio (Gestor de correo virtual) +users can define their own emailaccounts emailadmin es-es Los usuarios pueden definir sus propias cuentas de correo +users can define their own identities emailadmin es-es Los usuarios pueden definir sus propias identidades +users can define their own signatures emailadmin es-es Los usuarios pueden definir sus propias firmas +users can utilize these stationery templates emailadmin es-es Los usuarios pueden utilizar estas plantillas preimpresas +virtual mail manager emailadmin es-es Gestor de correo virtual +you have received a new message on the emailadmin es-es Ha recibido un mensaje nuevo en la +your name emailadmin es-es Su nombre diff --git a/emailadmin/lang/egw_et.lang b/emailadmin/lang/egw_et.lang new file mode 100755 index 0000000000..22db7dda50 --- /dev/null +++ b/emailadmin/lang/egw_et.lang @@ -0,0 +1,50 @@ +account '%1' not found !!! emailadmin et Kontot '%1' ei leitud !!! +add new email address: emailadmin et Lisa uus email aadress +add profile emailadmin et Lisa Profiil +admin password emailadmin et admin parool +admin username emailadmin et admin kasutajanimi +alternate email address emailadmin et Alternatiivne email aadress +bad login name or password. emailadmin et Vale kasutajanimi või parool +continue emailadmin et Jätka +cyrus imap server emailadmin et Cyrus IMAP Server +cyrus imap server administration emailadmin et Cyrus IMAP server administreerimine +default emailadmin et vaikimisi +do not validate certificate emailadmin et ära valideeri sertifikaati +do you really want to delete this profile emailadmin et Tahad tõesti kustutada seda Profiili +domainname emailadmin et Doomeninimi +edit email settings emailadmin et muuda emaili setinguid +email account active emailadmin et email konto aktiivne +email address emailadmin et email aadress +email settings common et Email setingud +encrypted connection emailadmin et krüpteeritud ühendus +encryption settings emailadmin et Krüpteerimise setingud +enter your default mail domain (from: user@domain) emailadmin et Sisesta oma vaikimisi mail doomen (kasutaja@doomen) +entry saved emailadmin et Kirje salvestatud +error saving the entry!!! emailadmin et Viga kirje salvestamisel !!! +global options emailadmin et Globaalsed omadused +imap admin password admin et IMAP admin parool +imap admin user admin et IMAP admin kasutaja +imap c-client version < 2001 emailadmin et IMAP C-Client Versioon < 2001 +imap server emailadmin et IMAP Server +imap server closed the connection. emailadmin et IMAP server sulges ühenduse. +imap server closed the connection. server responded: %s emailadmin et IMAP Server sulges ühenduse. Server Vastas: %s +imap server name emailadmin et imap serveri nimi +imap server port emailadmin et IMAP serveri port +imap/pop3 server name emailadmin et IMAP/POP3 server nimi +ldap settings emailadmin et LDAP setingud +mail settings admin et Mail setingud +no alternate email address emailadmin et pole alternatiivset email aadressi +no encryption emailadmin et ilna krüpteeringutta +organisation emailadmin et Organisatsioon +pop3 server port emailadmin et POP3 serveri port +port emailadmin et port +remove emailadmin et eemalda +select type of imap server emailadmin et vali IMAP serveri tüüp +select type of imap/pop3 server emailadmin et vali IMAP/POP3 serveri tüüp +select type of smtp server emailadmin et Vali SMTP serveri tüüp +server settings emailadmin et Serveri setingud +sieve server port emailadmin et Sieve serveri port +sieve settings emailadmin et Sieve setingud +smtp server name emailadmin et SMTP serveri nimi +smtp settings emailadmin et SMTP setingud +smtp-server port emailadmin et SMTP serveri port diff --git a/emailadmin/lang/egw_eu.lang b/emailadmin/lang/egw_eu.lang new file mode 100644 index 0000000000..d9f6d37844 --- /dev/null +++ b/emailadmin/lang/egw_eu.lang @@ -0,0 +1,31 @@ +advanced options emailadmin eu Aukera aurreratuak +alternate email address emailadmin eu Helbide elektroniko alternatiboa +bad login name or password. emailadmin eu Izen edo pasahitz okerra +default emailadmin eu Lehenetsia +email address emailadmin eu Helbide elektronikoa +encrypted connection emailadmin eu konexioa enkriptatua +entry saved emailadmin eu Sarrera gordeta +error saving the entry!!! emailadmin eu Errorea sarrera gordetzerakoan +imap server emailadmin eu IMAP Zerbitzaria +imap server closed the connection. emailadmin eu IMAP zerbitzariak konexioa itxi du +in mbyte emailadmin eu MByte-sen +leave empty for no quota emailadmin eu zurian utzi kuotarik ez badago +mail settings admin eu Posta elektronikoaren konfigurazioa +no alternate email address emailadmin eu ez da ordezko helbide elektronikorik +no message returned. emailadmin eu Ez da mezurik itzuli +order emailadmin eu Ordena +organisation emailadmin eu antolaketa +port emailadmin eu ataka +postfix with ldap emailadmin eu Postfix-ak LDAP-arekin +quota settings emailadmin eu kuotaren konfigurazioa +quota size in mbyte emailadmin eu kuotaren tamaina MBytes-etan +remove emailadmin eu ezabatu +sieve settings emailadmin eu SIEVE ren lehentasunak +smtp settings emailadmin eu SMTP lehentasunak +standard emailadmin eu estandar +standard imap server emailadmin eu IMAP zerbitzari estandarra +standard pop3 server emailadmin eu POP3 zerbitzari estandarra +standard smtp-server emailadmin eu SMTP zerbitzari estandarra +this php has no imap support compiled in!! emailadmin eu PHParendako IMAPa konpilatu gabe +use smtp auth emailadmin eu SMTP autentifikazioa erabili +users can define their own emailaccounts emailadmin eu Erabiltzaileek euren posta kontuak defini ditzazkete diff --git a/emailadmin/lang/egw_fa.lang b/emailadmin/lang/egw_fa.lang new file mode 100644 index 0000000000..c57c626d22 --- /dev/null +++ b/emailadmin/lang/egw_fa.lang @@ -0,0 +1,71 @@ +add profile emailadmin fa اÙزودن مجموعه تنظیمات +admin dn emailadmin fa dn مدیر +admin password emailadmin fa گذرواژه مدیر +admin username emailadmin fa نام کاربری مدیر +advanced options emailadmin fa تنظیمات پیشرÙته +alternate email address emailadmin fa نشانی پست الکترونیکی دیگر +any application emailadmin fa همه کاربردها +any group emailadmin fa همه گروهها +can be used by application emailadmin fa استÙاده شود توسط کاربرد +can be used by group emailadmin fa استÙاده شود توسط گروه +continue emailadmin fa ادامه +default emailadmin fa پیش Ùرض +deliver extern emailadmin fa حمل بیرونی +do you really want to delete this profile emailadmin fa آیا واقعا Ù…ÛŒ خواهید این مجموعه تنظیمات را حذ٠کنید؟ +domainname emailadmin fa نام حوزه +edit email settings emailadmin fa ویرایش تنظیمات نامه الکترونیکی +email account active emailadmin fa حساب نامه الکترونیکی Ùعال +email address emailadmin fa نشانی الکترونیکی +emailadmin emailadmin fa مدیر رایانامه +enable sieve emailadmin fa Ùعالسازی Sieve +encryption settings emailadmin fa تنظیمات رمز نگاری +enter your default mail domain (from: user@domain) emailadmin fa حوزه پیش Ùرض خود را وارد کنید:(مثلا: fgpars.net) +entry saved emailadmin fa ورودی ذخیره شد +error saving the entry!!! emailadmin fa خطای ذخیره ورودی!!! +forward also to emailadmin fa همچنین ارسال به +forward email's to emailadmin fa ارسال نامه ها به +forward only emailadmin fa Ùقط ارسال به +global options emailadmin fa گزینه های عمومی +imap admin password admin fa گذرواژه مدیر IMAP +imap admin user admin fa کاربر مدیر IMAP +imap server emailadmin fa کارگزار Ø¢ÛŒ مپ +imap server hostname or ip address emailadmin fa نام میزبان یا نشانی IP کارگزار IMAP +imap server logintyp emailadmin fa نوع ورود کارگزار IMAP +imap server port emailadmin fa درگاه کارگزار IMAP +imap/pop3 server name emailadmin fa نام کارگزار IMAP/POP3 +in mbyte emailadmin fa به مگابایت +leave empty for no quota emailadmin fa برای بدون سهمیه بودن، خالی بگذارید +mail settings admin fa تنظیمات نامه +name of organisation emailadmin fa نام سازمان +no alternate email address emailadmin fa بدون نشانی نامه الکترونیکی دیگر +no forwarding email address emailadmin fa بدون نشانی نامه الکترونیکی ارسال به دیگری +order emailadmin fa ترتیب +organisation emailadmin fa سازمان +pop3 server hostname or ip address emailadmin fa نام میزبان یا نشانی IP کارگزار POP3 +pop3 server port emailadmin fa درگاه کارگزار POP3 +profile access rights emailadmin fa حقوق دسترسی مجموعه تنظیمات +profile list emailadmin fa لیست مجموعه تنظیمات +profile name emailadmin fa نام مجموعه تنظیمات +quota settings emailadmin fa تنظیمات سهمیه +quota size in mbyte emailadmin fa سهمیه به مگابایت +remove emailadmin fa حذ٠+select type of imap/pop3 server emailadmin fa نوع کارگزار IMAP/POP3 را انتخاب کنید +select type of smtp server emailadmin fa نوع کارگزار SMTP را انتخاب کنید +server settings emailadmin fa تنظیمات کارگزار +sieve server hostname or ip address emailadmin fa نام میزبان یا نشانی IP کارگزار Sieve +sieve server port emailadmin fa درگاه کارگزار Sieve +sieve settings emailadmin fa تنظیمات Sieve +smtp authentication emailadmin fa تصدیق smtp +smtp server name emailadmin fa نام کارگزار SMTP +smtp settings emailadmin fa تنظیمات smtp +smtp-server hostname or ip address emailadmin fa نشانی IP یا نام میزبان SMTP +smtp-server port emailadmin fa درگاه کارگزار SMTP +standard emailadmin fa استاندارد +standard imap server emailadmin fa کارگزار استاندارد IMAP +standard pop3 server emailadmin fa کارگزار استاندارد POP3 +standard smtp-server emailadmin fa کارگزار استاندارد SMTP +this php has no imap support compiled in!! emailadmin fa این PHP پشتیبانی از Ø¢ÛŒ مپ را در خود ندارد! +use ldap defaults emailadmin fa از پیش Ùرضهای LDAP استÙاده شود +use smtp auth emailadmin fa استÙاده از تصدیق در SMTP +users can define their own emailaccounts emailadmin fa کاربران Ù…ÛŒ توانند حسابهای کاربری را خودشان تعری٠کنند +your name emailadmin fa نام شما diff --git a/emailadmin/lang/egw_fi.lang b/emailadmin/lang/egw_fi.lang new file mode 100644 index 0000000000..6247795801 --- /dev/null +++ b/emailadmin/lang/egw_fi.lang @@ -0,0 +1,157 @@ +%1 entries deleted. emailadmin fi %1 tapahtumaa poistettu +account '%1' not found !!! emailadmin fi Tiliä '%1' ei löytynyt! +active templates emailadmin fi Aktiiviset mallipohjat +add new email address: emailadmin fi Lisää uusi sähköpostiosoite +add profile emailadmin fi Lisää profiili +admin dn emailadmin fi Ylläpitäjän dn +admin password emailadmin fi Ylläpitäjän salasana +admin username emailadmin fi Ylläpitäjän käyttäjätunnus +advanced options emailadmin fi Lisäasetukset +alternate email address emailadmin fi Vaihtoehtoinen sähköpostiosoite +any application emailadmin fi Mikä tahansa sovellus +any group emailadmin fi Mikä tahansa ryhmä +any user emailadmin fi Kuka tahansa käyttäjä +back to admin/grouplist emailadmin fi Takaisin ylläpitoon / Ryhmäluetteloon +back to admin/userlist emailadmin fi Takaisin ylläpitoon / Käyttäjäluetteloon +bad login name or password. emailadmin fi Väärä käyttäjätunnus tai salasana +bad or malformed request. server responded: %s emailadmin fi Väärä tai viallinen pyyntö. %s +bad request: %s emailadmin fi Väärä pyyntö: %s +can be used by application emailadmin fi Sovellukselle +can be used by group emailadmin fi Ryhmälle +can be used by user emailadmin fi Käyttäjälle +connection dropped by imap server. emailadmin fi Yhteys IMAP palvelimeen katkesi. +continue emailadmin fi Jatka +could not complete request. reason given: %s emailadmin fi Pyyntöä ei voitu toteuttaa. %s +could not open secure connection to the imap server. %s : %s. emailadmin fi Turvattua yhteyttä IMAP palvelimeen ei voitu avata. %s : %s. +cram-md5 or digest-md5 requires the auth_sasl package to be installed. emailadmin fi CRAM-MD5 tai DIGEST-MD5 käyttö edellyttää Auth_SASL paketin asentamista. +cyrus imap server emailadmin fi Cyrus IMAP -palvelin +cyrus imap server administration emailadmin fi Cyrus IMAP -palvelimen hallinta +default emailadmin fi Oletus +deliver extern emailadmin fi Deliver extern +do not validate certificate emailadmin fi Älä tarkista sertifikaattia +do you really want to delete this profile emailadmin fi Haluatko varmasti poistaa tämän profiilin? +do you really want to reset the filter for the profile listing emailadmin fi Haluatko varmasti uudelleenasettaa suotimen profiililuetteloon? +domainname emailadmin fi Verkkotunnus +edit email settings emailadmin fi Muokkaa sähköpostin asetuksia +email account active emailadmin fi Sähköpostitili käytössä +email address emailadmin fi Sähköpostiosoite +email settings common fi Sähköpostin asetukset +emailadmin emailadmin fi Sähköpostin ylläpito +emailadmin: group assigned profile common fi Sähköpostin ylläpito: Ryhmälle suunnattu profiili +emailadmin: user assigned profile common fi Sähköpostin ylläpito: Käyttäjälle suunnattu profiili +enable cyrus imap server administration emailadmin fi Ota Cyrus IMAP -palvelimen hallinta käyttöön +enable sieve emailadmin fi Ota Sieve käyttöön +encrypted connection emailadmin fi Yhteyden suojaus +encryption settings emailadmin fi Yhteyden suojausasetukset +enter your default mail domain (from: user@domain) emailadmin fi Anna oletusverkkotunnus (käyttäjä@verkkotunnus) +entry saved emailadmin fi Tallennettu +error connecting to imap server. %s : %s. emailadmin fi Virhe yhdistettäessä IMAP palvelimeen. %s : %s. +error connecting to imap server: [%s] %s. emailadmin fi Virhe yhdistettäessä IMAP palvelimeen. [%s] %s. +error deleting entry! emailadmin fi Virhe poistettaessa! +error saving the entry!!! emailadmin fi Virhe tallennettaessa! +filtered by account emailadmin fi Käyttäjätilien mukaan +filtered by group emailadmin fi Ryhmän mukaan +forward also to emailadmin fi Välitä osoitteeseen +forward email's to emailadmin fi Välitä osoitteeseen +forward only emailadmin fi Ainoastaan edelleenlähetys +global options emailadmin fi Yleiset asetukset +if using ssl or tls, you must have the php openssl extension loaded. emailadmin fi Jos SSL tai TLS on käytössä, PHP openssl lisäosa pitää olla ladattuna. +imap admin password admin fi IMAP admin salasana +imap admin user admin fi IMAP admin käyttäjätunnus +imap c-client version < 2001 emailadmin fi IMAP C-Client versio < 2001 +imap server emailadmin fi IMAP -palvelin +imap server closed the connection. emailadmin fi IMAP palvelin katkaisi yhteyden. +imap server closed the connection. server responded: %s emailadmin fi IMAP palvelin katkaisi yhteyden. %s +imap server hostname or ip address emailadmin fi IMAP -palvelimen nimi tai IP-osoite +imap server logintyp emailadmin fi IMAP -palvelimen käyttäjätunnistus +imap server name emailadmin fi IMAP -palvelimen nimi +imap server port emailadmin fi IMAP -palvelimen portti +imap/pop3 server name emailadmin fi IMAP / POP3 -palvelimen nimi +in mbyte emailadmin fi Megatavua +inactive emailadmin fi Ei käytössä +ldap basedn emailadmin fi LDAP basedn +ldap server emailadmin fi LDAP -palvelin +ldap server accounts dn emailadmin fi LDAP -tunnusten DN +ldap server admin dn emailadmin fi LDAP -ylläpidon DN +ldap server admin password emailadmin fi LDAP -hallinnan salasana +ldap server hostname or ip address emailadmin fi LDAP -palvelimen nimi tai IP-osoite +ldap settings emailadmin fi LDAP -asetukset +leave empty for no quota emailadmin fi Jätä tyhjäksi, jos ei rajoiteta +mail settings admin fi Sähköpostin asetukset +manage stationery templates emailadmin fi Hallitse sähköpostin taustakuvamallipohjia +mb used emailadmin fi MB käytetty +name of organisation emailadmin fi Organisaation nimi +no alternate email address emailadmin fi Ei vaihtoehtoista osoitetta +no encryption emailadmin fi Ei suojausta +no forwarding email address emailadmin fi Välityksen sähköpostiosoitetta ei löytynyt +no message returned. emailadmin fi Viestiä ei palautettu +no supported imap authentication method could be found. emailadmin fi Tuettua IMAP tunnistustapaa ei löydetty. +order emailadmin fi Järjestys +organisation emailadmin fi Organisaatio +plesk can't rename users --> request ignored emailadmin fi Plesk ei voi nimetä käyttäjiä --> pyyntö hylätty +plesk imap server (courier) emailadmin fi Plesk IMAP palvelin (Courier) +plesk mail script '%1' not found !!! emailadmin fi Plesk sähköpostiskriptiä '%1' ei löydy !!! +plesk requires passwords to have at least 5 characters and not contain the account-name --> password not set!!! emailadmin fi Plesk:n salasanassa pitää olla vähintään 5 merkkiä, eikä se saa olla käyttäjätilin nimi --> salasanaa EI ole asetettu !!! +plesk smtp-server (qmail) emailadmin fi Plesk SMTP-palvelin (Qmail) +pop3 server hostname or ip address emailadmin fi POP3 -palvelimen nimi tai IP-osoite +pop3 server port emailadmin fi POP3 -palvelimen portti +port emailadmin fi Portti +postfix with ldap emailadmin fi Postfix ja LDAP +profile access rights emailadmin fi Profiilin käyttöoikeudet +profile is active emailadmin fi Profiili on aktiivinen +profile list emailadmin fi Profiileiluettelo +profile name emailadmin fi Profiilin nimi +qmaildotmode emailadmin fi qmaildotmode +quota settings emailadmin fi Tallennuskiintiön asetukset +quota size in mbyte emailadmin fi Rajoituksen koko Mb:nä +remove emailadmin fi Poista +reset filter emailadmin fi Poista suodatin +select type of imap server emailadmin fi Valitse IMAP -palvelimen tyyppi +select type of imap/pop3 server emailadmin fi Valitse IMAP / POP3 -palvelimen tyyppi +select type of smtp server emailadmin fi Valitse SMTP -palvelimen tyyppi +send using this email-address emailadmin fi Lähetä käyttäen tätä sähköpostiosoitetta +server settings emailadmin fi Palvelimen asetukset +sieve server hostname or ip address emailadmin fi Sieve -palvelimen nimi tai IP-osoite +sieve server port emailadmin fi Sieve -palvelimen portti +sieve settings emailadmin fi Sieven asetukset +smtp authentication emailadmin fi SMTP -tunnistus +smtp options emailadmin fi SMTP -asetukset +smtp server name emailadmin fi SMTP -palvelimen nimi +smtp settings emailadmin fi SMTP -asetukset +smtp-server hostname or ip address emailadmin fi SMTP -palvelimen nimi tai IP-osoite +smtp-server port emailadmin fi SMTP -palvelimen portti +standard emailadmin fi Vakio +standard imap server emailadmin fi Vakio IMAP -palvelin +standard pop3 server emailadmin fi Vakio POP3 -palvelin +standard smtp-server emailadmin fi Vakio SMTP -palvelin +starts with emailadmin fi Alkaa: +stationery emailadmin fi Sähköpostin taustakuvamallipohjat +the imap server does not appear to support the authentication method selected. please contact your system administrator. emailadmin fi IMAP palvelimelta ei löydy tukea valitulle tunnistusmuodolle, ota yhteyttä järjestelmän pääkäyttäjään. +this php has no imap support compiled in!! emailadmin fi Tämä PHP ei sisällä IMAP tukea!! +to use a tls connection, you must be running a version of php 5.1.0 or higher. emailadmin fi Käyttääksesi TLS yhteyttä, sinulla pitää olla käytössä PHP 5.1.0 tai uudempi versio. +unexpected response from server to authenticate command. emailadmin fi Odottamaton vastaus palvelimen AUTHENTICATE komennolta. +unexpected response from server to digest-md5 response. emailadmin fi Odottamaton vastaus palvelimen Digest-MD5 vastauksesta. +unexpected response from server to login command. emailadmin fi Odottamaton vastaus palvelimen LOGIN komennolta. +unknown imap response from the server. server responded: %s emailadmin fi Tuntematon IMAP vastaus palvelimelta. Palvelin vastasi: %s +unsupported action '%1' !!! emailadmin fi Toiminto, jota ei tueta '%1' !!! +update current email address: emailadmin fi Päivitä nykyinen sähköpostiosoite: +use ldap defaults emailadmin fi Käytä LDAP -oletuksia +use predefined username and password defined below emailadmin fi Käytä esimääriteltyä käyttäjänimeä ja salasanaa +use smtp auth emailadmin fi Käytä SMTP -käyttäjätunnistusta +use tls authentication emailadmin fi Käytä TLS -käyttäjätunnistusta +use tls encryption emailadmin fi Käytä TLS -salausta +use users email-address (as seen in useraccount) emailadmin fi Käytä käyttäjän sähköpostiosoitetta +user can edit forwarding address emailadmin fi Käyttäjä voi muokata välitys osoitetta +userid@domain eg. u1234@domain emailadmin fi käyttäjätunnus@verkkotunnus +username (standard) emailadmin fi Käyttäjätunnus (standardi) +username/password defined by admin emailadmin fi Ylläpidon määrittelemä käyttäjätunnus/salasana +username@domainname (virtual mail manager) emailadmin fi käyttäjätunnus@verkkotunnus (Virtual MAIL ManaGeR) +users can define their own emailaccounts emailadmin fi Käyttäjät voivat määritellä omia sähköpostitilejä +users can define their own identities emailadmin fi Käyttäjät voivat määritellä omia identiteettejä +users can define their own signatures emailadmin fi Käyttäjät voivat määritellä omia allekirjoituksia +users can utilize these stationery templates emailadmin fi Käyttäjät voivat määritellä omia sähköpostin taustakuvamallipohjia +virtual mail manager emailadmin fi Virtual MAIL ManaGeR +yes, use credentials below only for alarms and notifications, otherwise use credentials of current user emailadmin fi Kyllä, Käytä annettua salasanaa vain hälytyksiin ja huomautusviesteihin, muulloin käyttäjän tunnuksia +yes, use credentials of current user or if given credentials below emailadmin fi Kyllä, käyttäjän tunnukset, tai mahdolliset allaolevat tunnukset +you have received a new message on the emailadmin fi Sinulle on uusi viesti +your name emailadmin fi Nimesi diff --git a/emailadmin/lang/egw_fr.lang b/emailadmin/lang/egw_fr.lang new file mode 100644 index 0000000000..aeb7154635 --- /dev/null +++ b/emailadmin/lang/egw_fr.lang @@ -0,0 +1,157 @@ +%1 entries deleted. emailadmin fr %1 éléments supprimés. +account '%1' not found !!! emailadmin fr Le compte %1 n'a pas été trouvé!!! +active templates emailadmin fr Modèles actifs +add new email address: emailadmin fr Ajouter une nouvelle adresse email: +add profile emailadmin fr Ajouter un profil +admin dn emailadmin fr DN administrateur +admin password emailadmin fr Mot de passe administrateur +admin username emailadmin fr Nom d'utilisateur de l'administrateur +advanced options emailadmin fr Options avancées +alternate email address emailadmin fr Adresse email alternative +any application emailadmin fr Toutes les applications +any group emailadmin fr Tous les groupes +any user emailadmin fr Tous les utilisateurs +back to admin/grouplist emailadmin fr Retour à l'admin / liste des groupes +back to admin/userlist emailadmin fr Retour à l'admin / liste des utilisateurs +bad login name or password. emailadmin fr ID login ou mot de passe erroné +bad or malformed request. server responded: %s emailadmin fr Requête invalide ou erronnée. Réponse serveur: %s +bad request: %s emailadmin fr Requête invalide: %s +can be used by application emailadmin fr Peut être utilisée par application +can be used by group emailadmin fr Peut être utilisée par un groupe +can be used by user emailadmin fr Peut être utilisée par un utilisateur +connection dropped by imap server. emailadmin fr Connexion interrompue par le serveur IMAP. +continue emailadmin fr Continuer +could not complete request. reason given: %s emailadmin fr Impossible d'effectuer la requête. Raison invoquée: %s +could not open secure connection to the imap server. %s : %s. emailadmin fr Impossible d'ouvrir la connexion sécurisée avec le serveur IMAP. %s: %s +cram-md5 or digest-md5 requires the auth_sasl package to be installed. emailadmin fr CRAM-MD5 ou DIGEST-MD5 requiert l'installation du progiciel Auth_SASL +cyrus imap server emailadmin fr Serveur Cyrus IMAP +cyrus imap server administration emailadmin fr Administration du serveur Cyrus IMAP +default emailadmin fr défaut +deliver extern emailadmin fr Relais de messagerie +do not validate certificate emailadmin fr ne pas valider le certificat +do you really want to delete this profile emailadmin fr Voulez-vous vraiment supprimer ce profil ? +do you really want to reset the filter for the profile listing emailadmin fr Voulez-vous vraiment réinitialiser le filtre pour le listage des profils ? +domainname emailadmin fr Nom de domaine +edit email settings emailadmin fr Modifier les paramètres de messagerie +email account active emailadmin fr Compte de messagerie actif +email address emailadmin fr Adresse de messagerie +email settings common fr Paramètres de messagerie +emailadmin emailadmin fr Administration de la messagerie +emailadmin: group assigned profile common fr eMailAdmin : profil assigné par groupe +emailadmin: user assigned profile common fr eMailAdmin : profil assigné par utilisateur +enable cyrus imap server administration emailadmin fr Activer la gestion du serveur Cyrus IMAP +enable sieve emailadmin fr Activer Sieve +encrypted connection emailadmin fr connexion chiffrée +encryption settings emailadmin fr Paramètres de chiffrement +enter your default mail domain (from: user@domain) emailadmin fr Introduisez votre domaine par défaut (utilisateur@domaine.com) +entry saved emailadmin fr Entrée enregistrée +error connecting to imap server. %s : %s. emailadmin fr Erreur de connexion avec le serveur IMAP. %s: %s. +error connecting to imap server: [%s] %s. emailadmin fr Erreur de connexion avec le serveur IMAP. [%s] %s. +error deleting entry! emailadmin fr Erreur à la suppression de l'entrée ! +error saving the entry!!! emailadmin fr Erreur à l'enregistrement de l'entrée ! +filtered by account emailadmin fr Filtrage par compte +filtered by group emailadmin fr Filtrage par groupe +forward also to emailadmin fr Transférer aussi à +forward email's to emailadmin fr Transférer les emails à +forward only emailadmin fr Seulement transférer +global options emailadmin fr Options globales +if using ssl or tls, you must have the php openssl extension loaded. emailadmin fr Si vous utilisez SSl ou TLS, vous devez avoir chargé l'extension PHP openssl +if you specify port 5190 as sieve server port, you enforce ssl for sieve (server must support that) emailadmin fr Si vous spécifiez le port 5190 comme port du serveur SIEVE, vous forcer le SSL pour SIEVE (et le serveur doit le supporter...) +imap admin password admin fr Mot de passe de l'administrateur IMAP +imap admin user admin fr ID administrateur IMAP +imap c-client version < 2001 emailadmin fr IMAP C-Client Version < 2001 +imap server emailadmin fr Serveur IMAP +imap server closed the connection. emailadmin fr Le serveur IMAP a interrompu la connexion. +imap server closed the connection. server responded: %s emailadmin fr Le serveur IMAP a interrompu la connexion. Réponse du serveur: %s. +imap server hostname or ip address emailadmin fr Nom du serveur IMAP ou adresse IP +imap server logintyp emailadmin fr Type d'authentification IMAP +imap server name emailadmin fr Nom du serveur IMAP +imap server port emailadmin fr Port IMAP +imap/pop3 server name emailadmin fr Nom du serveur IMAP/POP3 +in mbyte emailadmin fr en Mo +inactive emailadmin fr Inactif +ldap basedn emailadmin fr LDAP DN de base +ldap server emailadmin fr LDAP Serveur +ldap server accounts dn emailadmin fr LDAP DN contenant les comptes utilisateurs +ldap server admin dn emailadmin fr LDAP DN administrateur +ldap server admin password emailadmin fr LDAP Mot de passe administateur +ldap server hostname or ip address emailadmin fr LDAP Nom du serveur ou adresse IP +ldap settings emailadmin fr LDAP Paramètres +leave empty for no quota emailadmin fr Laisser vide pour ne pas avoir de quota +mail settings admin fr Paramètres de messagerie +mb used emailadmin fr Mo utilisés +name of organisation emailadmin fr Nom de l'organisation +no alternate email address emailadmin fr Pas d'adresse email alternative +no encryption emailadmin fr Pas de chiffrement +no forwarding email address emailadmin fr Pas d'adresse email de transfert +no message returned. emailadmin fr Aucun message n'est retourné. +no supported imap authentication method could be found. emailadmin fr Il n'a été trouvé aucune méthode d'authentification IMAP supportée +order emailadmin fr Ordre +organisation emailadmin fr Organisation +plesk can't rename users --> request ignored emailadmin fr Plesk ne peut pas renommer les utilisateurs --> requête ignorée +plesk imap server (courier) emailadmin fr Serveur IMAP Plesk (Courier) +plesk mail script '%1' not found !!! emailadmin fr Le script email Plesk '%1' introuvable!!! +plesk requires passwords to have at least 5 characters and not contain the account-name --> password not set!!! emailadmin fr Plesk requiert des mots de passe d'au moins 5 caractères qui ne comprennent pas le nom du compte --> le mot de passe n'est PAS fixé!!! +plesk smtp-server (qmail) emailadmin fr Serveur SMTP Plesk (Qmail) +pop3 server hostname or ip address emailadmin fr Nom d'hôte ou adresse IP du serveur POP3 +pop3 server port emailadmin fr Port du serveur POP3 +port emailadmin fr port +postfix with ldap emailadmin fr Postfix avec support LDAP +profile access rights emailadmin fr Droits d'accès du profil +profile is active emailadmin fr Le profil est actif +profile list emailadmin fr Liste des profils +profile name emailadmin fr Nom de profil +qmaildotmode emailadmin fr qmaildotmode +quota settings emailadmin fr Paramètres de quota +quota size in mbyte emailadmin fr Taille des quota en Mo +remove emailadmin fr Supprimer +reset filter emailadmin fr Réinitialiser le filtre +select type of imap server emailadmin fr Sélectionner le type de serveur IMAP +select type of imap/pop3 server emailadmin fr Sélectionner le type de serveur IMAP/POP3 +select type of smtp server emailadmin fr Sélectionner le type de serveur SMTP +send using this email-address emailadmin fr Envoyer en utilisant cette adresse email +server settings emailadmin fr Configuration du serveur +sieve server hostname or ip address emailadmin fr Nom ou adresse IP du serveur Sieve +sieve server port emailadmin fr Port Sieve +sieve settings emailadmin fr Paramètres Sieve +smtp authentication emailadmin fr Authentication SMTP +smtp options emailadmin fr Options SMTP +smtp server name emailadmin fr Nom du serveur SMTP +smtp settings emailadmin fr Paramètres SMTP +smtp-server hostname or ip address emailadmin fr Nom ou adresse IP du serveur SMTP +smtp-server port emailadmin fr Port SMTP +standard emailadmin fr Standard +standard imap server emailadmin fr Serveur IMAP standard +standard pop3 server emailadmin fr Serveur POP3 standard +standard smtp-server emailadmin fr Serveur SMTP standard +starts with emailadmin fr commence par +stationery emailadmin fr Entrepôt +the imap server does not appear to support the authentication method selected. please contact your system administrator. emailadmin fr Le serveur IMAP ne supporterait pas la méthode d'authentication sélectionnée. Veuillez contacter votre administrateur système. +this php has no imap support compiled in!! emailadmin fr Ce PHP n'est pas compilé avec le support de l'IMAP !! +to use a tls connection, you must be running a version of php 5.1.0 or higher. emailadmin fr Pour utiliser une connexion TLS, vous devez utiliser une version PHP 5.1.0 ou supérieure. +unexpected response from server to authenticate command. emailadmin fr Réponse inattendue du serveur à la commande AUTHENTICATE. +unexpected response from server to digest-md5 response. emailadmin fr Réponse inattendue du serveur à la réponse Digest-MD5. +unexpected response from server to login command. emailadmin fr Réponse inattendue du serveur à la commande LOGIN. +unknown imap response from the server. server responded: %s emailadmin fr Réponse IMAP inconnue du serveur. Le serveur a répondu: %s +unsupported action '%1' !!! emailadmin fr Action '%1' non supportée ! +update current email address: emailadmin fr Mettre à jour l'adresse email actuelle : +use ldap defaults emailadmin fr Utiliser les paramètres LDAP par défaut +use predefined username and password defined below emailadmin fr Utiliser les login/mots de passe pré-définis ci-dessous +use smtp auth emailadmin fr Utiliser l'authentification SMTP +use tls authentication emailadmin fr Utiliser l'authentification TLS +use tls encryption emailadmin fr Utiliser le cryptage TLS +use users email-address (as seen in useraccount) emailadmin fr Utiliser les adresses email des utilisateurs, tel que défini dans leur profil de compte +user can edit forwarding address emailadmin fr L'utilisateur peut modifier l'adresse de transfert. +userid@domain eg. u1234@domain emailadmin fr UserId@domain ie. u1234@domain +username (standard) emailadmin fr nom de l'utilisateur (standard) +username/password defined by admin emailadmin fr Login / mot de passe définis par l'administrateur +username@domainname (virtual mail manager) emailadmin fr utilisateur@domaine (Virtual MAIL ManaGeR) +users can define their own emailaccounts emailadmin fr Les utilisateurs peuvent définir leurs propres comptes de messagerie +users can define their own identities emailadmin fr Les utilisateurs peuvent définir leurs propres identités +users can define their own signatures emailadmin fr les utilisateurs peuvent définir leurs propres signatures +vacation messages with start- and end-date require an admin account to be set emailadmin fr Les messages d'absence avec date de début et de fin doivent être définis par un administrateur ! +virtual mail manager emailadmin fr Virtual MAIL ManaGeR +yes, use credentials below only for alarms and notifications, otherwise use credentials of current user emailadmin fr Oui, utiliser les infos ci-dessous seulement pour les alarmes et les notifications, autrement utiliser les informations du compte utilisateur actif. +yes, use credentials of current user or if given credentials below emailadmin fr Oui, utiliser les informations du compte utilisateur actif ou bien de ces informations ci-dessous si elles sont renseignées +you have received a new message on the emailadmin fr Vous avez reçu un nouveau message sur le +your name emailadmin fr Votre nom diff --git a/emailadmin/lang/egw_hr.lang b/emailadmin/lang/egw_hr.lang new file mode 100755 index 0000000000..29a144c419 --- /dev/null +++ b/emailadmin/lang/egw_hr.lang @@ -0,0 +1,73 @@ +add profile emailadmin hr Add Profile +admin dn emailadmin hr admin dn +admin password emailadmin hr admin password +admin username emailadmin hr admin username +advanced options emailadmin hr advanced options +alternate email address emailadmin hr alternate email address +continue emailadmin hr Continue +cyrus imap server emailadmin hr Cyrus IMAP Server +cyrus imap server administration emailadmin hr Cyrus IMAP server administration +default emailadmin hr default +deliver extern emailadmin hr deliver extern +do you really want to delete this profile emailadmin hr Do you really want to delete this Profile +domainname emailadmin hr domainname +edit email settings emailadmin hr edit email settings +email account active emailadmin hr email account active +email address emailadmin hr email address +enable cyrus imap server administration emailadmin hr enable Cyrus IMAP server administration +enable sieve emailadmin hr enable Sieve +enter your default mail domain (from: user@domain) emailadmin hr Enter your default mail domain (from: user@domain) +entry saved emailadmin hr Entry saved +forward also to emailadmin hr forward also to +forward email's to emailadmin hr forward email's to +forward only emailadmin hr forward only +imap admin password admin hr IMAP admin password +imap admin user admin hr IMAP admin user +imap server emailadmin hr IMAP Poslužitelj +imap server hostname or ip address emailadmin hr IMAP server hostname or ip address +imap server logintyp emailadmin hr IMAP server login type +imap server port emailadmin hr IMAP server port +imap/pop3 server name emailadmin hr IMAP/POP3 server name +in mbyte emailadmin hr in MByte +ldap basedn emailadmin hr LDAP basedn +ldap server emailadmin hr LDAP server +ldap server accounts dn emailadmin hr LDAP server accounts DN +ldap server admin dn emailadmin hr LDAP server admin DN +ldap server admin password emailadmin hr LDAP server admin password +ldap server hostname or ip address emailadmin hr LDAP server hostname or ip address +ldap settings emailadmin hr LDAP settings +leave empty for no quota emailadmin hr leave empty for no quota +mail settings admin hr Mail settings +name of organisation emailadmin hr Name of organization +no alternate email address emailadmin hr no alternate email address +no forwarding email address emailadmin hr no forwarding email address +order emailadmin hr Naredba +organisation emailadmin hr organizacija +pop3 server hostname or ip address emailadmin hr POP3 server hostname or ip address +pop3 server port emailadmin hr POP3 server port +postfix with ldap emailadmin hr Postfix with LDAP +profile list emailadmin hr Profile List +profile name emailadmin hr Profile Name +qmaildotmode emailadmin hr qmaildotmode +quota settings emailadmin hr quota settings +remove emailadmin hr remove +select type of imap/pop3 server emailadmin hr Select type of IMAP/POP3 server +select type of smtp server emailadmin hr Select type of SMTP Server +sieve server hostname or ip address emailadmin hr Sieve server hostname or ip address +sieve server port emailadmin hr Sieve server port +sieve settings emailadmin hr Sieve settings +smtp server name emailadmin hr SMTP server name +smtp settings emailadmin hr Postavke SMTP +smtp-server hostname or ip address emailadmin hr SMTP server hostname or IP address +smtp-server port emailadmin hr SMTP server port +standard emailadmin hr Standard +standard imap server emailadmin hr Standard IMAP server +standard pop3 server emailadmin hr Standard POP3 server +standard smtp-server emailadmin hr Standard SMTP server +this php has no imap support compiled in!! emailadmin hr Ovaj PHP nema ugraÄ‘enu podrÅ¡ku za IMAP!! +use ldap defaults emailadmin hr use LDAP defaults +use smtp auth emailadmin hr Use SMTP auth +use tls authentication emailadmin hr Use TLS authentication +use tls encryption emailadmin hr Use TLS encryption +users can define their own emailaccounts emailadmin hr Users can define their own email accounts +virtual mail manager emailadmin hr Virtual MAIL ManaGeR diff --git a/emailadmin/lang/egw_hu.lang b/emailadmin/lang/egw_hu.lang new file mode 100644 index 0000000000..c6cb141dc1 --- /dev/null +++ b/emailadmin/lang/egw_hu.lang @@ -0,0 +1,149 @@ +account '%1' not found !!! emailadmin hu '%1' felhasználói azonosító nem található! +active templates emailadmin hu Aktív vázlatok +add new email address: emailadmin hu Új email cím hozzáadása: +add profile emailadmin hu Profil hozzáadása +admin dn emailadmin hu Admin dn +admin password emailadmin hu adminisztrátor jelszava +admin username emailadmin hu adminisztrátor neve +advanced options emailadmin hu haladó beállítások +alternate email address emailadmin hu alternatív emailcím +any application emailadmin hu Bármelyik modul +any group emailadmin hu Bármelyik csoport +any user emailadmin hu bármely felhasználó +back to admin/grouplist emailadmin hu Vissza az csoportok adminisztráláshoz +back to admin/userlist emailadmin hu Vissza a felhasználók adminisztráláshoz +bad login name or password. emailadmin hu Hibás belépési név vagy jelszó. +bad or malformed request. server responded: %s emailadmin hu Hibás kérés. Szerver válasza: %s +bad request: %s emailadmin hu Hibás kérés: %s +can be used by application emailadmin hu Ez a modul használhatja +can be used by group emailadmin hu Ez a csoport használhatja +can be used by user emailadmin hu a felhasználó alkalmazhatja +connection dropped by imap server. emailadmin hu A kapcsolatot az IMAP szerver eldobta. +continue emailadmin hu Folytat +could not complete request. reason given: %s emailadmin hu Kérést nem lehet teljesíteni. Az ok: %s +could not open secure connection to the imap server. %s : %s. emailadmin hu Nem létesíthetÅ‘ titkos csatorna az IMAP szerverhez. %s : %s +cram-md5 or digest-md5 requires the auth_sasl package to be installed. emailadmin hu CRAM-MD5 vagy DIGEST-MD5 használatához az Auth_SASL csomagot telepíteni kell. +cyrus imap server emailadmin hu Cyrus IMAP-kiszolgáló +cyrus imap server administration emailadmin hu Cyrus IMAP-kiszolgáló adminisztrációja +default emailadmin hu Alapértelmezett +deliver extern emailadmin hu külsÅ‘ kézbesítés +do not validate certificate emailadmin hu ne ellenÅ‘rizze a tanúsítványt +do you really want to delete this profile emailadmin hu Valóban törölni kívánja ezt a profilt +do you really want to reset the filter for the profile listing emailadmin hu Valóban törölni szeretnéd a profillista szűrÅ‘t? +domainname emailadmin hu tartománynév +edit email settings emailadmin hu email beállítások szerkesztése +email account active emailadmin hu email azonosító aktív +email address emailadmin hu email cím +email settings common hu Email beállítások +emailadmin emailadmin hu EmailAdmin +emailadmin: group assigned profile common hu eMailAdmin: csoport a profilhoz hozzárendelve +emailadmin: user assigned profile common hu eMailAdmin: felhasználó a profilhoz hozzárendelve +enable cyrus imap server administration emailadmin hu Cyrus IMAP-kiszolgáló adminisztrációjának engedélyezése +enable sieve emailadmin hu Sieve engedélyezése +encrypted connection emailadmin hu titkosított kapcsolat +encryption settings emailadmin hu Titkosítás beállításai +enter your default mail domain (from: user@domain) emailadmin hu Adja meg az alapértelmezett levelezési tartományt (a felhasználó@tartomány-ból) +entry saved emailadmin hu Bejegyzés elmentve +error connecting to imap server. %s : %s. emailadmin hu Hiba történt az IMAP szerverhez csatlakozás közben. %s : %s +error connecting to imap server: [%s] %s. emailadmin hu Hiba történt az IMAP szerverhez csatlakozás közben. [%s ]: %s +error saving the entry!!! emailadmin hu Bejegyzés mentése közben hiba történt!!! +filtered by account emailadmin hu Fiók szerint szűrve +filtered by group emailadmin hu Csoport szerint szűrve +forward also to emailadmin hu továbbítsd ide is +forward email's to emailadmin hu email továbbítása ide +forward only emailadmin hu továbbítás csak ide +global options emailadmin hu Globális opciók +if using ssl or tls, you must have the php openssl extension loaded. emailadmin hu SSL vagy TLS használatához a PHP openssl kiterjesztését telepíteni kell. +imap admin password admin hu IMAP adminisztrátor jelszava +imap admin user admin hu IMAP adminisztrátor felhasználóneve +imap c-client version < 2001 emailadmin hu IMAP C-Client verzió < 2001 +imap server emailadmin hu IMAP szerver +imap server closed the connection. emailadmin hu Az IMAP szerver lezárta a kapcsolatot. +imap server closed the connection. server responded: %s emailadmin hu Az IMAP szerver lezárta a kapcsolatot: %s +imap server hostname or ip address emailadmin hu IMAP szerver hosztneve vagy IP címe +imap server logintyp emailadmin hu IMAP szerver bejelentkezési típusa +imap server name emailadmin hu IMAP szerver neve +imap server port emailadmin hu IMAP szerver portja +imap/pop3 server name emailadmin hu IMAP/POP3 szerver neve +in mbyte emailadmin hu MBájtban +inactive emailadmin hu inaktív +ldap basedn emailadmin hu LDAP basedn +ldap server emailadmin hu LDAP szerver +ldap server accounts dn emailadmin hu LDAP szerver accounts DN +ldap server admin dn emailadmin hu LDAP szerver admin DN +ldap server admin password emailadmin hu LDAP szerver adminisztrátorának jelszava +ldap server hostname or ip address emailadmin hu LDAP szerver hosztneve vagy IP címe +ldap settings emailadmin hu LDAP beállítások +leave empty for no quota emailadmin hu hagyja üresen a kvóta figyelmen kívül hagyásához +mail settings admin hu Levelezési beállítások +manage stationery templates emailadmin hu Irodaszer minták kezelése +name of organisation emailadmin hu Szervezet neve +no alternate email address emailadmin hu nincs alternatív email cím +no encryption emailadmin hu titkosítás nélkül +no forwarding email address emailadmin hu nincs továbbküldési email cím +no message returned. emailadmin hu Nincs visszaadott üzenet. +no supported imap authentication method could be found. emailadmin hu Nem található támogatott IMAP hitelesítés. +order emailadmin hu Rendezés +organisation emailadmin hu Szervezet +plesk can't rename users --> request ignored emailadmin hu A Plesk nem tudja átnevezni a felhasználókat -> kérés figyelmen kívül hagyva +plesk imap server (courier) emailadmin hu Plesk IMAP Szerver (Courier) +plesk mail script '%1' not found !!! emailadmin hu Plesk levél szkript '%1' nem található!!! +plesk requires passwords to have at least 5 characters and not contain the account-name --> password not set!!! emailadmin hu Plesk megköveteli, hogy a jelszó legalább 5 karakter legyen és ne tartalmazza a felhasználói nevet --> jelszó beállítása NEM történt meg!!! +plesk smtp-server (qmail) emailadmin hu Plesk SMTP-Szerver (Qmail) +pop3 server hostname or ip address emailadmin hu POP3 szerver hosztneve vagy IP címe +pop3 server port emailadmin hu POP3 szerver portja +port emailadmin hu portcím +postfix with ldap emailadmin hu Postfix LDAP-vel +profile access rights emailadmin hu profil elérési jogosultságok +profile is active emailadmin hu a profil inaktív +profile list emailadmin hu Profil lista +profile name emailadmin hu Profilnév +qmaildotmode emailadmin hu qmaildotmode +quota settings emailadmin hu kvóta beállításai +quota size in mbyte emailadmin hu kvóta Mbájtokban +remove emailadmin hu eltávolítás +reset filter emailadmin hu szűrö törlése +select type of imap server emailadmin hu IMAP szerver típusának kiválasztása +select type of imap/pop3 server emailadmin hu Válassza ki az IMAP/POP3 szerver típusát +select type of smtp server emailadmin hu Válassza ki az SMTP szerver típusát +send using this email-address emailadmin hu Küldés errÅ‘l az e-mail címrÅ‘l +server settings emailadmin hu Szerver beállítások +sieve server hostname or ip address emailadmin hu Sieve szerver hosztneve vagy IP címe +sieve server port emailadmin hu Sieve szerver port +sieve settings emailadmin hu SIEVE beállítások +smtp authentication emailadmin hu SMTP azonosítás +smtp options emailadmin hu SMTP opciók +smtp server name emailadmin hu SMTP szerver neve +smtp settings emailadmin hu SMTP beállítások +smtp-server hostname or ip address emailadmin hu SMTP szerver hosztneve vagy IP címe +smtp-server port emailadmin hu SMTP szerver portja +standard emailadmin hu Szabványos +standard imap server emailadmin hu Szabványos IMAP-kiszolgáló +standard pop3 server emailadmin hu Szabványos POP3-kiszolgáló +standard smtp-server emailadmin hu Szabványos SMTP-kiszolgáló +stationery emailadmin hu Irodaszer +the imap server does not appear to support the authentication method selected. please contact your system administrator. emailadmin hu Az IMAP szerver úgy tűnik nem támogatja a kiválasztott hitelesítést. Lépjen kapcsolatba az adminisztrátorral. +this php has no imap support compiled in!! emailadmin hu A használt PHP verzió nem tartalmaz IMAP támogatást! (telepíteni kell) +to use a tls connection, you must be running a version of php 5.1.0 or higher. emailadmin hu TLS kapcsolat használatához legalább PHP 5.1.0-val kell rendelkeznie. +unexpected response from server to authenticate command. emailadmin hu Váratlan válasz a szervertÅ‘l az AUTHENTICATE parancsra. +unexpected response from server to digest-md5 response. emailadmin hu Váratlan válasz a szervertÅ‘l a Digest-MD5 parancsra. +unexpected response from server to login command. emailadmin hu Váratlan válasz a szervertÅ‘l a LOGIN parancsra. +unknown imap response from the server. server responded: %s emailadmin hu Váratlan válasz a szervertÅ‘l: %s +unsupported action '%1' !!! emailadmin hu Nem támogatott művelet '%1' !!! +update current email address: emailadmin hu Jelenlegi email cím frissítése: +use ldap defaults emailadmin hu használja az LDAP alapbeállításokat +use predefined username and password defined below emailadmin hu Használd a lent megadott felhasználónevet és jelszót +use smtp auth emailadmin hu SMTP hitelesítés használata +use tls authentication emailadmin hu TLS hitelesítés használata +use tls encryption emailadmin hu TLS kódolás használata +user can edit forwarding address emailadmin hu Felhasználó szerkesztheti a továbbításkor a címeket +username (standard) emailadmin hu felhasználó név (standard) +username/password defined by admin emailadmin hu Az adminisztrátok által meghatározott felhasználó és jelszó +username@domainname (virtual mail manager) emailadmin hu felhasználónév@tartománynév (Virtual MAIL ManaGeR) +users can define their own emailaccounts emailadmin hu A felhasználók saját maguk állíthatják be az email postafiókjaikat +users can define their own identities emailadmin hu A felhasználók beállíthatják a saját azonosítójukat +users can define their own signatures emailadmin hu A felhasználók beállíthatják a saját aláírásukat +users can utilize these stationery templates emailadmin hu A felhasználók alkalmazhatják az irodaszer mintákat +virtual mail manager emailadmin hu Virtuális MAIL ManaGeR +you have received a new message on the emailadmin hu Új üzenet érkezett a +your name emailadmin hu Az ön neve diff --git a/emailadmin/lang/egw_id.lang b/emailadmin/lang/egw_id.lang new file mode 100644 index 0000000000..32e0fac9a0 --- /dev/null +++ b/emailadmin/lang/egw_id.lang @@ -0,0 +1,93 @@ +%1 entries deleted. emailadmin id %1 entri dihapus. +account '%1' not found !!! emailadmin id Akoun '%1' tidak ditemukan !!! +active templates emailadmin id Templat yang aktif +add new email address: emailadmin id Tambah alamat email baru: +add profile emailadmin id Tambah Profil +admin dn emailadmin id Admin dn +admin password emailadmin id Password Admin +admin username emailadmin id Nama Admin +advanced options emailadmin id Opsi Canggih +alternate email address emailadmin id Alamat email pengganti +any application emailadmin id Semua aplikasi +any group emailadmin id Semua kelompok +any user emailadmin id semua pengguna +back to admin/grouplist emailadmin id Kembali ke Admin/Daftar Kelompok +back to admin/userlist emailadmin id Kembali ke Admin/Daftar pengguna +bad login name or password. emailadmin id Nama atau password tidak benar. +continue emailadmin id Lanjutkan +could not open secure connection to the imap server. %s : %s. emailadmin id Could not open secure connection to the IMAP server. %s : %s. +cram-md5 or digest-md5 requires the auth_sasl package to be installed. emailadmin id CRAM-MD5 or DIGEST-MD5 requires the Auth_SASL package to be installed. +cyrus imap server emailadmin id Server Cyrus IMAP +cyrus imap server administration emailadmin id Administrasi server Cyrus IMAP +default emailadmin id bawaan +deliver extern emailadmin id pengiriman eksternal +domainname emailadmin id Nama Domain +edit email settings emailadmin id Edit pengaturan email +email account active emailadmin id Akoun Email aktif +email address emailadmin id Alamat Email +email settings common id Pengaturan Email +emailadmin emailadmin id AdminEMail +emailadmin: group assigned profile common id AdminEMail: Profil menurut kelompok +emailadmin: user assigned profile common id AdminEMail: Profil menurut pengguna +enable sieve emailadmin id Bolehkan Sieve +encrypted connection emailadmin id koneksi ter-enkripsi +encryption settings emailadmin id Pengaturan Enkripsi +enter your default mail domain (from: user@domain) emailadmin id Berikan nama domain email anda (dari: pengguna@domain) +entry saved emailadmin id Entri disimpan +error saving the entry!!! emailadmin id Error saving the entry!!! +filtered by account emailadmin id saringan menurut Akoun +filtered by group emailadmin id saringan menurut Kelompok +forward only emailadmin id Hanya Forward +global options emailadmin id Opsi Global +imap admin password admin id password admin IMAP +imap admin user admin id admin IMAP +imap c-client version < 2001 emailadmin id IMAP C-Client Version < 2001 +imap server emailadmin id IMAP Server +in mbyte emailadmin id dalam MByte +inactive emailadmin id tidak aktif +ldap basedn emailadmin id LDAP basedn +ldap server emailadmin id LDAP server +ldap settings emailadmin id Pengaturan LDAP +leave empty for no quota emailadmin id kosongkan bila tanpa kuota +mail settings admin id Pengaturan Mail +name of organisation emailadmin id Nama Organisasi +no alternate email address emailadmin id tanpa alamat email pengganti +no encryption emailadmin id tanpa enkripsi +order emailadmin id Urutan +organisation emailadmin id Organisasi +port emailadmin id port +postfix with ldap emailadmin id Postfix with LDAP +profile list emailadmin id Daftar Profil +profile name emailadmin id Nama Profil +qmaildotmode emailadmin id qmaildotmode +quota settings emailadmin id Pengaturan Kuota +quota size in mbyte emailadmin id ukuran kuota dalam MByte +remove emailadmin id Buang +reset filter emailadmin id ulangi penyaringan +select type of imap server emailadmin id Pilih tipe Server IMAP +select type of imap/pop3 server emailadmin id Pilih tipe Server IMAP/POP3 +select type of smtp server emailadmin id Pilih tipe Server SMTP +send using this email-address emailadmin id kirim menggunakan alamat eMail ini +server settings emailadmin id Pengaturan Server +sieve server hostname or ip address emailadmin id Sieve server hostname or ip address +sieve server port emailadmin id Sieve server port +sieve settings emailadmin id Pengaturan Sieve +smtp authentication emailadmin id Otentikasi SMTP +smtp options emailadmin id Opsi SMTP +smtp server name emailadmin id Nama server SMTP +smtp settings emailadmin id Pengaturan SMTP +smtp-server hostname or ip address emailadmin id Nama atau alamat IP server SMTP +smtp-server port emailadmin id Port server SMTP +standard emailadmin id Standar +standard imap server emailadmin id Server IMAP Standar +standard pop3 server emailadmin id Server POP3 Standar +standard smtp-server emailadmin id Server SMTP Standar +starts with emailadmin id diawali dengan +stationery emailadmin id Stationery +unexpected response from server to login command. emailadmin id Unexpected response from server to LOGIN command. +update current email address: emailadmin id Memperbarui alamat email saat ini: +username (standard) emailadmin id namapengguna (standar) +username/password defined by admin emailadmin id Namapengguna/Password dibuat oleh admin +username@domainname (virtual mail manager) emailadmin id username@domainname (Virtual MAIL ManaGeR) +virtual mail manager emailadmin id Virtual MAIL ManaGeR +your name emailadmin id Your name diff --git a/emailadmin/lang/egw_it.lang b/emailadmin/lang/egw_it.lang new file mode 100644 index 0000000000..13ed9168c0 --- /dev/null +++ b/emailadmin/lang/egw_it.lang @@ -0,0 +1,156 @@ +%1 entries deleted. emailadmin it %1 inserimenti cancellati +account '%1' not found !!! emailadmin it Account '%1' non trovato !!! +active templates emailadmin it Modelli attivi +add new email address: emailadmin it Aggiungi un nuovo indirizzo di posta elettronica +add profile emailadmin it Aggiungi Profilo +admin dn emailadmin it dn amministratore +admin password emailadmin it password amministratore +admin username emailadmin it username amministratore +advanced options emailadmin it opzioni avanzate +alternate email address emailadmin it indirizzo email alternativo +any application emailadmin it ogni applicazione +any group emailadmin it ogni gruppo +any user emailadmin it Qualsiasi utente +back to admin/grouplist emailadmin it Indietro alla lista Admin / Group +back to admin/userlist emailadmin it Indietro alla lista Admin / User +bad login name or password. emailadmin it Nome utente o password errati. +bad or malformed request. server responded: %s emailadmin it Richiesta errata o mal composta. Il Server ha risposto: %s +bad request: %s emailadmin it Richiesta errata: %s +can be used by application emailadmin it può essere usato da applicazione +can be used by group emailadmin it può essere usato da gruppo +can be used by user emailadmin it può essere usato dall'utente +connection dropped by imap server. emailadmin it Connessione interrotta dal sever IMAP. +continue emailadmin it Continua +could not complete request. reason given: %s emailadmin it Impossibile completare la richiesta. Motivazione data: %s +could not open secure connection to the imap server. %s : %s. emailadmin it Non è possibile aprire una connessione sicura al server IMAP. %s : %s. +cram-md5 or digest-md5 requires the auth_sasl package to be installed. emailadmin it CRAM-MD5 o DIGEST-MD5 richiede che il pacchetto Auth_SASL sia installato. +cyrus imap server emailadmin it Server IMAP Cyrus +cyrus imap server administration emailadmin it Amministrazione Server IMAP Cyrus +default emailadmin it predefinito +deliver extern emailadmin it Consegna esterna +do not validate certificate emailadmin it Non convalidare il certificato +do you really want to delete this profile emailadmin it Vuoi davvero cancellare questo profilo? +do you really want to reset the filter for the profile listing emailadmin it Vuoi davvero reimpostare il filtro per l'elenco dei profili? +domainname emailadmin it nome dominio +edit email settings emailadmin it modifica impostazioni email +email account active emailadmin it account email attivo +email address emailadmin it indirizzo email +email settings common it Impostazioni email +emailadmin emailadmin it Gestione Email +emailadmin: group assigned profile common it eMailAdmin: Profilo assegnato al gruppo +emailadmin: user assigned profile common it eMailAdmin: Profilo assegnato all'utente +enable cyrus imap server administration emailadmin it abilita amministrazione Server IMAP Cyrus +enable sieve emailadmin it abilita Sieve +encrypted connection emailadmin it Connessione criptata +encryption settings emailadmin it impostazioni cifratura +enter your default mail domain (from: user@domain) emailadmin it Inserisci il tuo dominio di posta predefinito (da: utente@dominio) +entry saved emailadmin it Inserimento salvato +error connecting to imap server. %s : %s. emailadmin it Errore in connessione al server IMAP. %s : %s. +error connecting to imap server: [%s] %s. emailadmin it Errore in connessione al server IMAP: [%s] %s. +error deleting entry! emailadmin it Errore durante l'eliminazione dell'inserimento! +error saving the entry!!! emailadmin it Errore durante il salvataggio dell'inserimento +filtered by account emailadmin it Filtrati per account +filtered by group emailadmin it Filtrati per gruppo +forward also to emailadmin it Inoltra anche a +forward email's to emailadmin it Inoltra email a +forward only emailadmin it Inoltra solo +global options emailadmin it Opzioni globali +if using ssl or tls, you must have the php openssl extension loaded. emailadmin it Se stai usando SSL oppure TLS, devi aver caricato l'estensione SSL in PHP +imap admin password admin it Password amministratore IMAP +imap admin user admin it User amministratore IMAP +imap c-client version < 2001 emailadmin it Versione C-Cliente < 2001 +imap server emailadmin it Server IMAP +imap server closed the connection. emailadmin it Il server IMAP ha chiuso la connessione +imap server closed the connection. server responded: %s emailadmin it Il server IMAP ha chiuso la connessione. Risposta del server: %s +imap server hostname or ip address emailadmin it Nome Host o IP del server IMAP +imap server logintyp emailadmin it Tipo login server IMAP +imap server name emailadmin it Nome del server IMAP +imap server port emailadmin it Porta server IMAP +imap/pop3 server name emailadmin it Nome server IMAP/POP3 +in mbyte emailadmin it in MByte +inactive emailadmin it Inattivo +ldap basedn emailadmin it LDAP basedn +ldap server emailadmin it server LDAP +ldap server accounts dn emailadmin it DN account server LDAP +ldap server admin dn emailadmin it DN amministratore server LDAP +ldap server admin password emailadmin it password amministratore server LDAP +ldap server hostname or ip address emailadmin it Nome host o IP server LDAP +ldap settings emailadmin it impostazioni LDAP +leave empty for no quota emailadmin it lascia vuoto per nessuna quota +mail settings admin it Impostazioni Posta +manage stationery templates emailadmin it Amministra i modelli +name of organisation emailadmin it Nome dell'organizzazione +no alternate email address emailadmin it nessun indirizzo email alternativo +no encryption emailadmin it Nessuna cifratura +no forwarding email address emailadmin it nessun indirizzo email di inoltro +no message returned. emailadmin it nessun messaggio ricevuto +no supported imap authentication method could be found. emailadmin it Non è stato trovato alcun metodo di autenticazione supportato da IMAP +order emailadmin it ordine +organisation emailadmin it organizzazione +plesk can't rename users --> request ignored emailadmin it Plesk non può rinominare gli utenti --> richiesta ignorata +plesk imap server (courier) emailadmin it IMAP server Plesk (Courier) +plesk mail script '%1' not found !!! emailadmin it Script Plesk '%1' non trovato +plesk requires passwords to have at least 5 characters and not contain the account-name --> password not set!!! emailadmin it Plesk richiede password con almeno 5 caratteri e che non deve contenere il nome account --> password NON impostata +plesk smtp-server (qmail) emailadmin it Server SMTP Plesk (Qmail) +pop3 server hostname or ip address emailadmin it Nome host o IP server POP3 +pop3 server port emailadmin it porta server POP3 +port emailadmin it Porta +postfix with ldap emailadmin it Postfix con LDAP +profile access rights emailadmin it diritti di accesso profilo +profile is active emailadmin it Il profilo è attivo +profile list emailadmin it Elenco Profili +profile name emailadmin it Nome Profilo +qmaildotmode emailadmin it qmaildotmode +quota settings emailadmin it impostazioni quota +quota size in mbyte emailadmin it dimensione quota in MByte +remove emailadmin it rimuovi +reset filter emailadmin it Reimposta filtro +select type of imap server emailadmin it Seleziona il tipo di server IMAP +select type of imap/pop3 server emailadmin it Seleziona il tipo si server IMAP/POP3 +select type of smtp server emailadmin it Seleziona il tipo di server SMTP +send using this email-address emailadmin it Invia utilizzando questo indirizzo email +server settings emailadmin it impostazioni server +sieve server hostname or ip address emailadmin it Nome host o IP server Sieve +sieve server port emailadmin it porta server Sieve +sieve settings emailadmin it impostazioni SIeve +smtp authentication emailadmin it autenticazione smtp +smtp options emailadmin it opzioni smtp +smtp server name emailadmin it nome server SMTP +smtp settings emailadmin it impostazioni smtp +smtp-server hostname or ip address emailadmin it Nome host o IP server SMTP +smtp-server port emailadmin it porta server SMTP +standard emailadmin it Standard +standard imap server emailadmin it Server IMAP standard +standard pop3 server emailadmin it Server POP3 standard +standard smtp-server emailadmin it Server SMTP standard +starts with emailadmin it Comincia con +stationery emailadmin it Modeillo +the imap server does not appear to support the authentication method selected. please contact your system administrator. emailadmin it Il server IMAP sembra non supportare il metodo di autenticazione scelto. Contatta il tuo amministratore di sistema. +this php has no imap support compiled in!! emailadmin it PHP non è stato compilato con supporto IMAP! +to use a tls connection, you must be running a version of php 5.1.0 or higher. emailadmin it Per utilizzare una connessione TLS devi utilizzare una versione PHP 5.1.0 o superiore +unexpected response from server to authenticate command. emailadmin it Risposta inaspettata dal server al comando AUTHENTICATE +unexpected response from server to digest-md5 response. emailadmin it Risposta inaspettata dal server alla risposta Digest-MD5 +unexpected response from server to login command. emailadmin it Risposta inaspettata dal server al comando LOGIN +unknown imap response from the server. server responded: %s emailadmin it Riposta non riconosciuta dal server IMAP %s +unsupported action '%1' !!! emailadmin it Azione non supportata '%1' !!! +update current email address: emailadmin it Aggiorna l'indirizzo email attuale: +use ldap defaults emailadmin it usa predefiniti LDAP +use predefined username and password defined below emailadmin it Utilizza il nome utente e la password definiti qui sotto +use smtp auth emailadmin it usa autenticazione SMTP +use tls authentication emailadmin it usa autenticazione TLS +use tls encryption emailadmin it usa crittografia TLS +use users email-address (as seen in useraccount) emailadmin it Utilizza l'indirizzo email come impostato nell'account utente +user can edit forwarding address emailadmin it l'utente può modificare indirizzo di inoltro +userid@domain eg. u1234@domain emailadmin it UserId@dominio p.es. u1234@dominio +username (standard) emailadmin it Nome utente (standard) +username/password defined by admin emailadmin it Nome utente / password definiti dall'amministratore +username@domainname (virtual mail manager) emailadmin it nomeutente@nome dominio (ManaGeR MAIL Virtuale) +users can define their own emailaccounts emailadmin it Gli utenti possono definire i propri account email +users can define their own identities emailadmin it Gli utenti possono definire le loro identità personali +users can define their own signatures emailadmin it Gli utenti possono definire le loro firme personali +users can utilize these stationery templates emailadmin it Gli utenti possono utilizzare questi modelli +virtual mail manager emailadmin it ManaGeR MAIL Virtuale +yes, use credentials below only for alarms and notifications, otherwise use credentials of current user emailadmin it Sì, utilizza le credenziali sottostanti solo per allarmi e notifiche, altrimenti usa le credenziali dell'utente attuale +yes, use credentials of current user or if given credentials below emailadmin it Sì, utilizza le credenziali dell'utente attuale oppure, se date, e credenziali qui sotto +you have received a new message on the emailadmin it Hai ricevuto un nuovo messaggio sul +your name emailadmin it Il tuo nome diff --git a/emailadmin/lang/egw_iw.lang b/emailadmin/lang/egw_iw.lang new file mode 100755 index 0000000000..78da7329f1 --- /dev/null +++ b/emailadmin/lang/egw_iw.lang @@ -0,0 +1,74 @@ +add profile emailadmin iw הוסף פרופיל +admin dn emailadmin iw של המנהל dn +admin password emailadmin iw סיסמת מנהל +admin username emailadmin iw ×©× ×ž×©×ª×ž×© של המנהל +advanced options emailadmin iw ×ופציות מתקדמות +alternate email address emailadmin iw כתובת דו×ר ×לקטרוני חילופי +continue emailadmin iw המשך +cyrus imap server emailadmin iw Cyrus IMAP שרת +cyrus imap server administration emailadmin iw Cyrus IMAP ניהול שרת +default emailadmin iw ברירת מחדל +deliver extern emailadmin iw מסירה חיצונית +do you really want to delete this profile emailadmin iw בטוח שברצונך למחוק פרופיל ×–×” +domainname emailadmin iw ×©× ×”×“×•×ž×™×™×Ÿ +edit email settings emailadmin iw ערוך הגדרות דו×ר ×לקטרוני +email account active emailadmin iw חשבון דו×ר ×לקטרוני פעיל +email address emailadmin iw כתובת דו×ר ×לקטרוני +enable cyrus imap server administration emailadmin iw Cyrus IMAP ×יפשור ניהול שרת +enable sieve emailadmin iw Sieve ×ישפור +enter your default mail domain (from: user@domain) emailadmin iw (user@domain :ציין ×ת דומיין דו×ר המחדלי שלך (×œ×“×•×’×ž× +entry saved emailadmin iw הרשומה נשמרה +forward also to emailadmin iw להעביר ×’× ×ל +forward email's to emailadmin iw להעביר דו×ר ×לקטרוני ×ל +forward only emailadmin iw העבר בלבד +imap admin password admin iw IMAP סיסמת ניהול +imap admin user admin iw IMAP ×©× ×ž× ×”×œ +imap c-client version < 2001 emailadmin iw IMAP C-Client Version < 2001 +imap server emailadmin iw שרת IMAP +imap server hostname or ip address emailadmin iw שלו IP- ×ו כתובת ×”IMAP ×©× ×©×¨×ª +imap server logintyp emailadmin iw IMAP סוג כניסה לשרת +imap server port emailadmin iw IMAP פורט שרת +imap/pop3 server name emailadmin iw IMAP/POP3 ×©× ×©×¨×ª +in mbyte emailadmin iw MB-ב +ldap basedn emailadmin iw dn הביסי של LDAP +ldap server emailadmin iw LDAP שרת +ldap server accounts dn emailadmin iw LDAP של שרת חשבונות DN +ldap server admin dn emailadmin iw LDAP מנהל שרת של DN +ldap server admin password emailadmin iw LDAP סיסמה של מנהל שרת +ldap server hostname or ip address emailadmin iw IP ×ו כתובת LDAP ×©× ×©×¨×ª +ldap settings emailadmin iw LDAP הגדרות +leave empty for no quota emailadmin iw הש×ר ריק ×œ×œ× ×”×§×¦××” +mail settings admin iw הגדרות דו×ר +name of organisation emailadmin iw ×©× ×”×ירגון +no alternate email address emailadmin iw ×ין כתובת דו×ר ×לקטרוני חלופי +no forwarding email address emailadmin iw ×ין כתובת להעברת דו×ר ×לקטרוני +order emailadmin iw סדר +organisation emailadmin iw ×ירגון +pop3 server hostname or ip address emailadmin iw IP ×ו כתובת pop3 ×©× ×©×¨×ª +pop3 server port emailadmin iw POP3 פורט שרת +postfix with ldap emailadmin iw LDAP ×¢× Postfix +profile list emailadmin iw רשימת ×¤×¨×•×¤×™×œ×™× +profile name emailadmin iw ×©× ×¤×¨×•×¤×™×œ +qmaildotmode emailadmin iw qmaildotmode +quota settings emailadmin iw הגדרות הקצ××” +remove emailadmin iw הסר +select type of imap/pop3 server emailadmin iw IMAP/POP3 בחר סוג שרת +select type of smtp server emailadmin iw SMTP בחר סוג שרת +sieve server hostname or ip address emailadmin iw IP ×ו כתובת Sieve ×©× ×©×¨×ª +sieve server port emailadmin iw Sieve פורט שרת +sieve settings emailadmin iw Sieve הגדרות +smtp server name emailadmin iw SMTP ×©× ×©×¨×ª +smtp settings emailadmin iw הגדרות SMTP +smtp-server hostname or ip address emailadmin iw IP ×ו כתובת SMTP ×©× ×©×¨×ª +smtp-server port emailadmin iw SMTP פורת שרת +standard emailadmin iw תקני +standard imap server emailadmin iw תקני IMAP שרת +standard pop3 server emailadmin iw תקני POP3 שרת +standard smtp-server emailadmin iw תקני SMTP שרת +use ldap defaults emailadmin iw LDAP השתמש בברירות מחדל של +use smtp auth emailadmin iw LDAP השתמש ב×ימות ×ž×©×ª×ž×©×™× ×©×œ +use tls authentication emailadmin iw TLS השתמש ב×ימות +use tls encryption emailadmin iw TLS השתמש בהצפנת +users can define their own emailaccounts emailadmin iw ×ž×©×ª×ž×©×™× ×™×›×•×œ×™× ×œ×”×’×“×™×¨ ×‘×¢×¦×ž× ×ת חשבונות דו×ר ×”×לקטרוני ×©×œ×”× +virtual mail manager emailadmin iw מלהל דו×ר וירטו×לי +your name emailadmin iw ×”×©× ×©×œ×š diff --git a/emailadmin/lang/egw_ja.lang b/emailadmin/lang/egw_ja.lang new file mode 100644 index 0000000000..830ca96e64 --- /dev/null +++ b/emailadmin/lang/egw_ja.lang @@ -0,0 +1,7 @@ +admin password emailadmin ja パスワード +admin username emailadmin ja ユーザID +email settings common ja é›»å­ãƒ¡ãƒ¼ãƒ«è¨­å®š +mail settings admin ja é›»å­ãƒ¡ãƒ¼ãƒ«è¨­å®š +order emailadmin ja é †åº +remove emailadmin ja 削除 +standard emailadmin ja 標準 diff --git a/emailadmin/lang/egw_ko.lang b/emailadmin/lang/egw_ko.lang new file mode 100644 index 0000000000..d6dad69996 --- /dev/null +++ b/emailadmin/lang/egw_ko.lang @@ -0,0 +1,7 @@ +admin password emailadmin ko ê´€ë¦¬ìž ì•”í˜¸ +admin username emailadmin ko ê´€ë¦¬ì‚¬ìš©ìž ì´ë¦„ +continue emailadmin ko ê³„ì† +default emailadmin ko 기본 +mail settings admin ko ë©”ì¼ ì„¤ì • +remove emailadmin ko ì‚­ì œ +standard emailadmin ko 기본 diff --git a/emailadmin/lang/egw_lo.lang b/emailadmin/lang/egw_lo.lang new file mode 100644 index 0000000000..430ff4f538 --- /dev/null +++ b/emailadmin/lang/egw_lo.lang @@ -0,0 +1,24 @@ +advanced options emailadmin lo àºàº²àº™àº•àº±à»‰àº‡àº„່າà»àºšàºšàºžàº´à»€àºªàº” +alternate email address emailadmin lo ທີ່ຢູ່ email ອື່ນ +cyrus imap server emailadmin lo Cyrus IMAP Server +default emailadmin lo ຄ່າເລີ່ມຕົ້ນ +deliver extern emailadmin lo ສົ່ງ extern +edit email settings emailadmin lo à»àºà»‰à»„ຂàºàº²àº™àº•àº±à»‰àº‡àº„່າ email +email account active emailadmin lo email ບັນຊີທີ່ໃຊ້ຢູ່ +email address emailadmin lo ທີ່ຢູ່ email +forward also to emailadmin lo ສົ່ງຕà»à»ˆà»„ປຫາ +forward only emailadmin lo ສົ່ງຕà»à»ˆà»€àº—ົ່ານັ້ນ +in mbyte emailadmin lo ໃນ Mbyte +leave empty for no quota emailadmin lo ອອàºàºˆàº²àºàº¥àº°àºšàº»àºšàºªà»àº²àº¥àº±àºšàºšà»à»ˆàº¡àºµà»‚àºàº•à»‰àº² +mail settings admin lo ຕັ້ງຄ່າ mail +no alternate email address emailadmin lo ບà»à»ˆàº¡àºµ email ສະຫຼັບàºàº±àº™ +order emailadmin lo Order +postfix with ldap emailadmin lo Postfix ດ້ວຠLDAP +qmaildotmode emailadmin lo qmaildotmode +quota settings emailadmin lo ໂàºàº•à»‰àº²àºàº²àº™àº•àº±à»‰àº‡àº„່າ +quota size in mbyte emailadmin lo ຂະໜາດໂàºàº•à»‰àº²à»ƒàº™ ເມàºàº°à»„ບທ +remove emailadmin lo ລຶບອອຠ+standard emailadmin lo ມາດຕະຖານ +standard imap server emailadmin lo ມາດຕະຖານ IMAP ເຊີເວີ້ +standard pop3 server emailadmin lo ມາດຕະຖານ POP3 ເຊີເວີ້ +standard smtp-server emailadmin lo ມາດຕະຖານ SMTP-ເຊີເວີ້ diff --git a/emailadmin/lang/egw_lt.lang b/emailadmin/lang/egw_lt.lang new file mode 100644 index 0000000000..e69de29bb2 diff --git a/emailadmin/lang/egw_lv.lang b/emailadmin/lang/egw_lv.lang new file mode 100644 index 0000000000..51ef268c15 --- /dev/null +++ b/emailadmin/lang/egw_lv.lang @@ -0,0 +1,68 @@ +add profile emailadmin lv Pievienot profilu +admin dn emailadmin lv administratora dn +admin password emailadmin lv administratora parole +admin username emailadmin lv administratora lietotÄjvÄrds +advanced options emailadmin lv uzlabotÄs iespÄ“jas +alternate email address emailadmin lv alternatÄ«vas e-pasta adreses +continue emailadmin lv TurpinÄt +cyrus imap server emailadmin lv Cyrus IMAP serveris +cyrus imap server administration emailadmin lv Cyrus IMAP servera administrÄ“Å¡ana +default emailadmin lv noklusÄ“jums +do you really want to delete this profile emailadmin lv Vai tu tieÅ¡Äm vÄ“lies dzÄ“st Å¡o profilu? +domainname emailadmin lv domÄ“na vÄrds +edit email settings emailadmin lv rediģēt e-pasta uzstÄdÄ«jumus +email account active emailadmin lv e-pasta konts aktÄ«vs +email address emailadmin lv e-pasta adrese +enable cyrus imap server administration emailadmin lv atļaut Cyrus IMAP servera administrÄ“Å¡anu +enable sieve emailadmin lv atļaut Sieve +enter your default mail domain (from: user@domain) emailadmin lv Ievadi noklusÄ“to pasta domÄ“nu (no: lietotÄjs@domÄ“ns) +entry saved emailadmin lv Ieraksts saglabÄts +forward also to emailadmin lv pÄrsÅ«tÄ«t arÄ« +forward email's to emailadmin lv pÄrsÅ«tÄ«t e-pasta vÄ“stules +forward only emailadmin lv tikai pÄrsÅ«tÄ«t +imap admin password admin lv IMAP administratora parole +imap admin user admin lv IMAP administrators +imap c-client version < 2001 emailadmin lv IMAP C-Client Version <2001 +imap server emailadmin lv IMAP serveris +imap server hostname or ip address emailadmin lv IMAP servera hosta vÄrds vai IP adrese +imap server logintyp emailadmin lv IMAP servera autorizÄcijas veids +imap server port emailadmin lv IMAP servera ports +imap/pop3 server name emailadmin lv IMAP/POP3 servera nosaukums +ldap basedn emailadmin lv LDAP bÄzes dn +ldap server emailadmin lv LDAP serveris +ldap server accounts dn emailadmin lv LDAP servera konti DN +ldap server admin dn emailadmin lv LDAP servera administratora DN +ldap server admin password emailadmin lv LDAP servera administratora parole +ldap server hostname or ip address emailadmin lv LDAP servera hosta vÄrds vai IP adrese +ldap settings emailadmin lv LDAP uzstÄdÄ«jumi +mail settings admin lv Pasta uzstÄdÄ«jumi +name of organisation emailadmin lv OrganizÄcijas nosaukums +no alternate email address emailadmin lv nav alternatÄ«vas e-pasta adreses +no forwarding email address emailadmin lv nav pÄrsÅ«tÄmÄs e-pasta adreses +order emailadmin lv KÄrtÄ«ba +pop3 server hostname or ip address emailadmin lv POP3 servera hosta vÄrds vai IP adrese +pop3 server port emailadmin lv POP3 servera ports +profile list emailadmin lv Profila saraksts +profile name emailadmin lv Profila vÄrds +remove emailadmin lv pÄrvietot +select type of imap/pop3 server emailadmin lv AtzÄ«mÄ“t IMAP/POP3 servera tipu +select type of smtp server emailadmin lv AtzÄ«mÄ“t SMTP servera tipu +sieve server hostname or ip address emailadmin lv ?Sieve? servera hosta vÄrds vai IP adrese +sieve server port emailadmin lv Sieve servera ports +sieve settings emailadmin lv Sieve uzstadÄ«jumi +smtp server name emailadmin lv SMTP servera nosaukums +smtp settings emailadmin lv SMTP uzstÄdÄ«jumi +smtp-server hostname or ip address emailadmin lv SMTP servera hosta vÄrds vai IP adrese +smtp-server port emailadmin lv SMTP servera ports +standard emailadmin lv Standarta +standard imap server emailadmin lv Standarta IMAP serveris +standard pop3 server emailadmin lv Standarta POP3 serveris +standard smtp-server emailadmin lv Standarta SMTP serveris +this php has no imap support compiled in!! emailadmin lv Å im PHP nav IMAP nokompilÄ“ts atbalsts +use ldap defaults emailadmin lv lieto LDAP noklusÄ“jumus +use smtp auth emailadmin lv Lieto SMTP autentifikÄciju +use tls authentication emailadmin lv Lieto TLS autentifikÄciju +use tls encryption emailadmin lv Lieto TLS Å¡ifrÄ“Å¡anu +users can define their own emailaccounts emailadmin lv LIetotÄji paÅ¡i var definÄ“t savus e-pasta kontus +virtual mail manager emailadmin lv VIrtuÄlais MAIL ManaGeR +your name emailadmin lv Tavs vÄrds diff --git a/emailadmin/lang/egw_nl.lang b/emailadmin/lang/egw_nl.lang new file mode 100644 index 0000000000..139047d281 --- /dev/null +++ b/emailadmin/lang/egw_nl.lang @@ -0,0 +1,145 @@ +account '%1' not found !!! emailadmin nl Account '%1' niet gevonden !!! +add new email address: emailadmin nl Voeg nieuw emailadres toe: +add profile emailadmin nl Profiel toevoegen +admin dn emailadmin nl admin dn +admin password emailadmin nl Admin wachtwoord +admin username emailadmin nl Admin gebruikersnaam +advanced options emailadmin nl Geavanceerde opties +alternate email address emailadmin nl Alternatief emailadres +any application emailadmin nl Iedere toepassing +any group emailadmin nl Iedere groep +any user emailadmin nl iedere gebruiker +back to admin/grouplist emailadmin nl Terug naar Beheer/Groepslijst +back to admin/userlist emailadmin nl Terug naar Beheer/Gebruikerslijst +bad login name or password. emailadmin nl Ongeldige login of paswoord +bad or malformed request. server responded: %s emailadmin nl Ongeldig of slecht geformuleerde aanvraag. Server reageerde: %s +bad request: %s emailadmin nl Slechte vraag: %s +can be used by application emailadmin nl Kan gebruikt worden door toepassing +can be used by group emailadmin nl Kan gebruikt worden door groep +can be used by user emailadmin nl kan door gebruiker gebruikt worden +connection dropped by imap server. emailadmin nl Verbinding viel uit door IMAP server +continue emailadmin nl Doorgaan +could not complete request. reason given: %s emailadmin nl Kan verzoek niet afmaken. Opgegeven reden: %s +could not open secure connection to the imap server. %s : %s. emailadmin nl Kon geen veilige verbinding openen met de IMAP server. %s : %s +cram-md5 or digest-md5 requires the auth_sasl package to be installed. emailadmin nl CRAM-MDS of DIGEST-MDS vereist de installatie van het Auth_SASL pakket. +cyrus imap server emailadmin nl Cyrus IMAP-server +cyrus imap server administration emailadmin nl Cyrus IMAP-serverbeheer +default emailadmin nl standaard +deliver extern emailadmin nl bezorg extern +do not validate certificate emailadmin nl het certificaat niet valideren +do you really want to delete this profile emailadmin nl Weet u zeker dat u dit profiel wilt verwijderen +do you really want to reset the filter for the profile listing emailadmin nl Wilt u werkelijk het filter voor de profielenlijst opnieuw instellen +domainname emailadmin nl Domeinnaam +edit email settings emailadmin nl Wijzig emailinstellingen +email account active emailadmin nl Emailaccount actief +email address emailadmin nl Emailadres +email settings common nl Emailinstellingen +emailadmin emailadmin nl EmailAdmin +emailadmin: group assigned profile common nl eMailAdmin: Groep heeft profiel toegewezen gekregen +emailadmin: user assigned profile common nl eMailAdmin: Gebruiker heeft profiel toegewezen gekregen +enable cyrus imap server administration emailadmin nl activier Cyrus IMAP serverbeheer +enable sieve emailadmin nl activeer Sieve +encrypted connection emailadmin nl versleutelde verbinding +encryption settings emailadmin nl encryptie instellingen +enter your default mail domain (from: user@domain) emailadmin nl Voer uw standaard emaildomein in (uit: gebruiker@domein) +entry saved emailadmin nl Record opgeslagen +error connecting to imap server. %s : %s. emailadmin nl Fout verbinden met de IMAP server. %s : %s +error connecting to imap server: [%s] %s. emailadmin nl Fout verbinden met de IMAP server: [%s] %s +error saving the entry!!! emailadmin nl Fout bij het bewaren van de invoer!!! +filtered by account emailadmin nl gefilterd op Account +filtered by group emailadmin nl gefilterd op Groep +forward also to emailadmin nl Ook doorsturen naar +forward email's to emailadmin nl Emails doorsturen naar +forward only emailadmin nl Alleen doorsturen +global options emailadmin nl Algemene opties +if using ssl or tls, you must have the php openssl extension loaded. emailadmin nl Indien SSL of TSL wordt gebruikt, moet u de PHP openssl extensie opgeladen hebben +imap admin password admin nl IMAP beheerders wachtwoord +imap admin user admin nl IMAP beheerdersgebruiker +imap c-client version < 2001 emailadmin nl IMAP C-Client Versie < 2001 +imap server emailadmin nl IMAP server +imap server closed the connection. emailadmin nl IMAP server sloot de verbinding +imap server closed the connection. server responded: %s emailadmin nl IMAP Server sloot de verbinding. Server reageerde: %s +imap server hostname or ip address emailadmin nl IMAP-server hostnaam of IP-adres +imap server logintyp emailadmin nl IMAP-server logintype +imap server name emailadmin nl imap server naam +imap server port emailadmin nl IMAP-serverpoort +imap/pop3 server name emailadmin nl IMAP/POP3-servernaam +in mbyte emailadmin nl in MBytes +inactive emailadmin nl inactief +ldap basedn emailadmin nl LDAP basedn +ldap server emailadmin nl LDAP-server +ldap server accounts dn emailadmin nl LDAP server accounts DN +ldap server admin dn emailadmin nl LDAP server admin DN +ldap server admin password emailadmin nl LDAP-server admin wachtwoord +ldap server hostname or ip address emailadmin nl LDAP-serverhostnaam of IP-adres +ldap settings emailadmin nl LDAP-instellingen +leave empty for no quota emailadmin nl Laat leeg voor geen quota +mail settings admin nl Mailinstellingen +name of organisation emailadmin nl Naam van de Organisatie +no alternate email address emailadmin nl geen alternatief emailadres +no encryption emailadmin nl geen versleuteling +no forwarding email address emailadmin nl geen emailadres om naar door te sturen +no message returned. emailadmin nl Geen bericht teruggekomen. +no supported imap authentication method could be found. emailadmin nl Geen ondersteunde IMAP authenticatiemethode kon gevonden worden. +order emailadmin nl Volgorde +organisation emailadmin nl Organisatie +plesk can't rename users --> request ignored emailadmin nl Plesk kan gebruikers niet hernoemen --> verzoek genegeerd +plesk imap server (courier) emailadmin nl Plesk IMAP Server (Courier) +plesk mail script '%1' not found !!! emailadmin nl Plesk mail script '%1' niet gevonden !!! +plesk requires passwords to have at least 5 characters and not contain the account-name --> password not set!!! emailadmin nl Plesk vereist dat wachtwoorden minstens 5 karakters bevatten en niet de accountnaam mogen bevatten --> wachtwoord niet gewijzigd !!! +plesk smtp-server (qmail) emailadmin nl Plesk SMTP-Server (Qmail) +pop3 server hostname or ip address emailadmin nl POP3-server hostnaam of IP-adres +pop3 server port emailadmin nl POP3-serverpoort +port emailadmin nl poort +postfix with ldap emailadmin nl Postfix met LDAP +profile access rights emailadmin nl profiel toegangsrechten +profile is active emailadmin nl profiel is actief +profile list emailadmin nl Profiellijst +profile name emailadmin nl Profielnaam +qmaildotmode emailadmin nl qmaildotmode +quota settings emailadmin nl Quota-installingen +quota size in mbyte emailadmin nl quota grootte in MByte +remove emailadmin nl Verwijderen +reset filter emailadmin nl filter opnieuw instellen +select type of imap server emailadmin nl selecteer IMAP servertype +select type of imap/pop3 server emailadmin nl Selecteer IMAP/POP3-servertype +select type of smtp server emailadmin nl Selecteer SMTP-servertype +server settings emailadmin nl server instellingen +sieve server hostname or ip address emailadmin nl Sieve-serverhostnaam of IP-adres +sieve server port emailadmin nl Sieve-serverpoort +sieve settings emailadmin nl Sieve instellingen +smtp authentication emailadmin nl SMTP authenticatie +smtp options emailadmin nl SMTP opties +smtp server name emailadmin nl SMTP-servernaam +smtp settings emailadmin nl SMTP instellingen +smtp-server hostname or ip address emailadmin nl SMTP-serverhostnaam of IP-adres +smtp-server port emailadmin nl SMTP-serverpoort +standard emailadmin nl Standaard +standard imap server emailadmin nl Standaard IMAP-server +standard pop3 server emailadmin nl Standaard POP3-server +standard smtp-server emailadmin nl Standaard SMTP-server +stationery emailadmin nl stationery +the imap server does not appear to support the authentication method selected. please contact your system administrator. emailadmin nl The IMAP server blijkt geen authenticatiemethode te ondersteunen. Gelieve uw systeembeheerder te contacteren. +this php has no imap support compiled in!! emailadmin nl Deze PHP heeft geen IMAP ondersteuning verzamelt in!! +to use a tls connection, you must be running a version of php 5.1.0 or higher. emailadmin nl Om een TLS verbinding te gebruiken, moet u een versie van PHP 5.1.0 of hoger gebruiken. +unexpected response from server to authenticate command. emailadmin nl Onverwachte reactie van de server om de opdracht te AUTHENTICEREN. +unexpected response from server to digest-md5 response. emailadmin nl Onverwachte reactie van de server op Digest-MD5 reactie. +unexpected response from server to login command. emailadmin nl Onverwachte reactie van de server op LOGIN opdracht. +unknown imap response from the server. server responded: %s emailadmin nl Onverwachte IMAP reactie van de server. Server reageerde: %s +unsupported action '%1' !!! emailadmin nl Niet-ondersteunde actie '%1' +update current email address: emailadmin nl Huidige emailadres bijwerken: +use ldap defaults emailadmin nl gebruik LDAP standaard instellingen +use predefined username and password defined below emailadmin nl Gebruik voorgekozen gebruikersnaam en wachtwoord zoals hieronder is ingesteld +use smtp auth emailadmin nl Gebruik SMTP authenticatie +use tls authentication emailadmin nl Gebruik TLS authenticatie +use tls encryption emailadmin nl Gebruik TLS-encryptie +user can edit forwarding address emailadmin nl Gebruiker kan het doorstuuradres aanpassen +username (standard) emailadmin nl gebruikersnaam (standaard) +username/password defined by admin emailadmin nl Gebruikersnaam/Wachtwoord ingesteld door beheerder +username@domainname (virtual mail manager) emailadmin nl gebruikersnaam@domeinnaam (Virtual MAIL ManaGeR) +users can define their own emailaccounts emailadmin nl Gebruikers kunnen hun eigen emailaccounts definiëren +users can define their own identities emailadmin nl gebruikers kunnen hun eigen identiteit instellen +users can define their own signatures emailadmin nl gebruikers kunnen hun eigen ondertekening instellen +virtual mail manager emailadmin nl Virtual MAIL ManaGeR +you have received a new message on the emailadmin nl U heeft een nieuw bericht ontvangen op de +your name emailadmin nl Uw naam diff --git a/emailadmin/lang/egw_no.lang b/emailadmin/lang/egw_no.lang new file mode 100644 index 0000000000..8cc5c5cab8 --- /dev/null +++ b/emailadmin/lang/egw_no.lang @@ -0,0 +1,77 @@ +add profile emailadmin no Legg til profil +admin dn emailadmin no Admin dn +admin password emailadmin no Admin passord +admin username emailadmin no Admin brukernavn +advanced options emailadmin no Avanserte valg +alternate email address emailadmin no Alternativ e-mailadresse +continue emailadmin no Fortsette +cyrus imap server emailadmin no Cyrus IMAP tjener +cyrus imap server administration emailadmin no Cyrys IMAP tjeneradministrasjon +default emailadmin no standard +deliver extern emailadmin no lever eksternt +do you really want to delete this profile emailadmin no Ønsker du virkelig Ã¥ slette denne profilen +domainname emailadmin no Domenenavn +edit email settings emailadmin no Rediger e-mail oppsett +email account active emailadmin no E-mailkonto aktiv +email address emailadmin no E-mailadresse +enable cyrus imap server administration emailadmin no Tillat Cyrus IMAP-tjener administrasjon +enable sieve emailadmin no Tillat Sieve +enter your default mail domain (from: user@domain) emailadmin no Registrer ditt standard Emaildomene (fra: bruker@domene) +entry saved emailadmin no Registrering lagret +error saving the entry!!! emailadmin no Feil ved lagring av forekomst! +forward also to emailadmin no videresend ogsÃ¥ til +forward email's to emailadmin no videresend e-mailer til +forward only emailadmin no bare videresending +imap admin password admin no IMAP Administrasjonspassord +imap admin user admin no IMAP Administrasjonsbruker +imap c-client version < 2001 emailadmin no IMAP C-klient versjon < 2001 +imap server emailadmin no IMAP Tjener +imap server hostname or ip address emailadmin no Tjenernavn eller IP-adresse for IMAP tjener +imap server logintyp emailadmin no IMAP Tjener pÃ¥loggingstype +imap server port emailadmin no IMAP Tjenerport +imap/pop3 server name emailadmin no IMAP/POP3 tjenernavn +in mbyte emailadmin no i MByte +ldap basedn emailadmin no LDAP basedn +ldap server emailadmin no LDAP Tjener +ldap server accounts dn emailadmin no LDAP Tjenerkonti DN +ldap server admin dn emailadmin no LDAP Tjeneradmin. DN +ldap server admin password emailadmin no LDAP Tjeneradmin. passord +ldap server hostname or ip address emailadmin no Tjenernavn eller IP-adresse for LDAP tjener +ldap settings emailadmin no LDAP Instillinger +leave empty for no quota emailadmin no La stÃ¥ tomt for ingen begrensning. +mail settings admin no E-post innstillinger +name of organisation emailadmin no Navn pÃ¥ organisasjon +no alternate email address emailadmin no ingen alternativ e-mailadresse +no forwarding email address emailadmin no ingen e-mailadresse for videresending +order emailadmin no Ordre +organisation emailadmin no Organisasjon +pop3 server hostname or ip address emailadmin no Tjenernavn eller IP-adresse for POP3 tjener +pop3 server port emailadmin no POP3 Tjenerport +postfix with ldap emailadmin no Postfix med LDAP +profile list emailadmin no Profilliste +profile name emailadmin no Profilnavn +qmaildotmode emailadmin no qmaildotmodus +quota settings emailadmin no Grenseinstillinger +quota size in mbyte emailadmin no Grense Str. i Mbyte +remove emailadmin no fjern +select type of imap/pop3 server emailadmin no Velg type for IMAP/POP3 Tjener +select type of smtp server emailadmin no Velg type SMTP Tjener +sieve server hostname or ip address emailadmin no Tjenernavn eller IP-adresse for Sieve-tjener +sieve server port emailadmin no Sieve tjenerport +sieve settings emailadmin no Sieve innstillinger +smtp server name emailadmin no SMTP Tjenernavn +smtp settings emailadmin no SMTP innstillinger +smtp-server hostname or ip address emailadmin no Tjenernavn eller IP-Adresse for SMTP-Tjener +smtp-server port emailadmin no SMTP Tjenerport +standard emailadmin no standard +standard imap server emailadmin no Standard IMAP tjener +standard pop3 server emailadmin no Standard POP3 tjener +standard smtp-server emailadmin no Standard SMTP tjener +this php has no imap support compiled in!! emailadmin no PHP versjonen har ingen IMAP-støtte kompilert inn +use ldap defaults emailadmin no Bruk LDAP standaroppsett +use smtp auth emailadmin no Bruk SMTP autentisering +use tls authentication emailadmin no Bruk TLS autentisering +use tls encryption emailadmin no Bruk TLS kryptering +users can define their own emailaccounts emailadmin no Brukere kan definere egne e-post kontoer +virtual mail manager emailadmin no Virtuell Mail Manager +your name emailadmin no Ditt navn diff --git a/emailadmin/lang/egw_pl.lang b/emailadmin/lang/egw_pl.lang new file mode 100644 index 0000000000..02d534be56 --- /dev/null +++ b/emailadmin/lang/egw_pl.lang @@ -0,0 +1,150 @@ +account '%1' not found !!! emailadmin pl Konto '%1' nie zostaÅ‚o znalezione! +active templates emailadmin pl Aktywne szablony +add new email address: emailadmin pl Dodaj nowy adres poczty elektronicznej: +add profile emailadmin pl Dodaj profil +admin dn emailadmin pl DN (nazwa wyróżniajÄ…ca) administratora +admin password emailadmin pl HasÅ‚o administratora +admin username emailadmin pl Nazwa użytkownika konta administratora +advanced options emailadmin pl Ustawienia zaawansowane +alternate email address emailadmin pl Alternatywny adres email +any application emailadmin pl Dowolna aplikacja +any group emailadmin pl Dowolna grupa +any user emailadmin pl Dowolny użytkownik +back to admin/grouplist emailadmin pl Powrót do konta administratora/ listy grup +back to admin/userlist emailadmin pl Powrót do konta administratora/ listy użytkowników +bad login name or password. emailadmin pl ZÅ‚a nazwa użytkownika lub hasÅ‚o +bad or malformed request. server responded: %s emailadmin pl NieprawidÅ‚owe lub źle skonstruowane żądane. Serwer odpowiedziaÅ‚: %s +bad request: %s emailadmin pl NieprawidÅ‚owe żądanie: %s +can be used by application emailadmin pl Może być używane przez aplikacjÄ™ +can be used by group emailadmin pl Może byc używane przez grupÄ™ +can be used by user emailadmin pl Może być używane przez użytkownika +connection dropped by imap server. emailadmin pl PoÅ‚Ä…czenie zerwane przez serwer IMAP +continue emailadmin pl Kontunuj +could not complete request. reason given: %s emailadmin pl Nie udaÅ‚o siÄ™ zrealizować żądania. Powód: %s +could not open secure connection to the imap server. %s : %s. emailadmin pl Nie udaÅ‚o siÄ™ stworzyć bezpiecznego poÅ‚Ä…czenia do serwera IMAP. %s : %s. +cram-md5 or digest-md5 requires the auth_sasl package to be installed. emailadmin pl CRAM-MD5 lub DIGEST-MD5 wymagajÄ…, aby pakiet Auth_SASL (Perl) byÅ‚ zainstalowany +cyrus imap server emailadmin pl Serwer IMAP Cyrus +cyrus imap server administration emailadmin pl Administracja serwerem IMAP Cyrus +default emailadmin pl domyÅ›lny +deliver extern emailadmin pl dostarczanie na zewnÄ…trz +do not validate certificate emailadmin pl nie sprawdzaj poprawnoÅ›ci certyfikatu +do you really want to delete this profile emailadmin pl Czy na pewno chcesz usunąć ten profil? +do you really want to reset the filter for the profile listing emailadmin pl Czy na pewno chcesz zresetować filtr w liÅ›cie profilu? +domainname emailadmin pl Nazwa domeny +edit email settings emailadmin pl Edytuj ustawienia poczty elektronicznej +email account active emailadmin pl Konto poczty aktywne +email address emailadmin pl Adres poczty elektronicznej +email settings common pl Ustawienia poczty elektronicznej +emailadmin emailadmin pl Poczta Elektroniczna - Administracja +emailadmin: group assigned profile common pl Administrator Poczty: Grupa przypisana do profilu +emailadmin: user assigned profile common pl Administrator Poczty: użytkownik przypisany do profilu +enable cyrus imap server administration emailadmin pl aktywuj administracjÄ™ serwerem IMAP Cyrus +enable sieve emailadmin pl aktywuj sito (Sieve) +encrypted connection emailadmin pl poÅ‚Ä…czenie szyfrowane +encryption settings emailadmin pl ustawienia szyfrowania +enter your default mail domain (from: user@domain) emailadmin pl Wprowadź domyÅ›lnÄ… domenÄ™ pocztowÄ… (z użytkownik@domena) +entry saved emailadmin pl Wpis zachowany +error connecting to imap server. %s : %s. emailadmin pl BÅ‚Ä…d poÅ‚Ä…czenia do serwera IMAP. %s : %s +error connecting to imap server: [%s] %s. emailadmin pl BÅ‚Ä…d poÅ‚Ä…czenia do serwera IMAP. [%s] %s +error saving the entry!!! emailadmin pl BÅ‚Ä…d przy zachowywaniu wpisu !!! +filtered by account emailadmin pl Filtrowane przez konto +filtered by group emailadmin pl Filtrowane przez grupe +forward also to emailadmin pl PrzeÅ›lij również do +forward email's to emailadmin pl PrzeÅ›lij wiadomoÅ›ci do +forward only emailadmin pl PrzeÅ›lij tylko +global options emailadmin pl Ustawienia globalne +if using ssl or tls, you must have the php openssl extension loaded. emailadmin pl Jeżeli korzystasz z SSL lub TLS, musisz zapewnić obsÅ‚ugÄ™ OpenSSL w PHP. +imap admin password admin pl HasÅ‚o administratora IMAP +imap admin user admin pl Login administratora IMAP +imap c-client version < 2001 emailadmin pl Wersja C-Client IMAP < 2001 +imap server emailadmin pl Serwer IMAP +imap server closed the connection. emailadmin pl Serwer IMAP zakoÅ„czyÅ‚ poÅ‚Ä…czenie. +imap server closed the connection. server responded: %s emailadmin pl Serwer IMAP zakoÅ„czyÅ‚ poÅ‚Ä…czenie. Jego odpowiedź: %s +imap server hostname or ip address emailadmin pl Nazwa hosta lub IP serwera IMAP +imap server logintyp emailadmin pl Sposób logowania do serwera IMAP +imap server name emailadmin pl Nazwa serwera IMAP +imap server port emailadmin pl Port serwera IMAP +imap/pop3 server name emailadmin pl Nazwa serwera IMAP/POP3 +in mbyte emailadmin pl w megabajtach +inactive emailadmin pl nieaktywny +ldap basedn emailadmin pl Bazowy DN w LDAP +ldap server emailadmin pl Serwer LDAP +ldap server accounts dn emailadmin pl DN dla kont w LDAP +ldap server admin dn emailadmin pl DN administratora w LDAP +ldap server admin password emailadmin pl HasÅ‚o administratora serwera LDAP +ldap server hostname or ip address emailadmin pl Nazwa hosta lub IP serwera LDAP +ldap settings emailadmin pl Ustawienia LDAP +leave empty for no quota emailadmin pl zostaw puste aby wyÅ‚Ä…czyć quota +mail settings admin pl Ustawienia poczty elektronicznej +manage stationery templates emailadmin pl ZarzÄ…dzaj szablonami stacjonarnymi +name of organisation emailadmin pl Nazwa lub organizacja +no alternate email address emailadmin pl brak zapasowego adresu e-mail +no encryption emailadmin pl brak szyfrowania +no forwarding email address emailadmin pl brak adresu poczty przesyÅ‚anej +no message returned. emailadmin pl Nie otrzymano wiadomoÅ›ci. +no supported imap authentication method could be found. emailadmin pl Nie znaleziono obsÅ‚ugiwanej metody autentykacji dla IMAP +order emailadmin pl PorzÄ…dek +organisation emailadmin pl Organizacja +plesk can't rename users --> request ignored emailadmin pl Plesk nie może zmienić nazwy użytkowników --> proÅ›ba odrzucona +plesk imap server (courier) emailadmin pl Plesk IMAP Serwea (Kurier) +plesk mail script '%1' not found !!! emailadmin pl Skrypt %1 mail Plesk nie zostaÅ‚ znaleziony!!! +plesk requires passwords to have at least 5 characters and not contain the account-name --> password not set!!! emailadmin pl Plesk wymaga hasÅ‚a skÅ‚adajÄ…cego siÄ™ z przynajmniej piÄ™ciu znaków i nie zawierajÄ…cego nazwy konta --> hasÅ‚o nie ustalone!!! +plesk smtp-server (qmail) emailadmin pl Plesk Serwera SMTP (Qmail) +pop3 server hostname or ip address emailadmin pl Nazwa hosta lub IP serwera POP3 +pop3 server port emailadmin pl Port serwera POP3 +port emailadmin pl port +postfix with ldap emailadmin pl Postfix z LDAP +profile access rights emailadmin pl prawa dostÄ™pu do profilu +profile is active emailadmin pl Profil jest aktywny +profile list emailadmin pl Lista profili +profile name emailadmin pl Nazwa Profilu +qmaildotmode emailadmin pl qmaildotmode +quota settings emailadmin pl ustawnienia quoty +quota size in mbyte emailadmin pl ograniczenie w megabajtach +remove emailadmin pl UsuÅ„ +reset filter emailadmin pl Resetuj filtr +select type of imap server emailadmin pl Wybierz rodzaj serwera IMAP +select type of imap/pop3 server emailadmin pl Wybierz rodzaj serwera IMAP/POP3 +select type of smtp server emailadmin pl Wybierz rodzaj serwera SMTP +send using this email-address emailadmin pl PrzeÅ›lij za pomocÄ… tego adresu e-mail +server settings emailadmin pl Ustawienia serwera +sieve server hostname or ip address emailadmin pl Adres IP lub nazwa Sieve +sieve server port emailadmin pl Port serwera Sieve +sieve settings emailadmin pl Ustawienia Sieve +smtp authentication emailadmin pl Autentykacja SMTP +smtp options emailadmin pl Opcje SMTP +smtp server name emailadmin pl Nazwa serwera SMTP +smtp settings emailadmin pl Ustawienia SMTP +smtp-server hostname or ip address emailadmin pl Nazwa hosta lub IP serwera SMTP +smtp-server port emailadmin pl Port serwera SMTP +standard emailadmin pl Standard +standard imap server emailadmin pl Standardowy serwer IMAP +standard pop3 server emailadmin pl Standardowy serwer POP3 +standard smtp-server emailadmin pl Standardowy serwer SMTP +stationery emailadmin pl Stacjonarny +the imap server does not appear to support the authentication method selected. please contact your system administrator. emailadmin pl Serwer IMAP nie obsÅ‚uguje wskazanej metody autentykacji. ProszÄ™ skontaktować siÄ™ z administratorem. +this php has no imap support compiled in!! emailadmin pl Interpreter PHP nie posiada obsÅ‚ugi IMAP! +to use a tls connection, you must be running a version of php 5.1.0 or higher. emailadmin pl Aby skorzystać z poÅ‚Ä…czenia TLS, musisz posiadać wersjÄ™ PHP 5.1.0 lub wyższÄ…. +unexpected response from server to authenticate command. emailadmin pl Niespodziewana odpowiedź serwera na polecenie AUTHENTICATE. +unexpected response from server to digest-md5 response. emailadmin pl Niespodziewana odpowiedź serwera na zapytanie Digest-MD5 +unexpected response from server to login command. emailadmin pl Niespodziewana odpowiedź serwera na polecenie LOGIN. +unknown imap response from the server. server responded: %s emailadmin pl Nieznana odpowiedź z serwera IMAP. Serwer przesÅ‚aÅ‚: %s +unsupported action '%1' !!! emailadmin pl Operacja nieobsÅ‚ugiwana '%1'! +update current email address: emailadmin pl Uaktualnij bieżący adres pocztowy +use ldap defaults emailadmin pl Skorzystaj z domyÅ›lnych wartoÅ›ci LDAP +use predefined username and password defined below emailadmin pl Skorzystaj z predefiniowanych nazw użytkownika i hasÅ‚a podanego poniżej +use smtp auth emailadmin pl Skorzystaj z autentykacji SMTP +use tls authentication emailadmin pl Skorzystaj z autentykacji TLS +use tls encryption emailadmin pl Skorzystaj z szyfrowania TLS +use users email-address (as seen in useraccount) emailadmin pl użyj adresu użytkownika (taki jak widoczny w opcjach konta) +user can edit forwarding address emailadmin pl Użytkownik może zmieniać adres przekazywania +username (standard) emailadmin pl login (standardowo) +username/password defined by admin emailadmin pl Nazwa uzytkownika/HasÅ‚o okreÅ›lone przez administratora +username@domainname (virtual mail manager) emailadmin pl login@domena (wirtualny zarzÄ…dca kont) +users can define their own emailaccounts emailadmin pl Użytkownicy mogÄ… definiować swoje wÅ‚asne konta poczty elektronicznej +users can define their own identities emailadmin pl Użytkownicy mogÄ… definiować swoje wÅ‚asne tożsamoÅ›ci +users can define their own signatures emailadmin pl Użytkownicy mogÄ… definiować swoje wÅ‚asne podpisy +users can utilize these stationery templates emailadmin pl Użytkownicy mogÄ… użyć tych szablonów stacjonarnych +virtual mail manager emailadmin pl Wirtualny Serwer Pocztowy - zarzÄ…dca +you have received a new message on the emailadmin pl OtrzymaÅ‚eÅ› nowÄ… wiadomość o +your name emailadmin pl Twoje ImÄ™ diff --git a/emailadmin/lang/egw_pt-br.lang b/emailadmin/lang/egw_pt-br.lang new file mode 100644 index 0000000000..7bd63c6bc8 --- /dev/null +++ b/emailadmin/lang/egw_pt-br.lang @@ -0,0 +1,144 @@ +account '%1' not found !!! emailadmin pt-br Conta '%1' não encontrada !!! +add new email address: emailadmin pt-br Adicionar novo e-mail: +add profile emailadmin pt-br Adicionar Perfil +admin dn emailadmin pt-br dn do administrador +admin password emailadmin pt-br senha do administrador +admin username emailadmin pt-br nome de usuário do administrador +advanced options emailadmin pt-br opções avançadas +alternate email address emailadmin pt-br endereço de e-mail alternativo +any application emailadmin pt-br qualquer aplicação +any group emailadmin pt-br qualquer grupo +any user emailadmin pt-br qualquer usuário +back to admin/grouplist emailadmin pt-br Voltar para Administração/Lista de Grupos +back to admin/userlist emailadmin pt-br Voltar para Administração/Lista de Usuários +bad login name or password. emailadmin pt-br Nome de usuário ou senha inválido(s). +bad or malformed request. server responded: %s emailadmin pt-br Solicitação inválida. Resposta do servidor: %s +bad request: %s emailadmin pt-br Solicitação inválida: %s +can be used by application emailadmin pt-br pode ser usado pela aplicação +can be used by group emailadmin pt-br pode ser usado pelo grupo +can be used by user emailadmin pt-br pode ser usado pelo usuário +connection dropped by imap server. emailadmin pt-br Conexão interrompida pelo servidor IMAP. +continue emailadmin pt-br Continuar +could not complete request. reason given: %s emailadmin pt-br Não foi possível completar a solicitação. Resposta do servidor: %s +could not open secure connection to the imap server. %s : %s. emailadmin pt-br Não foi possível abrir conexão segura com o servidor IMAP. %s : %s. +cram-md5 or digest-md5 requires the auth_sasl package to be installed. emailadmin pt-br CRAM-MD5 ou DIGEST-MD5 necessitam que o pacote Auth_SASL esteja instalado. +cyrus imap server emailadmin pt-br Servidor Cyrus IMAP +cyrus imap server administration emailadmin pt-br Administração do Servidor Cyrus IMAP +default emailadmin pt-br padrão +deliver extern emailadmin pt-br entrega externa +do not validate certificate emailadmin pt-br não validar certificado +do you really want to delete this profile emailadmin pt-br Tem certeza que deseja remover esse perfil? +do you really want to reset the filter for the profile listing emailadmin pt-br Tem certeza que deseja reiniciar o filtro para a listagem do perfil +domainname emailadmin pt-br nome do domínio +edit email settings emailadmin pt-br editar configurações de e-mail +email account active emailadmin pt-br conta de e-mail ativa +email address emailadmin pt-br endrereço de e-mail +email settings common pt-br Configurações do E-mail +emailadmin emailadmin pt-br Administração do E-Mail +emailadmin: group assigned profile common pt-br Administração de eMail: Perfil de Grupo +emailadmin: user assigned profile common pt-br Administração de eMail: Perfil de Usuário +enable cyrus imap server administration emailadmin pt-br habilitar administração do Servidor Cyrus IMAP +enable sieve emailadmin pt-br habilitar Sieve +encrypted connection emailadmin pt-br conexão criptografada +encryption settings emailadmin pt-br configurações de criptografia +enter your default mail domain (from: user@domain) emailadmin pt-br Entre com o domínio de e-mail padrão (de: usuario@dominio) +entry saved emailadmin pt-br Registro salvo +error connecting to imap server. %s : %s. emailadmin pt-br Erro conectando ao servidor IMAP. %s : %s. +error connecting to imap server: [%s] %s. emailadmin pt-br Erro conectando ao servidor IMAP: [%s] %s. +error saving the entry!!! emailadmin pt-br Erro salvando o registro!! +filtered by account emailadmin pt-br filtrado por Conta +filtered by group emailadmin pt-br filtrado por Grupo +forward also to emailadmin pt-br encaminhar também para +forward email's to emailadmin pt-br encaminhar mensagens para +forward only emailadmin pt-br encaminhar somente +global options emailadmin pt-br opções globais +if using ssl or tls, you must have the php openssl extension loaded. emailadmin pt-br Se estiver usando SSL ou TLS, você deverá ter a extensão PHP 'openssl' carretada. +imap admin password admin pt-br Senha do administrador IMAP +imap admin user admin pt-br Usuário administrador do IMAP +imap c-client version < 2001 emailadmin pt-br IMAP C-Cliente Versão < 2001 +imap server emailadmin pt-br Servidor IMAP +imap server closed the connection. emailadmin pt-br O servidor IMAP fechou a conexão. +imap server closed the connection. server responded: %s emailadmin pt-br O servidor IMAP fechou a conexão. Resposta do servidor: %s +imap server hostname or ip address emailadmin pt-br Nome ou IP do servidor IMAP +imap server logintyp emailadmin pt-br Tipo de login do servidor IMAP +imap server name emailadmin pt-br nome do servidor imap +imap server port emailadmin pt-br Porta do servidor IMAP +imap/pop3 server name emailadmin pt-br Nome do servidor IMAP/POP3 +in mbyte emailadmin pt-br em Mbytes +inactive emailadmin pt-br inativo +ldap basedn emailadmin pt-br DN Base do LDAP +ldap server emailadmin pt-br Servidor LDAP +ldap server accounts dn emailadmin pt-br Contas DN de servidores LDAP +ldap server admin dn emailadmin pt-br Administrador DN de servidor LDAP +ldap server admin password emailadmin pt-br Senha do administrador DN de servidor LDAP +ldap server hostname or ip address emailadmin pt-br Nome ou endereço IP do servidor LDAP +ldap settings emailadmin pt-br Configurações do LDAP +leave empty for no quota emailadmin pt-br deixe em branco para nenhuma quota +mail settings admin pt-br Configurações de E-Mail +name of organisation emailadmin pt-br Nome da organização +no alternate email address emailadmin pt-br sem conta de e-mail alternativa +no encryption emailadmin pt-br sem criptografia +no forwarding email address emailadmin pt-br sem conta de e-mail para encaminhar +no message returned. emailadmin pt-br Nenhuma mensagem retornada. +no supported imap authentication method could be found. emailadmin pt-br Nenhum método de autenticação IMAP suportado pôde ser encontrado. +order emailadmin pt-br ordem +organisation emailadmin pt-br organização +plesk can't rename users --> request ignored emailadmin pt-br Plesk não pode renomear usuários --> solicitação ignorada +plesk imap server (courier) emailadmin pt-br Servidor IMAP Plesk (Courier) +plesk mail script '%1' not found !!! emailadmin pt-br Script de e-mail Plesk '%1' não encontrado !!! +plesk requires passwords to have at least 5 characters and not contain the account-name --> password not set!!! emailadmin pt-br Plesk exige que senhas tenha no mínimo 5 caracteres e não contenham o nome da conta --> senha NÃO configurada!!! +plesk smtp-server (qmail) emailadmin pt-br Servidor SMTP Plesk (Qmail) +pop3 server hostname or ip address emailadmin pt-br Nome ou endereço IP do servidor POP3 +pop3 server port emailadmin pt-br Porta do servidor POP3 +port emailadmin pt-br porta +postfix with ldap emailadmin pt-br Postfix com LDAP +profile access rights emailadmin pt-br direito de acesso aos perfis +profile is active emailadmin pt-br perfil está ativo +profile list emailadmin pt-br Lista de perfis +profile name emailadmin pt-br Nome do perfil +qmaildotmode emailadmin pt-br modo dos arquivos qmail (.qmail) +quota settings emailadmin pt-br configurações de quota +quota size in mbyte emailadmin pt-br tamanho da cota em MByte +remove emailadmin pt-br remover +reset filter emailadmin pt-br reiniciar filtro +select type of imap server emailadmin pt-br selecione o tipo de servidor IMAP +select type of imap/pop3 server emailadmin pt-br Selecione o tipo de servidor IMAP/POP3 +select type of smtp server emailadmin pt-br Selecione o tipo de servidor SMTP +server settings emailadmin pt-br configurações do servidor +sieve server hostname or ip address emailadmin pt-br Nome ou endereço IP do servidor Sieve +sieve server port emailadmin pt-br Porta do Servidor Sieve +sieve settings emailadmin pt-br Configurações Sieve +smtp authentication emailadmin pt-br autenticação smtp +smtp options emailadmin pt-br opções smtp +smtp server name emailadmin pt-br Nome do Servidor SMTP +smtp settings emailadmin pt-br configurações SMTP +smtp-server hostname or ip address emailadmin pt-br Nome ou endereço IP do servidor SMTP +smtp-server port emailadmin pt-br Porta do servidor SMTP +standard emailadmin pt-br Padrão +standard imap server emailadmin pt-br Servidor IMAP padrão +standard pop3 server emailadmin pt-br Servidor POP3 padrão +standard smtp-server emailadmin pt-br Servidor SMTP padrão +the imap server does not appear to support the authentication method selected. please contact your system administrator. emailadmin pt-br O servidor IMAP não parece suportar o método de autenticação selecionado. Por favor contacte o administrador do seu sistema. +this php has no imap support compiled in!! emailadmin pt-br Este PHP não tem suporte a IMAP compilado nele! +to use a tls connection, you must be running a version of php 5.1.0 or higher. emailadmin pt-br Para usar uma conexão TLS, você deve estar rodando PHP versão 5.1.0 ou maior. +unexpected response from server to authenticate command. emailadmin pt-br Resposta inesperada do servidor para o comando AUTHENTICATE. +unexpected response from server to digest-md5 response. emailadmin pt-br Resposta inesperada do servidor para a resposta Digest-MD5. +unexpected response from server to login command. emailadmin pt-br Resposta inesperada do servidor para o comando LOGIN. +unknown imap response from the server. server responded: %s emailadmin pt-br Resposta desconhecida do servidor IMAP. Resposta do servidor: %s +unsupported action '%1' !!! emailadmin pt-br Ação não suportada: '%1' !!! +update current email address: emailadmin pt-br Atualizar e-mail atual: +use ldap defaults emailadmin pt-br usar padrões LDAP +use predefined username and password defined below emailadmin pt-br Usar usuário e senha pré-definidos abaixo +use smtp auth emailadmin pt-br usar SMTP Autenticado +use tls authentication emailadmin pt-br Usar autenticação TLS +use tls encryption emailadmin pt-br Usar encriptação TLS +user can edit forwarding address emailadmin pt-br usuário pode editar endereço de encaminhamento +username (standard) emailadmin pt-br nome do usuário (padrão) +username/password defined by admin emailadmin pt-br Nome de usuário/Senha definidos pelo administrador +username@domainname (virtual mail manager) emailadmin pt-br nomedousuario@nomedodominio (Gerenciador Virtual Mail) +users can define their own emailaccounts emailadmin pt-br Os usuários podem definir sua próprias contas de correio +users can define their own identities emailadmin pt-br usuários podem definir suas próprias identidades +users can define their own signatures emailadmin pt-br usuários podem definir suas próprias assinaturas +virtual mail manager emailadmin pt-br Gerenciador Virtual Mail +you have received a new message on the emailadmin pt-br Você recebeu uma nova mensagem na +your name emailadmin pt-br Seu nome diff --git a/emailadmin/lang/egw_pt.lang b/emailadmin/lang/egw_pt.lang new file mode 100644 index 0000000000..44b9a75fb8 --- /dev/null +++ b/emailadmin/lang/egw_pt.lang @@ -0,0 +1,90 @@ +add profile emailadmin pt Adicionar perfil +admin dn emailadmin pt DN do administrador +admin password emailadmin pt Senha do administrador +admin username emailadmin pt Nome do utilizador do administrador +advanced options emailadmin pt Opções avançadas +alternate email address emailadmin pt Endereço de correio electrónico alternativo +any application emailadmin pt Qualquer aplicação +any group emailadmin pt Qualquer grupo +can be used by application emailadmin pt Pode ser utilizado pela aplicação +can be used by group emailadmin pt Pode ser utilizado pelo grupo +continue emailadmin pt Continuar +cyrus imap server emailadmin pt Servidor IMAP Cyrus +cyrus imap server administration emailadmin pt Administração do servidor IMAP Cyrus +default emailadmin pt Por omissão +deliver extern emailadmin pt Entrega externa +do you really want to delete this profile emailadmin pt Deseja relamente eliminar este perfil +domainname emailadmin pt Nome de domínio +edit email settings emailadmin pt Editar configurações do correio electrónico +email account active emailadmin pt Conta de correio electrónico activa +email address emailadmin pt Endereço de correio electrónico +enable cyrus imap server administration emailadmin pt Activar administração do servidor IMAP Cyrus +enable sieve emailadmin pt Activar Sieve +encryption settings emailadmin pt Definições de cifragem +enter your default mail domain (from: user@domain) emailadmin pt Insira o seu domínio de correio electrónico por omissão (de: utilizador@domínio) +entry saved emailadmin pt Registo guardado +error saving the entry!!! emailadmin pt Erro ao guardar o registo!!! +forward also to emailadmin pt Reencaminhar também para +forward email's to emailadmin pt Reencaminhar mensagens para +forward only emailadmin pt Reencaminhar apenas +global options emailadmin pt Opções gerais +imap admin password admin pt Senha do administrador IMAP +imap admin user admin pt Utilizador do administrador IMAP +imap c-client version < 2001 emailadmin pt Versão C-Client IMAP < 2001 +imap server emailadmin pt Servidor IMAP +imap server hostname or ip address emailadmin pt Nome do servidor ou endereço IP do servidor IMAP +imap server logintyp emailadmin pt Tipo de acesso do servidor IMAP +imap server port emailadmin pt Porto do servidor IMAP +imap/pop3 server name emailadmin pt Nome do servidor IMAP/POP3 +in mbyte emailadmin pt em MBytes +ldap basedn emailadmin pt Base DN LDAP +ldap server emailadmin pt Servidor LDAP +ldap server accounts dn emailadmin pt DN das contas de servidor LDAP +ldap server admin dn emailadmin pt DN do administrador de servidor LDAP +ldap server admin password emailadmin pt Senha do administrador de servidor LDAP +ldap server hostname or ip address emailadmin pt Nome do servidor ou endereço IP do servidor LDAP +ldap settings emailadmin pt Configurações do LDAP +leave empty for no quota emailadmin pt Deixar vazio para quota sem limites +mail settings admin pt Configurações do correio electrónico +name of organisation emailadmin pt Nome da empresa +no alternate email address emailadmin pt Sem endereço de correio electrónico alternativo +no forwarding email address emailadmin pt Sem endereço de correio electrónico para reencaminhamento +order emailadmin pt Ordem +organisation emailadmin pt Empresa +pop3 server hostname or ip address emailadmin pt Nome do servidor ou endereço IP do servidor POP3 +pop3 server port emailadmin pt Porto de servidor POP3 +postfix with ldap emailadmin pt Postfix com LDAP +profile access rights emailadmin pt Perfil de direitos de acesso +profile list emailadmin pt Lista de perfis +profile name emailadmin pt Nome do perfil +qmaildotmode emailadmin pt Quota do correio electrónico no modo Idot +quota settings emailadmin pt Configurações da quota +quota size in mbyte emailadmin pt tamanho da quota em MBytes +remove emailadmin pt Remover +select type of imap/pop3 server emailadmin pt Seleccionar tipo de servidor IMAP/POP3 +select type of smtp server emailadmin pt Seleccionar tipo de servidor SMTP +server settings emailadmin pt Definições do servidor +sieve server hostname or ip address emailadmin pt Nome do servidor ou endereço IP do servidor Sieve +sieve server port emailadmin pt Porto do servidor Sieve +sieve settings emailadmin pt Configurações do SIeve +smtp authentication emailadmin pt Autenticação SMTP +smtp options emailadmin pt Opções do SMTP +smtp server name emailadmin pt Nome do servidor SMTP +smtp settings emailadmin pt Definições do SMTP +smtp-server hostname or ip address emailadmin pt Nome do servidor ou endereço IP do servidor SMTP +smtp-server port emailadmin pt Porto do servidor SMTP +standard emailadmin pt Por omissão +standard imap server emailadmin pt Servidor IMAP por omissão +standard pop3 server emailadmin pt Servidor POP3 por omissão +standard smtp-server emailadmin pt Servidor SMTP por omissão +this php has no imap support compiled in!! emailadmin pt O PHP não tem suporte de IMAP compilado!! +use ldap defaults emailadmin pt Utilizar definições por omissão do LDAP +use smtp auth emailadmin pt Utilizar autenticação SMTP +use tls authentication emailadmin pt Utilizar autenticação TLS +use tls encryption emailadmin pt Utilizar cifra TLS +user can edit forwarding address emailadmin pt O utilizador pode editar endereços reencaminhados +username (standard) emailadmin pt Nome de utilizador (por omissão) +username@domainname (virtual mail manager) emailadmin pt utilizador@dominio (Gestor virtual de correio electrónico) +users can define their own emailaccounts emailadmin pt Os utilizadores podem definir as suas contas de correio electrónico +virtual mail manager emailadmin pt Gestor virtual de correio electrónico +your name emailadmin pt O seu nome diff --git a/emailadmin/lang/egw_ru.lang b/emailadmin/lang/egw_ru.lang new file mode 100644 index 0000000000..575c9c8474 --- /dev/null +++ b/emailadmin/lang/egw_ru.lang @@ -0,0 +1,159 @@ +%1 entries deleted. emailadmin ru %1 запиÑи удалены. +(imapclass must support this feature by querying the corresponding config value and pass it as defaultquota to the imapserver) emailadmin ru (клаÑÑ IMAP должен поддерживать возможноÑÑ‚ÑŒ запроÑа ÑоответÑтвующего параметра конфигурации и передачи Ñерверу IMAP как квоту по умолчанию) +account '%1' not found !!! emailadmin ru Ð£Ñ‡ÐµÑ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ '%1' не найдена !!! +active templates emailadmin ru Ðктивные шаблоны +add new email address: emailadmin ru Добавить новый Ð°Ð´Ñ€ÐµÑ Ñл. почты: +add profile emailadmin ru Добавить Профиль +admin dn emailadmin ru админиÑтратор домена +admin password emailadmin ru Пароль админиÑтратора +admin username emailadmin ru Ð˜Ð¼Ñ Ð²Ñ…Ð¾Ð´Ð° админиÑтратора +advanced options emailadmin ru РаÑширенные наÑтройки +alternate email address emailadmin ru Добавочный Ð°Ð´Ñ€ÐµÑ Ñл. почты +any application emailadmin ru Любое приложение +any group emailadmin ru Ð›ÑŽÐ±Ð°Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ð° +any user emailadmin ru любой пользователь +back to admin/grouplist emailadmin ru Ðазад к ÐдминиÑтрированию/СпиÑку групп +back to admin/userlist emailadmin ru Ðазад к ÐдминиÑтрированию/СпиÑку пользователей +bad login name or password. emailadmin ru Ðеверное Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð¸Ð»Ð¸ пароль. +bad or malformed request. server responded: %s emailadmin ru Ðеверный или нераÑпознанный запроÑ. Ответ Сервера: %s +bad request: %s emailadmin ru Ðеверный запроÑ: %s +can be used by application emailadmin ru Может быть иÑпользовано приложением +can be used by group emailadmin ru Может быть иÑпользовано группой +can be used by user emailadmin ru может быть иÑпользовано пользователем +connection dropped by imap server. emailadmin ru Соединение разорвано Ñервером IMAP +continue emailadmin ru Продолжить +could not complete request. reason given: %s emailadmin ru Ðе могу завершить запроÑ. Ð’Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð°Ñ ÐŸÑ€Ð¸Ñ‡Ð¸Ð½Ð°: %s +could not open secure connection to the imap server. %s : %s. emailadmin ru Ðе могу уÑтановить защищенное Ñоединение Ñ Ñервером IMAP. %s : %s. +cram-md5 or digest-md5 requires the auth_sasl package to be installed. emailadmin ru CRAM-MD5 и DIGEST-MD5 требуют уÑтановленного пакета Auth_SASL. +cyrus imap server emailadmin ru IMAP Ñервер Cyrus +cyrus imap server administration emailadmin ru админиÑтрирование IMAP Ñервера Cyrus +default emailadmin ru по умолчанию +deliver extern emailadmin ru доÑтавить внешний +do not validate certificate emailadmin ru не проверÑÑ‚ÑŒ Ñертификат +do you really want to delete this profile emailadmin ru Ð’Ñ‹ уверены, что хотите удалить Ñтот Профиль +do you really want to reset the filter for the profile listing emailadmin ru Ð’Ñ‹ уверены что хотите очиÑтить фильтр Ð´Ð»Ñ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ ÑпиÑка Профилей +domainname emailadmin ru Ð˜Ð¼Ñ Ð”Ð¾Ð¼ÐµÐ½Ð° +edit email settings emailadmin ru Редактировать наÑтройки почты +email account active emailadmin ru Ð£Ñ‡ÐµÑ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ включена +email address emailadmin ru ÐÐ´Ñ€ÐµÑ Ñл. почты +email settings common ru ÐаÑтройки почты +emailadmin emailadmin ru ÐдминиÑтрирование Почты +emailadmin: group assigned profile common ru ÐдминиÑтрирование почты: Ðазначение Ð¿Ñ€Ð¾Ñ„Ð¸Ð»Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ñ‹. +emailadmin: user assigned profile common ru ÐдминиÑтрирование почты: ÐÐ°Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ñ„Ð¸Ð»Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ +enable cyrus imap server administration emailadmin ru включить админиÑтрирование IMAP Ñервера Cyrus +enable sieve emailadmin ru Включить Sieve +encrypted connection emailadmin ru зашифрованное Ñоединение +encryption settings emailadmin ru ÐаÑтройки ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ +enter your default mail domain (from: user@domain) emailadmin ru Укажите ваш почтовый домен по умолчанию (от: user@domain) +entry saved emailadmin ru ЗапиÑÑŒ Ñохранена. +error connecting to imap server. %s : %s. emailadmin ru Ошибка ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ñ Ñервером IMAP. %s : %s. +error connecting to imap server: [%s] %s. emailadmin ru Ошибка ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ñ Ñервером IMAP: [%s] %s. +error deleting entry! emailadmin ru Ошибка при удалении запиÑи! +error saving the entry!!! emailadmin ru Ошибка при Ñохранении запиÑи! +filtered by account emailadmin ru отфильтровано по Учетной запиÑи +filtered by group emailadmin ru отфильтровано по группе +forward also to emailadmin ru ПереÑлать также в +forward email's to emailadmin ru ПереÑлать пиÑьма в +forward only emailadmin ru Только переÑлать +global options emailadmin ru Общие наÑтройки +if using ssl or tls, you must have the php openssl extension loaded. emailadmin ru При иÑпользовании SSL или TLS, у Ð²Ð°Ñ Ð´Ð¾Ð»Ð¶ÐµÐ½ быть уÑтановлен PHP Ñ Ð·Ð°Ð³Ñ€ÑƒÐ¶ÐµÐ½Ð½Ñ‹Ð¼ раÑширением openssl +imap admin password admin ru пароль админиÑтратора IMAP +imap admin user admin ru Ð˜Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð´Ð»Ñ Ð°Ð´Ð¼Ð¸Ð½Ð¸Ñтратора IMAP +imap c-client version < 2001 emailadmin ru ВерÑÐ¸Ñ C-клиента IMAP < 2001 +imap server emailadmin ru Сервер IMAP +imap server closed the connection. emailadmin ru Сервер IMAP разорвал Ñоединение. +imap server closed the connection. server responded: %s emailadmin ru Сервер IMAP разорвал Ñоединение. Сервер Ñообщил: %s +imap server hostname or ip address emailadmin ru Ð˜Ð¼Ñ Ñервера IMAP или его ip-Ð°Ð´Ñ€ÐµÑ +imap server logintyp emailadmin ru Тип входа Ñервера IMAP +imap server name emailadmin ru Ð˜Ð¼Ñ Ñервера imap +imap server port emailadmin ru Порт Ñервера IMAP +imap/pop3 server name emailadmin ru Ð˜Ð¼Ñ Ñервера IMAP/POP3 +in mbyte emailadmin ru Ð’ мегабайтах +inactive emailadmin ru Ðеактивно +ldap basedn emailadmin ru Ðачальный DN LDAP +ldap server emailadmin ru Сервер LDAP +ldap server accounts dn emailadmin ru DN учетных запиÑей Ñервера LDAP +ldap server admin dn emailadmin ru DN админиÑтратора Ñервера LDAP +ldap server admin password emailadmin ru Пароль админиÑтратора Ñервера LDAP +ldap server hostname or ip address emailadmin ru Ð˜Ð¼Ñ Ñервера LDAP или его ip-Ð°Ð´Ñ€ÐµÑ +ldap settings emailadmin ru ÐаÑтройки LDAP +leave empty for no quota emailadmin ru ОÑтавьте пуÑтым Ð´Ð»Ñ Ð¾Ñ‚ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ ÐºÐ²Ð¾Ñ‚ +mail settings admin ru ÐаÑтройки почты +manage stationery templates emailadmin ru Управление бланками шаблонов +mb used emailadmin ru ИÑпоьлзуетÑÑ MB +name of organisation emailadmin ru Ðазвание организации +no alternate email address emailadmin ru Ðет дополнительного Ñл. адреÑа +no encryption emailadmin ru Без ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ +no forwarding email address emailadmin ru Ðет адреÑа Ð¿ÐµÑ€ÐµÐ½Ð°Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ +no message returned. emailadmin ru Ðет ответа +no supported imap authentication method could be found. emailadmin ru Поддерживаемый метод авторизации IMAP не найден +order emailadmin ru УпорÑдочить +organisation emailadmin ru ÐžÑ€Ð³Ð°Ð½Ð¸Ð·Ð°Ñ†Ð¸Ñ +plesk can't rename users --> request ignored emailadmin ru Plesk не может переименовать пользователей --> Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¿Ñ€Ð¾Ð¸Ð³Ð½Ð¾Ñ€Ð¸Ñ€Ð¾Ð²Ð°Ð½ +plesk imap server (courier) emailadmin ru Сервер IMAP Plesk (Courier) +plesk mail script '%1' not found !!! emailadmin ru Ðе найден Ñкрипт '%1' почты Plesk!!! +plesk requires passwords to have at least 5 characters and not contain the account-name --> password not set!!! emailadmin ru Plesk требует длины Ð¿Ð°Ñ€Ð¾Ð»Ñ Ð½Ðµ менее 5 Ñимволов, не Ñодержащий имени аккаунта --> пароль ÐЕ уÑтановлен!!! +plesk smtp-server (qmail) emailadmin ru Plesk SMTP Ñервер (Qmail) +pop3 server hostname or ip address emailadmin ru Ð˜Ð¼Ñ ÐºÐ¾Ð¼Ð¿ÑŒÑŽÑ‚ÐµÑ€Ð° или ip-Ð°Ð´Ñ€ÐµÑ Ñервера POP3 +pop3 server port emailadmin ru Порт Ñервера POP3 +port emailadmin ru Порт +postfix with ldap emailadmin ru Postfix Ñ LDAP +profile access rights emailadmin ru права доÑтупа к профилю +profile is active emailadmin ru профиль активен +profile list emailadmin ru СпиÑок Ð¿Ñ€Ð¾Ñ„Ð¸Ð»Ñ +profile name emailadmin ru Ðазвание Ð¿Ñ€Ð¾Ñ„Ð¸Ð»Ñ +qmaildotmode emailadmin ru режим qmaildot +quota settings emailadmin ru ÐаÑтройки ÐºÐ²Ð¾Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ +quota size in mbyte emailadmin ru Размер квоты в МБ +remove emailadmin ru Удалить +reset filter emailadmin ru ОчиÑтить фильтр +select type of imap server emailadmin ru Выбор типа Ñервера IMAP +select type of imap/pop3 server emailadmin ru Выбор типа Ñервера IMAP/POP3 +select type of smtp server emailadmin ru Выбор типа Ñервера SMTP +send using this email-address emailadmin ru Отправить Ñ Ñтого адреÑа eMail +server settings emailadmin ru ÐаÑтройки Ñервера +sieve server hostname or ip address emailadmin ru Ð˜Ð¼Ñ ÐºÐ¾Ð¼Ð¿ÑŒÑŽÑ‚ÐµÑ€Ð° или IP Ñервера Sieve +sieve server port emailadmin ru Порт Ñервера Sieve +sieve settings emailadmin ru ÐаÑтройки Sieve +smtp authentication emailadmin ru ÐÐ²Ñ‚Ð¾Ñ€Ð¸Ð·Ð°Ñ†Ð¸Ñ SMTP +smtp options emailadmin ru Параметры SMTP +smtp server name emailadmin ru Ð˜Ð¼Ñ Ñервера SMTP +smtp settings emailadmin ru УÑтановки SMTP +smtp-server hostname or ip address emailadmin ru Ð˜Ð¼Ñ ÐºÐ¾Ð¼Ð¿ÑŒÑŽÑ‚ÐµÑ€Ð° или IP-Ð°Ð´Ñ€ÐµÑ Ñервера SMTP +smtp-server port emailadmin ru Порт Ñервера SMTP +standard emailadmin ru Стандартный +standard imap server emailadmin ru Стандартный IMAP Ñервер +standard pop3 server emailadmin ru Стандартный POP3 Ñервер +standard smtp-server emailadmin ru Стандартный SMTP Ñервер +starts with emailadmin ru ÐачинаетÑÑ Ñ +stationery emailadmin ru Бланк +the imap server does not appear to support the authentication method selected. please contact your system administrator. emailadmin ru Сервер IMAP не Ñообщил о поддержке выбранного метода авторизации. ПожалуйÑта ÑвÑжитеÑÑŒ Ñо Ñвоим ÑиÑтемным админиÑтратором. +this php has no imap support compiled in!! emailadmin ru Эта верÑÐ¸Ñ PHP Ñобрана без поддержки IMAP!! +to use a tls connection, you must be running a version of php 5.1.0 or higher. emailadmin ru Ð”Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ TLS, у Ð²Ð°Ñ Ð´Ð¾Ð»Ð¶ÐµÐ½ быть запущен PHP 5.1.0 или выше. +unexpected response from server to authenticate command. emailadmin ru ÐепредуÑмотренный ответ Ñервера на команду AUTHENTICATE. +unexpected response from server to digest-md5 response. emailadmin ru ÐепредуÑмотренный ответ Ñервера на Ñигнал Digest-MD5. +unexpected response from server to login command. emailadmin ru ÐепредуÑмотренный ответ Ñервера на команду LOGIN. +unknown imap response from the server. server responded: %s emailadmin ru ÐеизвеÑтный IMAP-ответ от Ñервера. Сервер Ñообщил: %s +unsupported action '%1' !!! emailadmin ru Ðеподдерживаемое дейÑтвие '%1' !!! +update current email address: emailadmin ru Обновить текущий Ð°Ð´Ñ€ÐµÑ Ñл.почты: +use ldap defaults emailadmin ru ИÑпользовать наÑтройки LDAP по умолчанию +use predefined username and password defined below emailadmin ru ИÑпользовать предопределенное Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð¸ пароль указанный ниже +use smtp auth emailadmin ru ИÑпользовать авторизацию SMTP +use tls authentication emailadmin ru ИÑпользовать авторизацию TLS +use tls encryption emailadmin ru ИÑпользовать шифрование TLS +use users email-address (as seen in useraccount) emailadmin ru ИÑпользуйте адреÑа Ñл.почты пользователÑ, как указано в наÑтройках аккаунта пользователÑ. +user can edit forwarding address emailadmin ru Ползователь может редактировать Ð°Ð´Ñ€ÐµÑ Ð¿ÐµÑ€ÐµÑылки. +userid@domain eg. u1234@domain emailadmin ru UserId@domain например u1234@domain +username (standard) emailadmin ru Ð˜Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ (Ñтандартное) +username/password defined by admin emailadmin ru Ð˜Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ/Пароль уÑтановлены админиÑтратором +username@domainname (virtual mail manager) emailadmin ru username@domainname (Менеджер Виртуальной почты) +users can define their own emailaccounts emailadmin ru Пользователь может назначать Ñвои ÑобÑтвенные учетные запиÑи Ñл. почты. +users can define their own identities emailadmin ru Пользователи могут уÑтанавливать Ñвои ÑобÑтвенные данные идентификации +users can define their own signatures emailadmin ru Пользователи могут уÑтанавливать Ñвои ÑобÑтвенные подпиÑи +users can utilize these stationery templates emailadmin ru Пользователи могут иÑпользовать Ñти бланки шаблонов +vacation messages with start- and end-date require an admin account to be set emailadmin ru Ð¡Ð¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð°Ð²Ñ‚Ð¾Ð¾Ñ‚Ð²ÐµÑ‚Ñ‡Ð¸ÐºÐ° Ñ Ð´Ð°Ñ‚Ð°Ð¼Ð¸ начала и Ð¾ÐºÐ¾Ð½Ñ‡Ð°Ð½Ð¸Ñ Ñ‚Ñ€ÐµÐ±ÑƒÑŽÑ‚ уÑтановленной учетной запиÑи админиÑтратора! +virtual mail manager emailadmin ru Менеджер Виртуальной почты +yes, use credentials below only for alarms and notifications, otherwise use credentials of current user emailadmin ru Да, иÑпользовать учётные данные, указанные ниже только Ð´Ð»Ñ Ð¿Ñ€ÐµÐ´ÑƒÐ¿Ñ€ÐµÐ¶Ð´ÐµÐ½Ð¸Ð¹ и напоминаний, в оÑтальных ÑлучаÑÑ… иÑпользовать учётные данные текущего пользователÑ. +yes, use credentials of current user or if given credentials below emailadmin ru Да, иÑпользовать учётные данные текущего пользователÑ, или еÑли указаны учётные данные ниже. +you have received a new message on the emailadmin ru Ð’Ñ‹ получили ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð½Ð° +your name emailadmin ru Ваше Ð¸Ð¼Ñ diff --git a/emailadmin/lang/egw_rw.lang b/emailadmin/lang/egw_rw.lang new file mode 100644 index 0000000000..d61abbb1c8 --- /dev/null +++ b/emailadmin/lang/egw_rw.lang @@ -0,0 +1 @@ +email address emailadmin rw email diff --git a/emailadmin/lang/egw_sk.lang b/emailadmin/lang/egw_sk.lang new file mode 100644 index 0000000000..c05ef135d5 --- /dev/null +++ b/emailadmin/lang/egw_sk.lang @@ -0,0 +1,151 @@ +%1 entries deleted. emailadmin sk %1 položiek odstránených. +account '%1' not found !!! emailadmin sk ÚÄet '%1' sa nenaÅ¡iel !!! +active templates emailadmin sk Aktívne Å¡ablóny +add new email address: emailadmin sk PridaÅ¥ novú E-mailovú adresu: +add profile emailadmin sk PridaÅ¥ profil +admin dn emailadmin sk Správcov dn +admin password emailadmin sk Heslo správcu +admin username emailadmin sk Používateľské meno správcu +advanced options emailadmin sk PokroÄilé možnosti +alternate email address emailadmin sk Alternatívna E-mailová adresa +any application emailadmin sk Ktorákoľvek aplikácia +any group emailadmin sk Ktorákoľvek skupina +any user emailadmin sk Ktorýkoľvek používateľ +back to admin/grouplist emailadmin sk Späť na Správu/Skupiny +back to admin/userlist emailadmin sk Späť na Správu/Používateľov +bad login name or password. emailadmin sk Chybné prihlasovacie meno alebo heslo. +bad or malformed request. server responded: %s emailadmin sk Chybná požiadavka. OdpoveÄ servera: %s +bad request: %s emailadmin sk Chybná požiadavka: %s +can be used by application emailadmin sk môže byÅ¥ použité aplikáciou +can be used by group emailadmin sk môže byÅ¥ použité skupinou +can be used by user emailadmin sk môže byÅ¥ použité používateľom +connection dropped by imap server. emailadmin sk Spojenie preruÅ¡ené IMAP serverom. +continue emailadmin sk PokraÄovaÅ¥ +could not complete request. reason given: %s emailadmin sk Nemožno dokonÄiÅ¥ požiadavku. Dôvod: %s +could not open secure connection to the imap server. %s : %s. emailadmin sk Nepodarilo sa nadviazaÅ¥ zabezpeÄené pripojenie k IMAP serveru. %s : %s. +cram-md5 or digest-md5 requires the auth_sasl package to be installed. emailadmin sk CRAM-MD5 alebo DIGEST-MD5 vyžaduje, aby bol nainÅ¡talovaný balík Auth_SASL. +cyrus imap server emailadmin sk Cyrus IMAP Server +cyrus imap server administration emailadmin sk Správa servera Cyrus IMAP +default emailadmin sk predvolené +deliver extern emailadmin sk doruÄiÅ¥ extern +do not validate certificate emailadmin sk neoveriÅ¥ certifikát +do you really want to delete this profile emailadmin sk Naozaj chcete odstrániÅ¥ tento Profil +do you really want to reset the filter for the profile listing emailadmin sk Naozaj chcete vynulovaÅ¥ filter pre výpis profilu? +domainname emailadmin sk názov domény +edit email settings emailadmin sk upraviÅ¥ nastavenia E-mailu +email account active emailadmin sk E-mailový úÄet je aktívny +email address emailadmin sk E-mailová adresa +email settings common sk Nastavenia E-mailu +emailadmin emailadmin sk EMailAdmin +emailadmin: group assigned profile common sk eMailAdmin: skupinovo priradený Profil +emailadmin: user assigned profile common sk eMailAdmin: používateľsky priradený Profil +enable cyrus imap server administration emailadmin sk zapnúť správu servera Cyrus IMAP +enable sieve emailadmin sk zapnúť Sieve +encrypted connection emailadmin sk Å¡ifrované spojenie +encryption settings emailadmin sk nastavenia Å¡ifrovania +enter your default mail domain (from: user@domain) emailadmin sk Zadajte vaÅ¡u predvolenú doménu (z tvaru: user@domain) +entry saved emailadmin sk Položka bola uložená +error connecting to imap server. %s : %s. emailadmin sk Chyba poÄas pripájania k IMAP serveru. %s : %s. +error connecting to imap server: [%s] %s. emailadmin sk Chyba poÄas pripájania k IMAP serveru: [%s] %s. +error deleting entry! emailadmin sk Chyba pri odstraňovaní položky! +error saving the entry!!! emailadmin sk Chyba pri ukladaní položky!!! +filtered by account emailadmin sk filtrované podľa ÚÄtu +filtered by group emailadmin sk filtrované podľa Skupiny +forward also to emailadmin sk preposlaÅ¥ taktiež (komu) +forward email's to emailadmin sk preposlaÅ¥ emaily (kam) +forward only emailadmin sk preposlaÅ¥ iba +global options emailadmin sk Globálne možnosti +if using ssl or tls, you must have the php openssl extension loaded. emailadmin sk Ak používate SSL alebo TLS, musíte maÅ¥ nahraté rozšírenie PHP openssl. +imap admin password admin sk heslo správcu IMAP +imap admin user admin sk používateľ pre správu IMAP +imap c-client version < 2001 emailadmin sk IMAP C-klient verzia < 2001 +imap server emailadmin sk IMAP server +imap server closed the connection. emailadmin sk IMAP server ukonÄil spojenie. +imap server closed the connection. server responded: %s emailadmin sk IMAP server ukonÄil spojenie. OdpoveÄ servera: %s +imap server hostname or ip address emailadmin sk názov (hostname) alebo IP adresa IMAP servera +imap server logintyp emailadmin sk typ prihlásenia IMAP servera +imap server name emailadmin sk Názov IMAP servera +imap server port emailadmin sk port IMAP servera +imap/pop3 server name emailadmin sk názov IMAP/POP3 servera +in mbyte emailadmin sk v Megabajtoch +inactive emailadmin sk neaktívne +ldap basedn emailadmin sk LDAP basedn +ldap server emailadmin sk LDAP server +ldap server accounts dn emailadmin sk DN úÄtov LDAP servera +ldap server admin dn emailadmin sk DN správcu LDAP servera +ldap server admin password emailadmin sk heslo správcu LDAP servera +ldap server hostname or ip address emailadmin sk názov (hostname) alebo IP adresa LDAP servera +ldap settings emailadmin sk nastavenia LDAP +leave empty for no quota emailadmin sk ak nechcete kvóty, ponechajte prázdne +mail settings admin sk nastavenia PoÅ¡ty +name of organisation emailadmin sk Názov organizácie +no alternate email address emailadmin sk žiadna alternatívna E-mailová adresa +no encryption emailadmin sk bez Å¡ifrovania +no forwarding email address emailadmin sk žiadna emailová adresa pre preposielanie +no message returned. emailadmin sk Žiadne správy sa nevrátili. +no supported imap authentication method could be found. emailadmin sk NenaÅ¡iel som žiadnu podporovanú overovaciu metódu IMAP. +order emailadmin sk Poradie +organisation emailadmin sk Organizácia +plesk can't rename users --> request ignored emailadmin sk Plesk používatelia sa nedajú premenovaÅ¥ --> požiadavka ignorovaná +plesk imap server (courier) emailadmin sk Plesk IMAP Server (Courier) +plesk mail script '%1' not found !!! emailadmin sk Plesk mail script '%1' sa nenaÅ¡lo !!! +plesk requires passwords to have at least 5 characters and not contain the account-name --> password not set!!! emailadmin sk Plesk vyžaduje, aby heslá mali aspoň 5 znakov a neobsahovali názov úÄtu --> heslo NEBOLO nastavené!!! +plesk smtp-server (qmail) emailadmin sk Plesk SMTP-Server (qmail) +pop3 server hostname or ip address emailadmin sk názov (hostname) alebo IP adresa POP3 servera +pop3 server port emailadmin sk port POP3 servera +port emailadmin sk Port +postfix with ldap emailadmin sk Postfix + LDAP +profile access rights emailadmin sk prístupové práva profilu +profile is active emailadmin sk profil je aktívny +profile list emailadmin sk Zoznam profilov +profile name emailadmin sk Názov profilu +qmaildotmode emailadmin sk qmail bodkový režim +quota settings emailadmin sk Nastavenia kvóty +quota size in mbyte emailadmin sk veľkosÅ¥ kvóty v Megabajtoch +remove emailadmin sk OdstrániÅ¥ +reset filter emailadmin sk vynulovaÅ¥ filter +select type of imap server emailadmin sk Vyberte typ IMAP servera +select type of imap/pop3 server emailadmin sk Vyberte typ IMAP/POP3 servera +select type of smtp server emailadmin sk Vyberte typ SMTP servera +send using this email-address emailadmin sk odoslaÅ¥ pomocou tejto e-mailovej adresy +server settings emailadmin sk Nastavenia servera +sieve server hostname or ip address emailadmin sk Názov (hostname) alebo IP Sieve servera +sieve server port emailadmin sk Nort Sieve servera +sieve settings emailadmin sk Nastavenia Sieve +smtp authentication emailadmin sk SMTP overovanie +smtp options emailadmin sk SMTP možnosti +smtp server name emailadmin sk Názov SMTP servera +smtp settings emailadmin sk SMTP nastavenia +smtp-server hostname or ip address emailadmin sk názov (hostname) alebo IP adresa SMTP servera +smtp-server port emailadmin sk port SMTP servera +standard emailadmin sk Å tandard +standard imap server emailadmin sk Å tandardný IMAP server +standard pop3 server emailadmin sk Å tandardný POP3 server +standard smtp-server emailadmin sk Å tandardný SMTP server +starts with emailadmin sk zaÄaÅ¥ s +the imap server does not appear to support the authentication method selected. please contact your system administrator. emailadmin sk Tento IMAP server, zdá sa, nepodporuje zvolenú overovaciu metódu. Prosím kontaktujte správcu systému. +this php has no imap support compiled in!! emailadmin sk Toto PHP nemá zakompilovanú podporu pre IMAP !! +to use a tls connection, you must be running a version of php 5.1.0 or higher. emailadmin sk Ak chcete používaÅ¥ pripojenie TLS, musíte fungovaÅ¥ na verzii PHP 5.1.0 alebo vyÅ¡Å¡ej. +unexpected response from server to authenticate command. emailadmin sk NeoÄakávaná odpoveÄ od servera na príkaz AUTENTICATE. +unexpected response from server to digest-md5 response. emailadmin sk NeoÄakávaná odpoveÄ od servera na Digest-MD5 odpoveÄ. +unexpected response from server to login command. emailadmin sk NeoÄakávaná odpoveÄ od servera na príkaz LOGIN. +unknown imap response from the server. server responded: %s emailadmin sk Neznáma IMAP odpoveÄ od servera. Server odpovedal: %s +unsupported action '%1' !!! emailadmin sk Nepodporovaná akcia '%1' !!! +update current email address: emailadmin sk AktualizovaÅ¥ súÄasnú E-mailovú adresu: +use ldap defaults emailadmin sk PoužiÅ¥ predvolené hodnoty LDAP +use predefined username and password defined below emailadmin sk PoužiÅ¥ preddefinované používateľské meno a heslo, definované nižšie +use smtp auth emailadmin sk PoužiÅ¥ SMTP overovanie +use tls authentication emailadmin sk PoužiÅ¥ TLS overovanie +use tls encryption emailadmin sk PoužiÅ¥ TLS Å¡ifrovanie +use users email-address (as seen in useraccount) emailadmin sk použiÅ¥ emailové adresy používateľov (ako vidno v používateľskom úÄte) +user can edit forwarding address emailadmin sk Používateľ môže upraviÅ¥ adresu preposielania +username (standard) emailadmin sk používateľské meno (Å¡tandardné) +username/password defined by admin emailadmin sk Používateľské meno/heslo definované správcom +username@domainname (virtual mail manager) emailadmin sk používateľ@doména (Virtuálny Mail ManaGeR) +users can define their own emailaccounts emailadmin sk Používatelia môžu definovaÅ¥ ich vlastné E-mailové úÄty +users can define their own identities emailadmin sk Používatelia môžu definovaÅ¥ svoje vlastné identity +users can define their own signatures emailadmin sk Používatelia môžu definovaÅ¥ svoje vlastné podpisy +virtual mail manager emailadmin sk Virtuálny MAIL ManaGeR +yes, use credentials below only for alarms and notifications, otherwise use credentials of current user emailadmin sk Ãno, použiÅ¥ nižšieuvedené prístupové údaje iba pre upozornenia, inÃ¡Ä použiÅ¥ prístupové údaje aktuálneho používateľa +yes, use credentials of current user or if given credentials below emailadmin sk Ãno, použiÅ¥ prístupové údaje aktuálneho používateľa, alebo prístupové údaje uvedené nižšie ak sú vyplnené +you have received a new message on the emailadmin sk Obdržali ste novú správu ohľadom diff --git a/emailadmin/lang/egw_sl.lang b/emailadmin/lang/egw_sl.lang new file mode 100644 index 0000000000..925cfe4780 --- /dev/null +++ b/emailadmin/lang/egw_sl.lang @@ -0,0 +1,145 @@ +account '%1' not found !!! emailadmin sl RaÄun '%1' ni bil najden! +add new email address: emailadmin sl Dodaj nov e-naslov: +add profile emailadmin sl Dodaj profil +admin dn emailadmin sl Oskrbnikov dn +admin password emailadmin sl Oskrbnikovo geslo +admin username emailadmin sl Oskrbnikovo uporabniÅ¡ko ime +advanced options emailadmin sl Napredne izbire +alternate email address emailadmin sl Alternativen E-naslov +any application emailadmin sl Katerakoli aplikacija +any group emailadmin sl Katerakoli skupina +any user emailadmin sl Katerikoli uporabnik +back to admin/grouplist emailadmin sl Nazaj na Admin/Seznam skupin +back to admin/userlist emailadmin sl Nazaj na Admin/Seznam uporabnikov +bad login name or password. emailadmin sl NapaÄno uporabniÅ¡ko ime ali geslo. +bad or malformed request. server responded: %s emailadmin sl NapaÄna ali napaÄno oblikovana zahteva. Odgovor strežnika: %s +bad request: %s emailadmin sl NapaÄna zahteva: %s +can be used by application emailadmin sl Je lahko uporabljen iz aplikacije +can be used by group emailadmin sl Je lahko uporabljen iz skupine +can be used by user emailadmin sl Lahko uporablja uporabnik +connection dropped by imap server. emailadmin sl Strežnik IMAP je prekinil povezavo. +continue emailadmin sl Nadaljuj +could not complete request. reason given: %s emailadmin sl Ne morem dokonÄati zahteve. Podan vzrok: %s +could not open secure connection to the imap server. %s : %s. emailadmin sl Ne morem odpreti varne povezave s strežnikom IMAP. %s : %s. +cram-md5 or digest-md5 requires the auth_sasl package to be installed. emailadmin sl CRAM-MD5 ali DIGEST-MD5 zahteva, da je nameÅ¡Äen paket Auth_SASL. +cyrus imap server emailadmin sl Cyrus IMAP strežnik +cyrus imap server administration emailadmin sl Upravljanje Cyrus IMAP strežnika +default emailadmin sl Privzeto +deliver extern emailadmin sl Zunanja dostava +do not validate certificate emailadmin sl Ne preverjaj certifikata +do you really want to delete this profile emailadmin sl Ali res želite izbrisati ta profil? +do you really want to reset the filter for the profile listing emailadmin sl Res želite ponastaviti filter za listanje profilov? +domainname emailadmin sl Ime domene +edit email settings emailadmin sl Uredi nastavitve E-poÅ¡te +email account active emailadmin sl Nabiralnik E-poÅ¡te je aktiven +email address emailadmin sl E-naslov +email settings common sl Nastavitve E-poÅ¡te +emailadmin emailadmin sl EMailAdmin +emailadmin: group assigned profile common sl eMailAdmin: profil, dodeljen skupini +emailadmin: user assigned profile common sl eMailAdmin: profil, dodeljen uprabniku +enable cyrus imap server administration emailadmin sl OmogoÄi upravljanje Cyrus IMAP strežnika +enable sieve emailadmin sl OmogoÄi Sieve +encrypted connection emailadmin sl Kodirana povezava +encryption settings emailadmin sl Nastavitve Å¡ifriranja +enter your default mail domain (from: user@domain) emailadmin sl Vnesite privzeto domeno (oblika: uporabnik@domena) +entry saved emailadmin sl Vnos shranjen +error connecting to imap server. %s : %s. emailadmin sl Napaka pri povezavi s strežnikom IMAP. %s: %s. +error connecting to imap server: [%s] %s. emailadmin sl Napaka pri povezavi s strežnikom IMAP: [%s] %s. +error saving the entry!!! emailadmin sl Napaka pri shranjevanju vnosa! +filtered by account emailadmin sl Filtrirano s strani raÄuna +filtered by group emailadmin sl Filtrirano s strani skupine +forward also to emailadmin sl Posreduj tudi +forward email's to emailadmin sl Posreduj sporoÄila k +forward only emailadmin sl Samo posreduj +global options emailadmin sl Globalne možnosti +if using ssl or tls, you must have the php openssl extension loaded. emailadmin sl ÄŒe uporabljate SSL ali TLS, morate imeti naloženo razÅ¡iritev PHP openssl +imap admin password admin sl Geslo oskrbnika IMAPa +imap admin user admin sl UporabniÅ¡ko ime oskrbnika IMAP-a +imap c-client version < 2001 emailadmin sl IMAP C-klient razliÄica pred 2001 +imap server emailadmin sl Strežnik IMAP +imap server closed the connection. emailadmin sl Strežnik IMAP je prekinil povezavo. +imap server closed the connection. server responded: %s emailadmin sl Strežnik IMAP je prekinil povezavo. Odgovor strežnika: %s +imap server hostname or ip address emailadmin sl Ime ali IP strežnika IMAP +imap server logintyp emailadmin sl NaÄin prijave strežnik IMAP +imap server name emailadmin sl Ime strežnika IMAP +imap server port emailadmin sl Vrata strežnika IMAP +imap/pop3 server name emailadmin sl Ime strežnika IMAP/POP3 +in mbyte emailadmin sl v MB +inactive emailadmin sl Neaktivno +ldap basedn emailadmin sl Osnovni DN LDAP +ldap server emailadmin sl Strežnik LDAP +ldap server accounts dn emailadmin sl DN raÄunov LDAP strežnika +ldap server admin dn emailadmin sl Oskrbnikov DN LDAP strežnika +ldap server admin password emailadmin sl Geslo oskrbnika LDAP strežnika +ldap server hostname or ip address emailadmin sl Ime ali IP strežnika LDAP +ldap settings emailadmin sl Nastavitve LDAP +leave empty for no quota emailadmin sl Pustite prazno za neomejeno +mail settings admin sl Nastavitve E-poÅ¡te +name of organisation emailadmin sl Ime organizacije +no alternate email address emailadmin sl Ni alternativnega E-naslova +no encryption emailadmin sl Brez Å¡ifriranja +no forwarding email address emailadmin sl Ni E-naslova za posredovanje +no message returned. emailadmin sl Ni vrnjenega sporoÄila. +no supported imap authentication method could be found. emailadmin sl Ni najdena metoda avtentikacije IMAP. +order emailadmin sl Vrstni red +organisation emailadmin sl Organizacija +plesk can't rename users --> request ignored emailadmin sl Plesk ne more preimenovati uporabnikov --> zahteva presliÅ¡ana +plesk imap server (courier) emailadmin sl Strežnik Plesk IMAP (Courier) +plesk mail script '%1' not found !!! emailadmin sl PoÅ¡tni skript Plesk '%1' ni bil najden! +plesk requires passwords to have at least 5 characters and not contain the account-name --> password not set!!! emailadmin sl Plesk zahteva geslo, dolga najmanj 5 znakov in ne sme vsebovati imena raÄuna --> geslo ni bilo nastavljeno! +plesk smtp-server (qmail) emailadmin sl Strežnik Plesk IMAP (Qmail) +pop3 server hostname or ip address emailadmin sl Ime ali IP POP3 strežnika +pop3 server port emailadmin sl Vrata POP3 strežnika +port emailadmin sl Vrata +postfix with ldap emailadmin sl KonÄnica pri LDAP +profile access rights emailadmin sl Pravice dostopa do profila +profile is active emailadmin sl Profil je aktiven +profile list emailadmin sl Seznam profilov +profile name emailadmin sl Ime profila +qmaildotmode emailadmin sl NaÄin za qmaildot +quota settings emailadmin sl Nastavitvev kvote +quota size in mbyte emailadmin sl Velikost kvote v MB +remove emailadmin sl Odstrani +reset filter emailadmin sl Ponastavi filter +select type of imap server emailadmin sl Izberite vrsto strežnika IMAP +select type of imap/pop3 server emailadmin sl Izberite tip IMAP/POP3 strežnika +select type of smtp server emailadmin sl Izberite tip SMTP strežnika +server settings emailadmin sl Nastavitve strežnika +sieve server hostname or ip address emailadmin sl IP ali naslov strežnika za sito (Sieve) +sieve server port emailadmin sl Vrata strežnika za sito (Sieve) +sieve settings emailadmin sl Nastavitve sita (Sieve) +smtp authentication emailadmin sl SMTP avtentikacija +smtp options emailadmin sl SMTP možnosti +smtp server name emailadmin sl ime SMTP strežnika +smtp settings emailadmin sl SMTP nastavitve +smtp-server hostname or ip address emailadmin sl Ime ali IP SMTP strežnika +smtp-server port emailadmin sl Vrata SMTP strežnika +standard emailadmin sl Standarden +standard imap server emailadmin sl Standardni IMAP strežnik +standard pop3 server emailadmin sl Standardni POP3 strežnik +standard smtp-server emailadmin sl Standardni SMTP strežnik +stationery emailadmin sl Stacionarno +the imap server does not appear to support the authentication method selected. please contact your system administrator. emailadmin sl Strežnik IMAP ne podpira izbrane metode avtentikacije. Kontaktirajte sistemskega upravitelja. +this php has no imap support compiled in!! emailadmin sl Ta PHP ne vsebuje podpore za IMAP! +to use a tls connection, you must be running a version of php 5.1.0 or higher. emailadmin sl ÄŒe želite uporabljati povezavo TLS, morate imeti nameÅ¡Äeno razliÄico PHP 5.1.0 ali viÅ¡jo. +unexpected response from server to authenticate command. emailadmin sl NepriÄakovan odgovor strežnika na ukaz AUTHENTICATE. +unexpected response from server to digest-md5 response. emailadmin sl NepriÄakovan odgovor strežnika na odgovor Digest-MD5. +unexpected response from server to login command. emailadmin sl NepriÄakovan odgovor strežnika na ukaz LOGIN. +unknown imap response from the server. server responded: %s emailadmin sl Neznan odgovor IMAP s strani strežnika. Odgovor strežnika: %s +unsupported action '%1' !!! emailadmin sl Nepodprto dejanje '%1'! +update current email address: emailadmin sl Posodobi trenutni e-naslov: +use ldap defaults emailadmin sl Uporabi privzeto za LDAP +use predefined username and password defined below emailadmin sl Uporabi spodnje, vnaprej doloÄeno uporabniÅ¡ko ime in geslo +use smtp auth emailadmin sl Uporabi SMTP avtentikacijo +use tls authentication emailadmin sl Uporabi TLS avtentikacijo +use tls encryption emailadmin sl Uporabi TLS enkripcijo +user can edit forwarding address emailadmin sl Uporabnik lahko doloÄa posredovalni naslov +username (standard) emailadmin sl Uporabnik (standardno) +username/password defined by admin emailadmin sl UporabniÅ¡ko ime/geslo dodeljeno s strani administratorja +username@domainname (virtual mail manager) emailadmin sl uporabnik@domena (Virtual MAIL ManaGeR) +users can define their own emailaccounts emailadmin sl Uporabniki lahko doloÄajo lastne E-poÅ¡tne predale +users can define their own identities emailadmin sl Uporabniki lahko doloÄijo lastne identitete +users can define their own signatures emailadmin sl Uporabniki lahko doloÄijo lastne podpise +virtual mail manager emailadmin sl Virtualni upravljalec E-poÅ¡te +you have received a new message on the emailadmin sl Prejeli ste novo sporoÄilo na +your name emailadmin sl VaÅ¡e ime diff --git a/emailadmin/lang/egw_sv.lang b/emailadmin/lang/egw_sv.lang new file mode 100644 index 0000000000..d58028d384 --- /dev/null +++ b/emailadmin/lang/egw_sv.lang @@ -0,0 +1,100 @@ +add profile emailadmin sv Skapa profil +admin dn emailadmin sv Admin dn +admin password emailadmin sv Admin lösenord +admin username emailadmin sv Admin användare +advanced options emailadmin sv Avanserade alternativ +alternate email address emailadmin sv Alternerande e-post adress +bad login name or password. emailadmin sv Ogiltigt användarna eller lösenord +bad or malformed request. server responded: %s emailadmin sv Ogiltig eller ofullständig förfrÃ¥gan. Server svarade: %s +bad request: %s emailadmin sv Ogiltig förfrÃ¥gan: %s +connection dropped by imap server. emailadmin sv Anslutningen stängd av IMAP server +continue emailadmin sv Fortsätt +could not complete request. reason given: %s emailadmin sv Kunde inte fullfölja förfrÃ¥gan. Svaret: %s +could not open secure connection to the imap server. %s : %s. emailadmin sv Kunde inte öppna en söker anslutning till IMAP servern. %s: %s +cram-md5 or digest-md5 requires the auth_sasl package to be installed. emailadmin sv CRAM-MD5 och DIGEST-MD5 kräver att Auth_SASL paketet installerats +cyrus imap server emailadmin sv Cyrus IMAP Server +cyrus imap server administration emailadmin sv Cyrus IMAP server administration +default emailadmin sv Standard +deliver extern emailadmin sv Leverera extern +do not validate certificate emailadmin sv Validera inte certifikat +do you really want to delete this profile emailadmin sv Vill du verkligen radera profilen? +domainname emailadmin sv Domän namn +edit email settings emailadmin sv Redigera e-post alternativ +email account active emailadmin sv Aktivt e-post konto +email address emailadmin sv E-post adress +enable cyrus imap server administration emailadmin sv Aktivera Cyrus IMAP server administration +enable sieve emailadmin sv Aktivera Sieve +encrypted connection emailadmin sv Krypterad anslutning +enter your default mail domain (from: user@domain) emailadmin sv Standard e-post domän (frÃ¥n: user@domain) +entry saved emailadmin sv Post sparad +error connecting to imap server. %s : %s. emailadmin sv Kunde inte ansluta till IMAP server %s : %s +error connecting to imap server: [%s] %s. emailadmin sv Kunde inte ansluta till IMAP server [%s] %s +error saving the entry!!! emailadmin sv Fel uppstod vid sparandet av posten! +forward also to emailadmin sv Vidarebefodra även till +forward email's to emailadmin sv Vidarebefodra e-post till +forward only emailadmin sv Vidarebefodra endast +if using ssl or tls, you must have the php openssl extension loaded. emailadmin sv Om du vill använda SSL eller TLS mÃ¥ste PHP openssl stödet laddas +imap admin password admin sv IMAP admin lösenord +imap admin user admin sv IMAP admin användare +imap c-client version < 2001 emailadmin sv IMAP C-Client version < 2001 +imap server emailadmin sv IMAP Server +imap server closed the connection. emailadmin sv IMAP server stängde anslutningen +imap server closed the connection. server responded: %s emailadmin sv IMAP server stängde anslutningen. Serverna svarade: %s +imap server hostname or ip address emailadmin sv IMAP server hostnamn eller IP adress +imap server logintyp emailadmin sv IMAP server inloggnings typ +imap server port emailadmin sv IMAP server port +imap/pop3 server name emailadmin sv IMAP/POP3 server namn +in mbyte emailadmin sv i MByte +ldap basedn emailadmin sv LDAP basedn +ldap server emailadmin sv LDAP server +ldap server accounts dn emailadmin sv LDAP server konton DN +ldap server admin dn emailadmin sv LDAP server admin DN +ldap server admin password emailadmin sv LDAP server admin lösenord +ldap server hostname or ip address emailadmin sv LDAP server hostnamn eller IP adress +ldap settings emailadmin sv LDAP alternativ +leave empty for no quota emailadmin sv Lämna tomt för ingen kvot +mail settings admin sv E-post alternativ +name of organisation emailadmin sv Organisations namn +no alternate email address emailadmin sv Ingen alternerande e-post adress +no encryption emailadmin sv Ingen kryptering +no forwarding email address emailadmin sv Ingen e-post vidarebefodrings adress +no message returned. emailadmin sv Inget svar meddelandes +no supported imap authentication method could be found. emailadmin sv Kunde inte hitta supporterad IMAP autentiserings metod +order emailadmin sv Sortering +organisation emailadmin sv Organisation +pop3 server hostname or ip address emailadmin sv POP3 server hostnamn eller IP adress +pop3 server port emailadmin sv POP3 server port +port emailadmin sv Port +postfix with ldap emailadmin sv Postfix med LDAP +profile list emailadmin sv Profil lista +profile name emailadmin sv Profil namn +qmaildotmode emailadmin sv qmaildotmode +quota settings emailadmin sv Kvot alternativ +quota size in mbyte emailadmin sv Kvotstorlek i Mb +remove emailadmin sv Radera +select type of imap/pop3 server emailadmin sv Välj typ av IMAP/POP3 server +select type of smtp server emailadmin sv Välj typ av SMTP Server +sieve server hostname or ip address emailadmin sv Sieve server hostnamn eller IP adress +sieve server port emailadmin sv Sieve server port +sieve settings emailadmin sv Sieve alternativ +smtp server name emailadmin sv SMTP server namn +smtp settings emailadmin sv SMTP alternativ +smtp-server hostname or ip address emailadmin sv SMTP server hostnamn eller IP adress +smtp-server port emailadmin sv SMTP server port +standard emailadmin sv Standard +standard imap server emailadmin sv Standard IMAP server +standard pop3 server emailadmin sv Standard POP3 server +standard smtp-server emailadmin sv Standard SMTP server +the imap server does not appear to support the authentication method selected. please contact your system administrator. emailadmin sv IMAP servern verkar inte stödja den autentiserings metod du valt. Var god och kontakta administratör. +this php has no imap support compiled in!! emailadmin sv Denna installation har inte IMAP stöd kompilerat i PHP. +to use a tls connection, you must be running a version of php 5.1.0 or higher. emailadmin sv För att använda TLS anslutning mÃ¥ste du ha PHP version 5.1.0 eller högre +unexpected response from server to authenticate command. emailadmin sv Oväntat svar frÃ¥n servern pÃ¥ AUTENTISERINGS kommandot +unexpected response from server to digest-md5 response. emailadmin sv Oväntat svar frÃ¥n servern pÃ¥ Digest-MD5 +unexpected response from server to login command. emailadmin sv Oväntat svar frÃ¥n servern pÃ¥ INLOGGINGS kommandot +unknown imap response from the server. server responded: %s emailadmin sv Okänt IMAP svar frÃ¥n server. Svarade: %s +use ldap defaults emailadmin sv använd LDAP standarder +use smtp auth emailadmin sv Använd SMTP autentisering +use tls authentication emailadmin sv Använd TLS autentisering +use tls encryption emailadmin sv Använd TLS kryptering +users can define their own emailaccounts emailadmin sv Användare kan definiera egna epost konton? +virtual mail manager emailadmin sv Virtuell E-post administration diff --git a/emailadmin/lang/egw_tr.lang b/emailadmin/lang/egw_tr.lang new file mode 100644 index 0000000000..b806d24aed --- /dev/null +++ b/emailadmin/lang/egw_tr.lang @@ -0,0 +1,5 @@ +default emailadmin tr Varsayýlan +mail settings admin tr Mail ayarlarý +order emailadmin tr Sýralama +remove emailadmin tr Kaldýr +standard emailadmin tr standart diff --git a/emailadmin/lang/egw_uk.lang b/emailadmin/lang/egw_uk.lang new file mode 100644 index 0000000000..f7fe410c33 --- /dev/null +++ b/emailadmin/lang/egw_uk.lang @@ -0,0 +1,4 @@ +default emailadmin uk По замовченню +order emailadmin uk ПоÑлідовніÑÑ‚ÑŒ +remove emailadmin uk Видалити +standard emailadmin uk Ñтандарт diff --git a/emailadmin/lang/egw_vi.lang b/emailadmin/lang/egw_vi.lang new file mode 100644 index 0000000000..6dac79fd2f --- /dev/null +++ b/emailadmin/lang/egw_vi.lang @@ -0,0 +1 @@ +order emailadmin vi Thứ tự diff --git a/emailadmin/lang/egw_zh-tw.lang b/emailadmin/lang/egw_zh-tw.lang new file mode 100644 index 0000000000..c94bfdaf30 --- /dev/null +++ b/emailadmin/lang/egw_zh-tw.lang @@ -0,0 +1,127 @@ +account '%1' not found !!! emailadmin zh-tw 找ä¸åˆ°å¸³è™Ÿ '%1'ï¼ +add new email address: emailadmin zh-tw 新增信箱: +add profile emailadmin zh-tw 新增資料 +admin dn emailadmin zh-tw ç®¡ç† dn +admin password emailadmin zh-tw 管ç†å¯†ç¢¼ +admin username emailadmin zh-tw 管ç†å¸³è™Ÿ +advanced options emailadmin zh-tw 進階é¸é … +alternate email address emailadmin zh-tw 替代郵件ä½å€ +any application emailadmin zh-tw 任何模組 +any group emailadmin zh-tw 任何群組 +bad login name or password. emailadmin zh-tw 帳號或密碼有誤。 +bad or malformed request. server responded: %s emailadmin zh-tw 錯誤的請求,伺æœå™¨å›žæ‡‰ï¼š %s +bad request: %s emailadmin zh-tw 錯誤的請求: %s +can be used by application emailadmin zh-tw å¯ä»¥å–用資料的模組 +can be used by group emailadmin zh-tw å¯ä»¥å–用資料的群組 +connection dropped by imap server. emailadmin zh-tw 連線被 IMAP 伺æœå™¨ä¸­æ–·äº† +continue emailadmin zh-tw 繼續 +could not complete request. reason given: %s emailadmin zh-tw 無法完æˆè«‹æ±‚,ç†ç”±ï¼š %s +could not open secure connection to the imap server. %s : %s. emailadmin zh-tw 無法開啟安全連線到 IMAP 伺æœå™¨ã€‚%s : %s. +cram-md5 or digest-md5 requires the auth_sasl package to be installed. emailadmin zh-tw CRAM-MD5 或 DIGEST-MD5 需è¦å…ˆå®‰è£ Auth_SASL æ‰èƒ½ä½¿ç”¨ã€‚ +cyrus imap server emailadmin zh-tw Cyrus IMAP伺æœå™¨ +cyrus imap server administration emailadmin zh-tw Cyrus IMAP伺æœå™¨ç®¡ç† +default emailadmin zh-tw é è¨­ +deliver extern emailadmin zh-tw 傳é€åˆ°å¤–部 +do not validate certificate emailadmin zh-tw 沒有å¯ç”¨çš„執照 +do you really want to delete this profile emailadmin zh-tw 您確定è¦åˆªé™¤é€™å€‹è³‡æ–™ +domainname emailadmin zh-tw 網域å稱 +edit email settings emailadmin zh-tw 編輯郵件設定 +email account active emailadmin zh-tw 郵件帳號啟用 +email address emailadmin zh-tw 郵件ä½å€ +email settings common zh-tw 郵件設定 +emailadmin emailadmin zh-tw éƒµä»¶ç®¡ç† +enable cyrus imap server administration emailadmin zh-tw 啟用Cyrus IMAP伺æœå™¨ç®¡ç† +enable sieve emailadmin zh-tw 啟用Sieve +encrypted connection emailadmin zh-tw 加密連線 +encryption settings emailadmin zh-tw 加密設定 +enter your default mail domain (from: user@domain) emailadmin zh-tw 輸入您的é è¨­éƒµä»¶ç¶²åŸŸ(å°è€é¼ å¾Œé¢æ‰€æœ‰å­—:帳號@網域) +entry saved emailadmin zh-tw 資料儲存了 +error connecting to imap server. %s : %s. emailadmin zh-tw 連線到 IMAP 伺æœå™¨æ™‚發生錯誤。 %s : %s +error connecting to imap server: [%s] %s. emailadmin zh-tw 連線到 IMAP 伺æœå™¨æ™‚發生錯誤。 [%s] %s +error saving the entry!!! emailadmin zh-tw å„²å­˜è³‡æ–™æ™‚ç™¼ç”ŸéŒ¯èª¤ï¼ +forward also to emailadmin zh-tw åŒæ™‚轉寄到 +forward email's to emailadmin zh-tw 轉寄信件到 +forward only emailadmin zh-tw 轉寄 +global options emailadmin zh-tw 全域é¸é … +if using ssl or tls, you must have the php openssl extension loaded. emailadmin zh-tw 如果使用 SSL 或 TLS,您必須先載入 PHP openssl 外掛。 +imap admin password admin zh-tw IMAP管ç†è€…密碼 +imap admin user admin zh-tw IMAP管ç†è€…帳號 +imap c-client version < 2001 emailadmin zh-tw IMAP C-終端的版本å°æ–¼2001 +imap server emailadmin zh-tw IMAP伺æœå™¨ +imap server closed the connection. emailadmin zh-tw IMAP伺æœå™¨é—œé–‰é€£ç·š +imap server closed the connection. server responded: %s emailadmin zh-tw IMAP伺æœå™¨é—œé–‰é€£ç·šï¼Œä¼ºæœå™¨å›žæ‡‰ï¼š %s +imap server hostname or ip address emailadmin zh-tw IMAP伺æœå™¨çš„主機å稱或是IPä½å€ +imap server logintyp emailadmin zh-tw IMAP伺æœå™¨ç™»å…¥é¡žåž‹ +imap server name emailadmin zh-tw IMAP伺æœå™¨å稱 +imap server port emailadmin zh-tw IMAP伺æœå™¨é€£æŽ¥åŸ  +imap/pop3 server name emailadmin zh-tw IMAP/POP3伺æœå™¨å稱 +in mbyte emailadmin zh-tw 以MB顯示 +ldap basedn emailadmin zh-tw LDAP basedn +ldap server emailadmin zh-tw LDAP 伺æœå™¨ +ldap server accounts dn emailadmin zh-tw LDAP 伺æœå™¨å¸³è™Ÿ DN +ldap server admin dn emailadmin zh-tw LDAP 伺æœå™¨ç®¡ç†è€… DN +ldap server admin password emailadmin zh-tw LDAP 伺æœå™¨ç®¡ç†è€…密碼 +ldap server hostname or ip address emailadmin zh-tw LDAP 伺æœå™¨ä¸»æ©Ÿå稱或是IPä½å€ +ldap settings emailadmin zh-tw LDAP 設定 +leave empty for no quota emailadmin zh-tw ä¸å¡«å…¥ä»»ä½•è³‡æ–™è¡¨ç¤ºç„¡é™åˆ¶ +mail settings admin zh-tw 郵件設定 +name of organisation emailadmin zh-tw 組織å稱 +no alternate email address emailadmin zh-tw 沒有å¯æ›¿æ›çš„郵件ä½å€ +no encryption emailadmin zh-tw 沒有加密 +no forwarding email address emailadmin zh-tw 沒有轉寄郵件ä½å€ +no message returned. emailadmin zh-tw 沒有訊æ¯å›žæ‡‰ã€‚ +no supported imap authentication method could be found. emailadmin zh-tw 找ä¸åˆ°å¯ä»¥æ”¯æ´çš„ IMAP èªè­‰æ–¹å¼ã€‚ +order emailadmin zh-tw é †åº +organisation emailadmin zh-tw 組織 +plesk can't rename users --> request ignored emailadmin zh-tw Plesk 無法修改使用者å稱 --> 忽略 +plesk imap server (courier) emailadmin zh-tw Plesk IMAP 伺æœå™¨(Courier) +plesk mail script '%1' not found !!! emailadmin zh-tw 找ä¸åˆ° Plesk 郵件指令 '%1' ï¼ +plesk requires passwords to have at least 5 characters and not contain the account-name --> password not set!!! emailadmin zh-tw Plesk è¦æ±‚密碼至少必須有 5 個字元而且ä¸èƒ½åŒ…å«å¸³è™Ÿå稱 --> å¯†ç¢¼æ²’æœ‰è¨­å®šï¼ +plesk smtp-server (qmail) emailadmin zh-tw Plesk SMTP伺æœå™¨ (Qmail) +pop3 server hostname or ip address emailadmin zh-tw POP3伺æœå™¨ä¸»æ©Ÿå稱或是IPä½å€ +pop3 server port emailadmin zh-tw POP3伺æœå™¨é€£æŽ¥åŸ  +port emailadmin zh-tw 連接埠 +postfix with ldap emailadmin zh-tw 使用LDAP與 Postfix +profile access rights emailadmin zh-tw å­˜å–è³‡æ–™æ¬Šé™ +profile list emailadmin zh-tw 資料清單 +profile name emailadmin zh-tw 資料å稱 +qmaildotmode emailadmin zh-tw qmaildotmode +quota settings emailadmin zh-tw é…é¡è¨­å®š +quota size in mbyte emailadmin zh-tw é…é¡å¤§å°ï¼ˆMB) +remove emailadmin zh-tw 移除 +select type of imap server emailadmin zh-tw é¸æ“‡IMAP伺æœå™¨é¡žåž‹ +select type of imap/pop3 server emailadmin zh-tw é¸æ“‡IMAP/POP3伺æœå™¨çš„æ ¼å¼ +select type of smtp server emailadmin zh-tw é¸æ“‡SMTP伺æœå™¨çš„æ ¼å¼ +server settings emailadmin zh-tw 伺æœå™¨è¨­å®š +sieve server hostname or ip address emailadmin zh-tw Sieve伺æœå™¨ä¸»æ©Ÿå稱或是IPä½å€ +sieve server port emailadmin zh-tw Sieve伺æœå™¨é€£æŽ¥åŸ  +sieve settings emailadmin zh-tw Sieve設定 +smtp authentication emailadmin zh-tw SMTP èªè­‰ +smtp options emailadmin zh-tw SMTP é¸é … +smtp server name emailadmin zh-tw SMTP伺æœå™¨å稱 +smtp settings emailadmin zh-tw SMTP 設定 +smtp-server hostname or ip address emailadmin zh-tw SMTP伺æœå™¨ä¸»æ©Ÿå稱或是IPä½å€ +smtp-server port emailadmin zh-tw SMTP伺æœå™¨é€£æŽ¥åŸ  +standard emailadmin zh-tw 標準 +standard imap server emailadmin zh-tw 標準IMAP伺æœå™¨ +standard pop3 server emailadmin zh-tw 標準POP3伺æœå™¨ +standard smtp-server emailadmin zh-tw 標準SMTP伺æœå™¨ +the imap server does not appear to support the authentication method selected. please contact your system administrator. emailadmin zh-tw IMAP 伺æœå™¨ä¸æ”¯æ´æŒ‡å®šçš„èªè­‰æ–¹å¼ï¼Œè«‹è¯çµ¡æ‚¨çš„系統管ç†å“¡ã€‚ +this php has no imap support compiled in!! emailadmin zh-tw 您的PHP並未編譯為支æ´IMAP的狀態 +to use a tls connection, you must be running a version of php 5.1.0 or higher. emailadmin zh-tw è¦ä½¿ç”¨ TLS 連線,您必須執行在 PHP 5.1.0 或是更新版本。 +unexpected response from server to authenticate command. emailadmin zh-tw AUTHENTICATE 指令讓伺æœå™¨å‚³å›žä¸å¦‚é æœŸçš„回應。 +unexpected response from server to digest-md5 response. emailadmin zh-tw 伺æœå™¨å‚³å›žä¸å¦‚é æœŸçš„ Digest-MD5 回應。 +unexpected response from server to login command. emailadmin zh-tw 伺æœå™¨å‚³å›žä¸å¦‚é æœŸçš„ LOGIN 指令。 +unknown imap response from the server. server responded: %s emailadmin zh-tw ä¸çŸ¥åçš„ IMAP 伺æœå™¨å›žæ‡‰ï¼Œå›žæ‡‰å…§å®¹ï¼š %s +unsupported action '%1' !!! emailadmin zh-tw ä¸æ”¯æ´ '%1' 這個æ“ä½œï¼ +update current email address: emailadmin zh-tw æ›´æ–°ç›®å‰ä¿¡ç®±ï¼š +use ldap defaults emailadmin zh-tw 使用LDAPé è¨­å€¼ +use smtp auth emailadmin zh-tw 使用SMTPèªè­‰ +use tls authentication emailadmin zh-tw 使用TLSèªè­‰ +use tls encryption emailadmin zh-tw 使用TLS加密 +user can edit forwarding address emailadmin zh-tw 使用者å¯ä»¥ç·¨è¼¯è‡ªå‹•è½‰å¯„ä¿¡ç®± +username (standard) emailadmin zh-tw 帳號(標準) +username@domainname (virtual mail manager) emailadmin zh-tw 帳號@網域(虛擬郵件管ç†ï¼‰ +users can define their own emailaccounts emailadmin zh-tw 使用者å¯ä»¥è‡ªè¡Œå®šç¾©éƒµä»¶å¸³è™Ÿ +virtual mail manager emailadmin zh-tw 虛擬郵件管ç†è€… +your name emailadmin zh-tw 您的å稱 diff --git a/emailadmin/lang/egw_zh.lang b/emailadmin/lang/egw_zh.lang new file mode 100644 index 0000000000..a4b850d303 --- /dev/null +++ b/emailadmin/lang/egw_zh.lang @@ -0,0 +1,128 @@ +account '%1' not found !!! emailadmin zh å¸æˆ· '%1' 未å‘çŽ°ï¼ +add new email address: emailadmin zh 添加新邮箱地å€ï¼š +add profile emailadmin zh 添加 profile +admin dn emailadmin zh ç®¡ç† DN +admin password emailadmin zh 管ç†å¯†ç  +admin username emailadmin zh 管ç†å¸æˆ· +advanced options emailadmin zh 高级选项 +alternate email address emailadmin zh å€™é€‰é‚®ç®±åœ°å€ +any application emailadmin zh ä»»ä½•ç”¨ç”¨ç¨‹åº +any group emailadmin zh 任何组 +bad login name or password. emailadmin zh 错误登录å和密ç ã€‚ +bad or malformed request. server responded: %s emailadmin zh ä¸æ­£ç¡®çš„请求。æœåŠ¡å™¨æˆ–应:%s +bad request: %s emailadmin zh 错误请求:%s +can be used by application emailadmin zh å¯ç”¨äºŽåº”ç”¨ç¨‹åº +can be used by group emailadmin zh å¯ç”¨äºŽç¾¤ç»„ +connection dropped by imap server. emailadmin zh 连接被 IMAP æœåŠ¡å™¨ä¸­æ–­ã€‚ +continue emailadmin zh 继续 +could not complete request. reason given: %s emailadmin zh 无法完æˆè¯·æ±‚。原因是:%s +could not open secure connection to the imap server. %s : %s. emailadmin zh 无法打开到 IMAP æœåŠ¡å™¨çš„安全连接。%s:%s。 +cram-md5 or digest-md5 requires the auth_sasl package to be installed. emailadmin zh CRAM-MD5 或 DIGEST-MD5 需è¦å®‰è£… auth_sasl 包。 +cyrus imap server emailadmin zh Cyrus IMAP æœåŠ¡å™¨ +cyrus imap server administration emailadmin zh Cyrus IMAP æœåŠ¡å™¨ç®¡ç† +default emailadmin zh 默认 +deliver extern emailadmin zh ä¼ é€å¤–部 +do not validate certificate emailadmin zh ä¸ç¡®è®¤è¯ä¹¦ +do you really want to delete this profile emailadmin zh 您确定è¦åˆ é™¤è¿™ä¸ª profile 文件 +domainname emailadmin zh 域å +edit email settings emailadmin zh 编辑邮箱设置 +email account active emailadmin zh 邮箱å¸æˆ·æ¿€æ´» +email address emailadmin zh é‚®ç®±åœ°å€ +email settings common zh 邮箱设置 +emailadmin emailadmin zh é‚®ç®±ç®¡ç† +enable cyrus imap server administration emailadmin zh å¯ç”¨ Cyrus IMAP æœåŠ¡å™¨ç®¡ç† +enable sieve emailadmin zh å¯ç”¨è¿‡æ»¤ +encrypted connection emailadmin zh 加密连接 +encryption settings emailadmin zh 加密设置 +enter your default mail domain (from: user@domain) emailadmin zh 输入您的默认邮箱域 (比如:user@domain,å–@之åŽçš„所有è¯æˆ–å­—æ¯) +entry saved emailadmin zh æ¡ç›®å·²å‚¨å­˜ +error connecting to imap server. %s : %s. emailadmin zh 连接到 IMAP æœåŠ¡å™¨é”™è¯¯ã€‚%s : %s。 +error connecting to imap server: [%s] %s. emailadmin zh 连接到 IMAP æœåŠ¡å™¨é”™è¯¯ï¼š[%s] %s。 +error saving the entry!!! emailadmin zh ä¿å­˜æ¡ç›®æ—¶å‘ç”Ÿé”™è¯¯ï¼ +forward also to emailadmin zh åŒæ—¶è½¬å‘到 +forward email's to emailadmin zh 转å‘邮件到 +forward only emailadmin zh è½¬å‘ +global options emailadmin zh 全局选项 +if using ssl or tls, you must have the php openssl extension loaded. emailadmin zh 如果使用 SSL 或 TLS,您必须加载 PHP openssl 扩展。 +imap admin password admin zh IMAP 管ç†è€…å¯†ç  +imap admin user admin zh IMAP 管ç†è€…å¸æˆ· +imap c-client version < 2001 emailadmin zh IMAP C-Clien ç‰ˆæœ¬å° < 2001 +imap server emailadmin zh IMAP æœåŠ¡å™¨ +imap server closed the connection. emailadmin zh IMAP æœåŠ¡å™¨å…³é—­è¿žæŽ¥ã€‚ +imap server closed the connection. server responded: %s emailadmin zh IMAP æœåŠ¡å™¨å…³é—­è¿žæŽ¥ã€‚æœåŠ¡å™¨å›žåº”:%s +imap server hostname or ip address emailadmin zh IMAP æœåŠ¡å™¨çš„主机å或 IP åœ°å€ +imap server logintyp emailadmin zh IMAP æœåŠ¡å™¨ç™»å½•ç±»åž‹ +imap server name emailadmin zh IMAP æœåŠ¡å™¨å +imap server port emailadmin zh IMAP æœåŠ¡å™¨ç«¯å£å· +imap/pop3 server name emailadmin zh IMAP / POP3 æœåŠ¡å™¨å +in mbyte emailadmin zh 以 MB 表示 +ldap basedn emailadmin zh LDAP basedn +ldap server emailadmin zh LDAP æœåŠ¡å™¨ +ldap server accounts dn emailadmin zh LDAP æœåŠ¡å™¨å¸æˆ· DN +ldap server admin dn emailadmin zh LDAP æœåŠ¡å™¨ç®¡ç†å‘˜ DN +ldap server admin password emailadmin zh LDAP æœåŠ¡å™¨ç®¡ç®¡ç†å‘˜å¯†ç  +ldap server hostname or ip address emailadmin zh LDAP æœåŠ¡å™¨ä¸»æœºå或 IP åœ°å€ +ldap settings emailadmin zh LDAP 设置 +leave empty for no quota emailadmin zh 留空表示无é™é¢ +mail settings admin zh 邮箱设置 +name of organisation emailadmin zh 组织å称 +no alternate email address emailadmin zh æ— å¤‡ç”¨é‚®ç®±åœ°å€ +no encryption emailadmin zh 未加密 +no forwarding email address emailadmin zh 没有转å‘é‚®ä»¶åœ°å€ +no message returned. emailadmin zh 无返回消æ¯ã€‚ +no supported imap authentication method could be found. emailadmin zh æ— æ”¯æŒ IMAP 认è¯çš„方法å¯ä»¥æ‰¾åˆ°ã€‚ +order emailadmin zh æŽ’åº +organisation emailadmin zh 组织 +plesk can't rename users --> request ignored emailadmin zh Plesk ä¸èƒ½é‡å‘½å用户 --> 请求忽略 +plesk imap server (courier) emailadmin zh Plesk IMAP æœåŠ¡å™¨ (Courier) +plesk mail script '%1' not found !!! emailadmin zh Plesk 邮件脚本 '%1' æœªæ‰¾åˆ°ï¼ +plesk requires passwords to have at least 5 characters and not contain the account-name --> password not set!!! emailadmin zh Plesk 需è¦è‡³å°‘5个字符密ç å¹¶ä¸”ä¸åŒ…å«å¸æˆ·å --> 密ç æœªè®¾ç½®ï¼ +plesk smtp-server (qmail) emailadmin zh Plesk SMTP-Server (Qmail) +pop3 server hostname or ip address emailadmin zh POP3 æœåŠ¡å™¨ä¸»æœºå或 IP åœ°å€ +pop3 server port emailadmin zh POP3 æœåŠ¡å™¨ç«¯å£å· +port emailadmin zh ç«¯å£ +postfix with ldap emailadmin zh LDAP 用于 Postfix +profile access rights emailadmin zh profile 访问æƒé™ +profile list emailadmin zh profile 列表 +profile name emailadmin zh profile å +qmaildotmode emailadmin zh qmaildotmode +quota settings emailadmin zh é…é¢è®¾ç½® +quota size in mbyte emailadmin zh é…é¢å¤§å°(MB) +remove emailadmin zh 移除 +select type of imap server emailadmin zh 选择 IMAP æœåŠ¡å™¨ç±»åž‹ +select type of imap/pop3 server emailadmin zh 选择 IMAP / POP3 æœåŠ¡å™¨ç±»åž‹ +select type of smtp server emailadmin zh 选择 SMAP æœåŠ¡å™¨ç±»åž‹ +server settings emailadmin zh æœåŠ¡å™¨è®¾ç½® +sieve server hostname or ip address emailadmin zh Sieve æœåŠ¡å™¨ä¸»æœºå或 IP åœ°å€ +sieve server port emailadmin zh Sieve æœåŠ¡å™¨ç«¯å£å· +sieve settings emailadmin zh Sieve 设置 +smtp authentication emailadmin zh SMTP è®¤è¯ +smtp options emailadmin zh SMTP 选项 +smtp server name emailadmin zh SMTP æœåŠ¡å™¨å +smtp settings emailadmin zh SMTP 设置 +smtp-server hostname or ip address emailadmin zh SMTP æœåŠ¡å™¨ä¸»æœºå或 IP åœ°å€ +smtp-server port emailadmin zh SMTP æœåŠ¡å™¨ç«¯å£å· +standard emailadmin zh 标准 +standard imap server emailadmin zh 标准 IMAP æœåŠ¡å™¨ +standard pop3 server emailadmin zh 标准 POP3 æœåŠ¡å™¨ +standard smtp-server emailadmin zh 标准 SMTP æœåŠ¡å™¨ +the imap server does not appear to support the authentication method selected. please contact your system administrator. emailadmin zh IMAP æœåŠ¡å™¨ä¼¼ä¹Žä¸æ”¯æŒæ‰€é€‰æ‹©çš„认è¯æ–¹æ³•ã€‚请è”系系统管ç†å‘˜ã€‚ +this php has no imap support compiled in!! emailadmin zh PHP 没有 IMAP 支æŒçš„ç¼–è¯‘ï¼ +to use a tls connection, you must be running a version of php 5.1.0 or higher. emailadmin zh 未使用一个 TLS 连接,您必须è¿è¡Œ PHP 5.1.0 或更高版本。 +unexpected response from server to authenticate command. emailadmin zh æœåŠ¡å™¨ä¸º AUTHENTICATE 指令返回ä¸å½“以外回应。 +unexpected response from server to digest-md5 response. emailadmin zh æœåŠ¡å™¨ä¸º Digest-MD5 返回æ„外的ä¸å½“的回应。 +unexpected response from server to login command. emailadmin zh æœåŠ¡å™¨ä¸º LOGIN 指令返回æ„外的ä¸å½“回应。 +unknown imap response from the server. server responded: %s emailadmin zh INAP æœåŠ¡å™¨è¿”回未知回应。æœåŠ¡å™¨å›žåº”:%s +unsupported action '%1' !!! emailadmin zh ä¸æ”¯æŒ '%1' çš„æ“ä½œï¼ +update current email address: emailadmin zh 更新当å‰é‚®ä»¶åœ°å€ï¼š +use ldap defaults emailadmin zh 使用 LDAP 默认值 +use smtp auth emailadmin zh 使用 SMTP è®¤è¯ +use tls authentication emailadmin zh 使用TLS è®¤è¯ +use tls encryption emailadmin zh 使用TLS 加密 +user can edit forwarding address emailadmin zh 用户å¯ä»¥ç¼–辑转å‘åœ°å€ +username (standard) emailadmin zh 用户å(标准) +username@domainname (virtual mail manager) emailadmin zh 用户å@域 (虚拟邮箱管ç†) +users can define their own emailaccounts emailadmin zh 用户å¯ä»¥è‡ªå®šä¹‰é‚®ç®±è´¦æˆ· +users can define their own signatures emailadmin zh 用户å¯ä»¥å®šä¹‰ä»–们自己的签å +virtual mail manager emailadmin zh è™šæ‹Ÿé‚®ç®±ç®¡ç† +your name emailadmin zh 您的åå­— diff --git a/emailadmin/setup/etemplates.inc.php b/emailadmin/setup/etemplates.inc.php new file mode 100644 index 0000000000..c5b4155700 --- /dev/null +++ b/emailadmin/setup/etemplates.inc.php @@ -0,0 +1,100 @@ + 'emailadmin.account','template' => '','lang' => '','group' => '0','version' => '1.9.001','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:6:{i:0;a:5:{s:1:"A";s:3:"120";s:1:"B";s:3:"200";s:2:"h2";s:2:"30";s:2:"h3";s:2:"30";s:2:"c3";s:18:"emailadmin_no_user";}i:1;a:3:{s:1:"A";a:3:{s:4:"span";s:11:"all,message";s:4:"name";s:3:"msg";s:4:"type";s:5:"label";}s:1:"B";a:1:{s:4:"type";s:5:"label";}s:1:"C";a:1:{s:4:"type";s:5:"label";}}i:2;a:3:{s:1:"A";a:3:{s:3:"for";s:8:"acc_name";s:4:"type";s:5:"label";s:5:"label";s:15:"Name of account";}s:1:"B";a:5:{s:4:"type";s:4:"hbox";s:4:"size";s:6:"2,,0,0";i:1;a:4:{s:6:"needed";s:1:"1";s:4:"size";s:2:"80";s:4:"name";s:8:"acc_name";s:4:"type";s:4:"text";}i:2;a:4:{s:4:"type";s:5:"label";s:4:"name";s:6:"acc_id";s:4:"span";s:22:",emailadmin_diagnostic";s:5:"align";s:5:"right";}s:4:"span";s:3:"all";}s:1:"C";a:1:{s:4:"type";s:5:"label";}}i:3;a:3:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:5:"label";s:9:"Valid for";s:4:"size";s:13:",,,account_id";}s:1:"B";a:4:{s:4:"type";s:4:"hbox";s:4:"size";s:6:"2,,0,0";i:1;a:4:{s:4:"name";s:10:"account_id";s:4:"type";s:14:"select-account";s:4:"size";s:13:"Everyone,both";s:8:"onchange";s:40:"app.emailadmin.account_hide_not_applying";}i:2;a:5:{s:5:"label";s:15:"Select multiple";s:7:"onclick";s:28:"app.emailadmin.edit_multiple";s:4:"size";s:5:"users";s:4:"type";s:10:"buttononly";s:4:"name";s:16:"button[multiple]";}}s:1:"C";a:3:{s:4:"type";s:8:"checkbox";s:5:"label";s:24:"account editable by user";s:4:"name";s:17:"acc_user_editable";}}i:4;a:3:{s:1:"A";a:5:{s:4:"type";s:3:"tab";s:4:"span";s:3:"all";s:5:"label";s:41:"Identity+Signature|IMAP|Folder|Sieve|SMTP";s:4:"name";s:36:"tabs=identity|imap|folder|sieve|smtp";s:4:"help";s:74:"Identity&Signature|incoming mail|Folder|serverside filtering|outgoing mail";}s:1:"B";a:1:{s:4:"type";s:5:"label";}s:1:"C";a:1:{s:4:"type";s:5:"label";}}i:5;a:3:{s:1:"A";a:7:{s:4:"span";s:1:"2";s:4:"type";s:4:"hbox";s:4:"size";s:1:"4";i:1;a:3:{s:5:"label";s:4:"Save";s:4:"name";s:12:"button[save]";s:4:"type";s:6:"button";}i:2;a:3:{s:5:"label";s:5:"Apply";s:4:"name";s:13:"button[apply]";s:4:"type";s:6:"button";}i:3;a:4:{s:4:"type";s:6:"button";s:5:"label";s:6:"Wizard";s:4:"name";s:14:"button[wizard]";s:4:"help";s:44:"Use wizard to detect or verify configuration";}i:4;a:4:{s:5:"label";s:6:"Cancel";s:7:"onclick";s:15:"window.close();";s:4:"name";s:14:"button[cancel]";s:4:"type";s:6:"button";}}s:1:"B";a:1:{s:4:"type";s:5:"label";}s:1:"C";a:5:{s:4:"type";s:6:"button";s:5:"label";s:6:"Delete";s:5:"align";s:5:"right";s:4:"name";s:14:"button[delete]";s:7:"onclick";s:38:"return confirm(\'Delete this account\');";}}}s:4:"cols";i:3;s:4:"rows";i:5;s:4:"size";s:4:"100%";s:7:"options";a:1:{i:0;s:4:"100%";}}}','size' => '100%','style' => '','modified' => '1383410466',); + +$templ_data[] = array('name' => 'emailadmin.account.folder','template' => '','lang' => '','group' => '0','version' => '1.9.001','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:6:{i:0;a:2:{s:1:"A";s:3:"115";s:2:"h5";s:3:"100";}i:1;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:18:",,,acc_folder_sent";s:5:"label";s:11:"Sent folder";}s:1:"B";a:3:{s:4:"type";s:6:"select";s:4:"size";s:14:"Select one ...";s:4:"name";s:15:"acc_folder_sent";}}i:2;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:19:",,,acc_folder_trash";s:5:"label";s:12:"Trash folder";}s:1:"B";a:3:{s:4:"type";s:6:"select";s:4:"size";s:14:"Select one ...";s:4:"name";s:16:"acc_folder_trash";}}i:3;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:19:",,,acc_folder_draft";s:5:"label";s:13:"Drafts folder";}s:1:"B";a:3:{s:4:"type";s:6:"select";s:4:"size";s:14:"Select one ...";s:4:"name";s:16:"acc_folder_draft";}}i:4;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:22:",,,acc_folder_template";s:5:"label";s:16:"Templates folder";}s:1:"B";a:3:{s:4:"type";s:6:"select";s:4:"size";s:14:"Select one ...";s:4:"name";s:19:"acc_folder_template";}}i:5;a:2:{s:1:"A";a:1:{s:4:"type";s:5:"label";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}}s:4:"rows";i:5;s:4:"cols";i:2;s:4:"size";s:8:"100%,300";s:7:"options";a:2:{i:0;s:4:"100%";i:1;s:3:"300";}}}','size' => '100%,300','style' => '','modified' => '1383411107',); + +$templ_data[] = array('name' => 'emailadmin.account.identity','template' => '','lang' => '','group' => '0','version' => '1.9.001','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:6:{i:0;a:2:{s:1:"A";s:3:"113";s:1:"B";s:3:"200";}i:1;a:3:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:5:"label";s:8:"Identity";s:4:"size";s:11:",,,ident_id";}s:1:"B";a:4:{s:4:"type";s:4:"hbox";s:4:"size";s:6:"2,,0,0";i:1;a:4:{s:4:"type";s:6:"select";s:8:"onchange";i:1;s:7:"no_lang";s:1:"1";s:4:"name";s:8:"ident_id";}i:2;a:5:{s:4:"type";s:6:"button";s:4:"name";s:23:"button[delete_identity]";s:4:"size";s:6:"delete";s:5:"label";s:15:"Delete identity";s:7:"onclick";s:34:"return confirm(\'Delete identity\');";}}s:1:"C";a:4:{s:4:"type";s:8:"checkbox";s:5:"label";s:40:"allow users to create further identities";s:4:"name";s:22:"acc_further_identities";s:4:"span";s:19:",emailadmin_no_user";}}i:2;a:3:{s:1:"A";a:3:{s:3:"for";s:14:"ident_realname";s:4:"type";s:5:"label";s:5:"label";s:9:"Your name";}s:1:"B";a:5:{s:4:"type";s:4:"hbox";s:4:"size";s:6:"2,,0,0";s:4:"span";s:3:"all";i:1;a:3:{s:4:"size";s:6:"80,128";s:4:"name";s:14:"ident_realname";s:4:"type";s:4:"text";}i:2;a:5:{s:4:"type";s:10:"buttononly";s:4:"size";s:11:"dialog_help";s:5:"label";s:12:"Placeholders";s:7:"onclick";s:195:"window.open(egw::link(\'/index.php\',\'menuaction=addressbook.addressbook_merge.show_replacements&nonavbar=1\'),\'_blank\',\'dependent=yes,width=860,height=620,scrollbars=yes,status=yes\'); return false;";s:4:"name";s:20:"button[placeholders]";}}s:1:"C";a:1:{s:4:"type";s:5:"label";}}i:3;a:3:{s:1:"A";a:3:{s:3:"for";s:9:"ident_org";s:4:"type";s:5:"label";s:5:"label";s:12:"Organisation";}s:1:"B";a:4:{s:4:"size";s:6:"80,128";s:4:"name";s:9:"ident_org";s:4:"type";s:4:"text";s:4:"span";s:3:"all";}s:1:"C";a:1:{s:4:"type";s:5:"label";}}i:4;a:3:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:5:"label";s:13:"EMail address";s:4:"size";s:14:",,,ident_email";}s:1:"B";a:4:{s:4:"name";s:11:"ident_email";s:4:"type";s:9:"url-email";s:4:"size";s:6:"80,128";s:4:"span";s:3:"all";}s:1:"C";a:1:{s:4:"type";s:5:"label";}}i:5;a:3:{s:1:"A";a:4:{s:4:"type";s:8:"htmlarea";s:4:"name";s:15:"ident_signature";s:4:"size";s:17:"simple,155,,false";s:4:"span";s:3:"all";}s:1:"B";a:1:{s:4:"type";s:5:"label";}s:1:"C";a:1:{s:4:"type";s:5:"label";}}}s:4:"cols";i:3;s:4:"rows";i:5;s:4:"size";s:8:"100%,300";s:7:"options";a:2:{i:0;s:4:"100%";i:1;s:3:"300";}}}','size' => '100%,300','style' => '','modified' => '1383642607',); + +$templ_data[] = array('name' => 'emailadmin.account.imap','template' => '','lang' => '','group' => '0','version' => '1.9.001','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:7:{i:0;a:7:{s:1:"A";s:3:"115";s:2:"c4";s:13:"@manual_class";s:2:"c2";s:13:"@manual_class";s:1:"B";s:3:"200";s:1:"C";s:2:"60";s:2:"c1";s:20:"emailadmin_no_single";s:2:"c6";s:20:"emailadmin_no_single";}i:1;a:4:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:21:",,,acc_imap_logintype";s:5:"label";s:4:"Type";}s:1:"B";a:2:{s:4:"type";s:6:"select";s:4:"name";s:13:"acc_imap_type";}s:1:"C";a:4:{s:4:"type";s:5:"label";s:4:"size";s:21:",,,acc_imap_logintype";s:5:"label";s:5:"Login";s:4:"span";s:21:",emailadmin_no_single";}s:1:"D";a:4:{s:4:"type";s:6:"select";s:4:"name";s:18:"acc_imap_logintype";s:4:"help";s:28:"How username get constructed";s:4:"span";s:21:",emailadmin_no_single";}}i:2;a:4:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:20:",,,acc_imap_username";s:5:"label";s:8:"Username";}s:1:"B";a:3:{s:4:"type";s:4:"text";s:4:"size";s:6:"32,128";s:4:"name";s:17:"acc_imap_username";}s:1:"C";a:4:{s:4:"type";s:5:"label";s:4:"size";s:13:",,,acc_domain";s:5:"label";s:6:"Domain";s:4:"span";s:21:",emailadmin_no_single";}s:1:"D";a:4:{s:4:"type";s:4:"text";s:4:"size";s:6:"32,128";s:4:"name";s:10:"acc_domain";s:4:"span";s:21:",emailadmin_no_single";}}i:3;a:4:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:20:",,,acc_imap_password";s:5:"label";s:8:"Password";}s:1:"B";a:3:{s:4:"type";s:6:"passwd";s:4:"size";s:6:"32,128";s:4:"name";s:17:"acc_imap_password";}s:1:"C";a:3:{s:4:"type";s:5:"label";s:4:"span";s:22:",emailadmin_diagnostic";s:4:"name";s:19:"acc_imap_account_id";}s:1:"D";a:1:{s:4:"type";s:5:"label";}}i:4;a:4:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:16:",,,acc_imap_host";s:5:"label";s:11:"IMAP server";}s:1:"B";a:6:{s:4:"type";s:4:"text";s:4:"size";s:6:"32,128";s:4:"name";s:13:"acc_imap_host";s:4:"blur";s:14:"Hostname or IP";s:6:"needed";s:1:"1";s:4:"span";s:3:"all";}s:1:"C";a:1:{s:4:"type";s:5:"label";}s:1:"D";a:1:{s:4:"type";s:5:"label";}}i:5;a:4:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:15:",,,acc_imap_ssl";s:5:"label";s:17:"Secure connection";}s:1:"B";a:4:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"2";i:1;a:5:{s:4:"type";s:6:"select";s:4:"name";s:12:"acc_imap_ssl";s:8:"onchange";s:39:"app.emailadmin.wizard_imap_ssl_onchange";s:6:"needed";s:1:"1";s:4:"span";s:15:",emailadmin_ssl";}i:2;a:5:{s:4:"type";s:3:"int";s:4:"name";s:13:"acc_imap_port";s:5:"label";s:4:"Port";s:6:"needed";s:1:"1";s:4:"span";s:16:",emailadmin_port";}}s:1:"C";a:1:{s:4:"type";s:5:"label";}s:1:"D";a:1:{s:4:"type";s:5:"label";}}i:6;a:4:{s:1:"A";a:5:{s:4:"type";s:8:"groupbox";s:4:"size";s:1:"1";s:4:"span";s:25:"all,emailadmin_imap_admin";s:5:"label";s:19:"IMAP administration";i:1;a:5:{s:4:"type";s:4:"grid";s:4:"data";a:3:{i:0;a:1:{s:1:"A";s:3:"100";}i:1;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:5:"label";s:10:"Admin user";s:4:"size";s:26:",,,acc_imap_admin_username";}s:1:"B";a:3:{s:4:"type";s:4:"text";s:4:"size";s:6:"32,128";s:4:"name";s:23:"acc_imap_admin_username";}}i:2;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:26:",,,acc_imap_admin_password";s:5:"label";s:8:"Password";}s:1:"B";a:3:{s:4:"type";s:6:"passwd";s:4:"size";s:6:"32,128";s:4:"name";s:23:"acc_imap_admin_password";}}}s:4:"rows";i:2;s:4:"cols";i:2;s:7:"options";a:0:{}}}s:1:"B";a:1:{s:4:"type";s:5:"label";}s:1:"C";a:1:{s:4:"type";s:5:"label";}s:1:"D";a:1:{s:4:"type";s:5:"label";}}}s:4:"rows";i:6;s:4:"cols";i:4;s:4:"size";s:8:"100%,300";s:7:"options";a:2:{i:0;s:4:"100%";i:1;s:3:"300";}}}','size' => '100%,300','style' => '','modified' => '1383410923',); + +$templ_data[] = array('name' => 'emailadmin.account.sieve','template' => '','lang' => '','group' => '0','version' => '1.9.001','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:5:{i:0;a:4:{s:1:"A";s:3:"120";s:2:"c3";s:13:"@manual_class";s:2:"c2";s:13:"@manual_class";s:2:"h4";s:3:"150";}i:1;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:20:",,,acc_sieve_enabled";s:5:"label";s:12:"Enable Sieve";}s:1:"B";a:3:{s:4:"type";s:11:"select-bool";s:4:"name";s:17:"acc_sieve_enabled";s:6:"needed";s:1:"1";}}i:2;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:17:",,,acc_sieve_host";s:5:"label";s:12:"Sieve server";}s:1:"B";a:5:{s:4:"type";s:4:"text";s:4:"size";s:6:"32,128";s:4:"name";s:14:"acc_sieve_host";s:4:"blur";s:14:"Hostname or IP";s:8:"onchange";s:36:"app.emailadmin.wizard_sieve_onchange";}}i:3;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:16:",,,acc_sieve_ssl";s:5:"label";s:17:"Secure connection";}s:1:"B";a:4:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"2";i:1;a:4:{s:4:"type";s:6:"select";s:4:"name";s:13:"acc_sieve_ssl";s:8:"onchange";s:40:"app.emailadmin.wizard_sieve_ssl_onchange";s:4:"span";s:15:",emailadmin_ssl";}i:2;a:5:{s:4:"type";s:3:"int";s:4:"name";s:14:"acc_sieve_port";s:5:"label";s:4:"Port";s:8:"onchange";s:36:"app.emailadmin.wizard_sieve_onchange";s:4:"span";s:16:",emailadmin_port";}}}i:4;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:5:"label";s:77:"Vacation messages with start and end date require an admin account to be set!";s:4:"span";s:24:"all,emailadmin_no_single";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}}s:4:"rows";i:4;s:4:"cols";i:2;s:4:"size";s:8:"100%,300";s:7:"options";a:2:{i:0;s:4:"100%";i:1;s:3:"300";}}}','size' => '100%,300','style' => '','modified' => '1383411147',); + +$templ_data[] = array('name' => 'emailadmin.account.smtp','template' => '','lang' => '','group' => '0','version' => '1.9.001','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:8:{i:0;a:8:{s:1:"A";s:3:"115";s:2:"c4";s:13:"@manual_class";s:2:"c3";s:13:"@manual_class";s:2:"c5";s:13:"@manual_class";s:2:"h7";s:2:"50";s:2:"c6";s:13:"@manual_class";s:2:"c1";s:20:"emailadmin_no_single";s:2:"c2";s:20:"emailadmin_no_single";}i:1;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:16:",,,acc_smtp_type";s:5:"label";s:4:"Type";}s:1:"B";a:2:{s:4:"type";s:6:"select";s:4:"name";s:13:"acc_smtp_type";}}i:2;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:24:",,,acc_smtp_auth_session";s:5:"label";s:14:"Authentication";}s:1:"B";a:3:{s:4:"type";s:8:"checkbox";s:5:"label";s:39:"Use username+password from current user";s:4:"name";s:21:"acc_smtp_auth_session";}}i:3;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:20:",,,acc_smtp_username";s:5:"label";s:8:"Username";}s:1:"B";a:4:{s:4:"type";s:4:"text";s:4:"size";s:6:"32,128";s:4:"name";s:17:"acc_smtp_username";s:4:"blur";s:26:"if authentication required";}}i:4;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:20:",,,acc_smtp_password";s:5:"label";s:8:"Password";}s:1:"B";a:4:{s:4:"type";s:4:"hbox";s:4:"size";s:6:"2,,0,0";i:1;a:3:{s:4:"type";s:6:"passwd";s:4:"size";s:6:"32,128";s:4:"name";s:17:"acc_smtp_password";}i:2;a:3:{s:4:"type";s:5:"label";s:4:"name";s:19:"acc_smtp_account_id";s:4:"span";s:22:",emailadmin_diagnostic";}}}i:5;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:16:",,,acc_smtp_host";s:5:"label";s:11:"SMTP server";}s:1:"B";a:5:{s:4:"type";s:4:"text";s:4:"size";s:6:"32,128";s:4:"name";s:13:"acc_smtp_host";s:4:"blur";s:14:"Hostname or IP";s:6:"needed";s:1:"1";}}i:6;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:15:",,,acc_smtp_ssl";s:5:"label";s:17:"Secure connection";}s:1:"B";a:4:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"2";i:1;a:5:{s:4:"type";s:6:"select";s:4:"name";s:12:"acc_smtp_ssl";s:8:"onchange";s:39:"app.emailadmin.wizard_smtp_ssl_onchange";s:6:"needed";s:1:"1";s:4:"span";s:15:",emailadmin_ssl";}i:2;a:5:{s:4:"type";s:3:"int";s:4:"name";s:13:"acc_smtp_port";s:5:"label";s:4:"Port";s:6:"needed";s:1:"1";s:4:"span";s:16:",emailadmin_port";}}}i:7;a:2:{s:1:"A";a:1:{s:4:"type";s:5:"label";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}}s:4:"rows";i:7;s:4:"cols";i:2;s:4:"size";s:8:"100%,300";s:7:"options";a:2:{i:0;s:4:"100%";i:1;s:3:"300";}}}','size' => '100%,300','style' => '','modified' => '1383411195',); + +$templ_data[] = array('name' => 'emailadmin.edit','template' => '','lang' => '','group' => '0','version' => '1.7.003','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:5:{i:0;a:2:{s:2:"h1";s:6:",!@msg";s:1:"A";s:4:"100%";}i:1;a:1:{s:1:"A";a:4:{s:4:"type";s:5:"label";s:5:"align";s:6:"center";s:4:"name";s:3:"msg";s:4:"span";s:10:",redItalic";}}i:2;a:1:{s:1:"A";a:6:{s:4:"type";s:4:"grid";s:4:"data";a:2:{i:0;a:3:{s:1:"C";s:3:"50%";s:1:"B";s:3:"40%";s:1:"D";s:2:"5%";}i:1;a:4:{s:1:"A";a:4:{s:4:"type";s:4:"text";s:5:"label";s:2:"ID";s:4:"name";s:13:"ea_profile_id";s:8:"readonly";s:1:"1";}s:1:"B";a:2:{s:4:"type";s:5:"label";s:5:"label";s:12:"Profile Name";}s:1:"C";a:3:{s:4:"type";s:4:"text";s:4:"name";s:14:"ea_description";s:5:"align";s:5:"right";}s:1:"D";a:2:{s:4:"type";s:5:"label";s:5:"align";s:5:"right";}}}s:4:"rows";i:1;s:4:"cols";i:4;s:4:"size";s:3:"98%";s:7:"options";a:1:{i:0;s:3:"98%";}}}i:3;a:1:{s:1:"A";a:3:{s:4:"type";s:3:"tab";s:5:"label";s:45:"Global|SMTP|IMAP|Signature|Stationery|History";s:4:"name";s:50:"tabs=global|SMTP|IMAP|signature|stationery|history";}}i:4;a:1:{s:1:"A";a:4:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"2";i:1;a:5:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"3";i:1;a:3:{s:4:"type";s:6:"button";s:4:"name";s:4:"save";s:5:"label";s:4:"Save";}i:2;a:3:{s:4:"type";s:6:"button";s:5:"label";s:5:"Apply";s:4:"name";s:5:"apply";}i:3;a:3:{s:4:"type";s:6:"button";s:5:"label";s:6:"Cancel";s:4:"name";s:6:"cancel";}}i:2;a:5:{s:4:"type";s:6:"button";s:4:"name";s:6:"delete";s:5:"label";s:6:"Delete";s:5:"align";s:5:"right";s:7:"onclick";s:60:"return confirm(\'Do you really want to delete this Profile\');";}}}}s:4:"rows";i:4;s:4:"cols";i:1;s:4:"size";s:4:"100%";s:7:"options";a:1:{i:0;s:4:"100%";}}}','size' => '100%','style' => '.redItalic { color: red; font-style: italics; }','modified' => '1255612671',); + +$templ_data[] = array('name' => 'emailadmin.edit.global','template' => '','lang' => '','group' => '0','version' => '1.9.002','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:4:{i:0;a:0:{}i:1;a:1:{s:1:"A";a:4:{s:4:"type";s:8:"groupbox";s:5:"label";s:12:"Organisation";s:4:"size";s:1:"1";i:1;a:5:{s:4:"type";s:4:"grid";s:4:"data";a:3:{i:0;a:0:{}i:1;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:50:"enter your default mail domain (from: user@domain)";}s:1:"B";a:2:{s:4:"type";s:4:"text";s:4:"name";s:17:"ea_default_domain";}}i:2;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:20:"name of organisation";}s:1:"B";a:2:{s:4:"type";s:4:"text";s:4:"name";s:20:"ea_organisation_name";}}}s:4:"rows";i:2;s:4:"cols";i:2;s:7:"options";a:0:{}}}}i:2;a:1:{s:1:"A";a:4:{s:4:"type";s:8:"groupbox";s:4:"size";s:1:"1";s:5:"label";s:21:"profile access rights";i:1;a:5:{s:4:"type";s:4:"grid";s:7:"options";a:0:{}s:4:"data";a:4:{i:0;a:1:{s:2:"h2";s:9:",@ea_user";}i:1;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:26:"can be used by application";}s:1:"B";a:3:{s:4:"type";s:6:"select";s:4:"name";s:10:"ea_appname";s:4:"size";s:15:"any application";}}i:2;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:20:"can be used by group";}s:1:"B";a:3:{s:4:"type";s:14:"select-account";s:4:"size";s:16:"any group,groups";s:4:"name";s:8:"ea_group";}}i:3;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:19:"can be used by user";}s:1:"B";a:4:{s:4:"type";s:14:"select-account";s:4:"size";s:17:"any user,accounts";s:4:"name";s:7:"ea_user";s:8:"onchange";s:23:"disableGroupSelector();";}}}s:4:"rows";i:3;s:4:"cols";i:2;}}}i:3;a:1:{s:1:"A";a:4:{s:4:"type";s:8:"groupbox";s:4:"size";s:1:"1";s:5:"label";s:14:"global options";i:1;a:5:{s:4:"type";s:4:"grid";s:7:"options";a:0:{}s:4:"data";a:5:{i:0;a:0:{}i:1;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:17:"profile is active";}s:1:"B";a:2:{s:4:"type";s:8:"checkbox";s:4:"name";s:9:"ea_active";}}i:2;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:37:"users can define their own identities";}s:1:"B";a:3:{s:4:"type";s:8:"checkbox";s:4:"name";s:26:"ea_user_defined_identities";s:4:"size";s:6:"yes,no";}}i:3;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:40:"users can define their own emailaccounts";}s:1:"B";a:3:{s:4:"type";s:8:"checkbox";s:4:"name";s:24:"ea_user_defined_accounts";s:4:"size";s:6:"yes,no";}}i:4;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:5:"order";}s:1:"B";a:2:{s:4:"type";s:3:"int";s:4:"name";s:8:"ea_order";}}}s:4:"rows";i:4;s:4:"cols";i:2;}}}}s:4:"rows";i:3;s:4:"cols";i:1;s:4:"size";s:4:"100%";s:7:"options";a:1:{i:0;s:4:"100%";}}}','size' => '100%','style' => '','modified' => '1335343968',); + +$templ_data[] = array('name' => 'emailadmin.edit.history','template' => '','lang' => '','group' => '0','version' => '1.9.001','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:2:{i:0;a:0:{}i:1;a:1:{s:1:"A";a:2:{s:4:"type";s:10:"historylog";s:4:"name";s:7:"history";}}}s:4:"rows";i:1;s:4:"cols";i:1;s:4:"size";s:17:"100%,400,,,,,auto";s:7:"options";a:3:{i:0;s:4:"100%";i:1;s:3:"400";i:6;s:4:"auto";}}}','size' => '100%,400,,,,,auto','style' => '','modified' => '1283865538',); + +$templ_data[] = array('name' => 'emailadmin.edit.IMAP','template' => '','lang' => '','group' => '0','version' => '1.9.005','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:8:{i:0;a:4:{s:2:"h3";s:27:",!@ea_imap_login_type=admin";s:2:"h5";s:27:",!@imapcapabilities=/sieve/";s:2:"h6";s:27:",!@imapcapabilities=/admin/";s:2:"h7";s:41:",!@imapcapabilities=/providedefaultquota/";}i:1;a:1:{s:1:"A";a:4:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"2";i:1;a:2:{s:4:"type";s:5:"label";s:5:"label";s:26:"select type of IMAP server";}i:2;a:4:{s:4:"type";s:6:"select";s:4:"name";s:12:"ea_imap_type";s:5:"align";s:5:"right";s:8:"onchange";i:1;}}}i:2;a:1:{s:1:"A";a:4:{s:4:"type";s:8:"groupbox";s:4:"size";s:1:"1";s:5:"label";s:15:"server settings";i:1;a:5:{s:4:"type";s:4:"grid";s:7:"options";a:0:{}s:4:"data";a:4:{i:0;a:0:{}i:1;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:34:"IMAP server hostname or ip address";}s:1:"B";a:2:{s:4:"type";s:4:"text";s:4:"name";s:14:"ea_imap_server";}}i:2;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:16:"IMAP server port";}s:1:"B";a:2:{s:4:"type";s:3:"int";s:4:"name";s:12:"ea_imap_port";}}i:3;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:20:"imap server logintyp";}s:1:"B";a:3:{s:4:"type";s:6:"select";s:4:"name";s:18:"ea_imap_login_type";s:8:"onchange";i:1;}}}s:4:"rows";i:3;s:4:"cols";i:2;}}}i:3;a:1:{s:1:"A";a:3:{s:4:"type";s:8:"groupbox";s:4:"size";s:1:"1";i:1;a:5:{s:4:"type";s:4:"grid";s:7:"options";a:0:{}s:4:"data";a:4:{i:0;a:0:{}i:1;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"span";s:1:"2";s:5:"label";s:42:"Use predefined username and password below";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}i:2;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:8:"username";}s:1:"B";a:2:{s:4:"type";s:4:"text";s:4:"name";s:21:"ea_imap_auth_username";}}i:3;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:8:"password";}s:1:"B";a:2:{s:4:"type";s:6:"passwd";s:4:"name";s:21:"ea_imap_auth_password";}}}s:4:"rows";i:3;s:4:"cols";i:2;}}}i:4;a:1:{s:1:"A";a:4:{s:4:"type";s:8:"groupbox";s:4:"size";s:1:"1";s:5:"label";s:19:"encryption settings";i:1;a:5:{s:4:"type";s:4:"grid";s:7:"options";a:0:{}s:4:"data";a:3:{i:0;a:0:{}i:1;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:20:"encrypted connection";}s:1:"B";a:6:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"4";i:1;a:4:{s:4:"type";s:5:"radio";s:4:"size";s:1:"1";s:5:"label";s:8:"STARTTLS";s:4:"name";s:22:"ea_imap_tsl_encryption";}i:2;a:4:{s:4:"type";s:5:"radio";s:4:"name";s:22:"ea_imap_tsl_encryption";s:4:"size";s:1:"2";s:5:"label";s:3:"TLS";}i:3;a:4:{s:4:"type";s:5:"radio";s:4:"name";s:22:"ea_imap_tsl_encryption";s:4:"size";s:1:"3";s:5:"label";s:3:"SSL";}i:4;a:4:{s:4:"type";s:5:"radio";s:4:"name";s:22:"ea_imap_tsl_encryption";s:4:"size";s:1:"0";s:5:"label";s:13:"no encryption";}}}i:2;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:27:"do not validate certificate";}s:1:"B";a:3:{s:4:"type";s:8:"checkbox";s:4:"name";s:16:"ea_imap_tsl_auth";s:4:"size";s:6:"no,yes";}}}s:4:"rows";i:2;s:4:"cols";i:2;}}}i:5;a:1:{s:1:"A";a:4:{s:4:"type";s:8:"groupbox";s:4:"size";s:1:"1";s:5:"label";s:14:"sieve settings";i:1;a:5:{s:4:"type";s:4:"grid";s:7:"options";a:0:{}s:4:"data";a:5:{i:0;a:2:{s:2:"h2";s:27:",!@ea_imap_enable_sieve=yes";s:2:"h3";s:27:",!@ea_imap_enable_sieve=yes";}i:1;a:3:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:12:"enable Sieve";}s:1:"B";a:4:{s:4:"type";s:8:"checkbox";s:4:"name";s:20:"ea_imap_enable_sieve";s:4:"size";s:6:"yes,no";s:8:"onchange";i:1;}s:1:"C";a:1:{s:4:"type";s:5:"label";}}i:2;a:3:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:35:"Sieve server hostname or ip address";}s:1:"B";a:2:{s:4:"type";s:4:"text";s:4:"name";s:20:"ea_imap_sieve_server";}s:1:"C";a:1:{s:4:"type";s:5:"label";}}i:3;a:3:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:17:"Sieve server port";}s:1:"B";a:2:{s:4:"type";s:3:"int";s:4:"name";s:18:"ea_imap_sieve_port";}s:1:"C";a:3:{s:4:"type";s:5:"label";s:5:"label";s:99:"if you specify port 5190 as sieve server port, you enforce ssl for sieve (server must support that)";s:8:"readonly";s:1:"1";}}i:4;a:3:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"span";s:1:"3";s:5:"label";s:77:"Vacation messages with start- and end-date require an admin account to be set";}s:1:"B";a:1:{s:4:"type";s:5:"label";}s:1:"C";a:1:{s:4:"type";s:5:"label";}}}s:4:"rows";i:4;s:4:"cols";i:3;}}}i:6;a:1:{s:1:"A";a:4:{s:4:"type";s:8:"groupbox";s:4:"size";s:1:"1";s:5:"label";s:32:"Cyrus IMAP server administration";i:1;a:5:{s:4:"type";s:4:"grid";s:7:"options";a:0:{}s:4:"data";a:4:{i:0;a:2:{s:2:"h2";s:27:",!@ea_imap_enable_cyrus=yes";s:2:"h3";s:27:",!@ea_imap_enable_cyrus=yes";}i:1;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:39:"enable Cyrus IMAP server administration";}s:1:"B";a:4:{s:4:"type";s:8:"checkbox";s:4:"size";s:6:"yes,no";s:4:"name";s:20:"ea_imap_enable_cyrus";s:8:"onchange";i:1;}}i:2;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:14:"admin username";}s:1:"B";a:2:{s:4:"type";s:4:"text";s:4:"name";s:18:"ea_imap_admin_user";}}i:3;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:14:"admin password";}s:1:"B";a:2:{s:4:"type";s:6:"passwd";s:4:"name";s:16:"ea_imap_admin_pw";}}}s:4:"rows";i:3;s:4:"cols";i:2;}}}i:7;a:1:{s:1:"A";a:4:{s:4:"type";s:8:"groupbox";s:4:"size";s:1:"1";s:5:"label";s:14:"quota settings";i:1;a:4:{s:4:"type";s:4:"grid";s:4:"data";a:2:{i:0;a:0:{}i:1;a:3:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:19:"quota size in mbyte";}s:1:"B";a:2:{s:4:"type";s:3:"int";s:4:"name";s:12:"defaultQuota";}s:1:"C";a:2:{s:4:"type";s:5:"label";s:5:"label";s:126:"(imapclass must support this feature by querying the corresponding config value and pass it as defaultquota to the imapserver)";}}}s:4:"rows";i:1;s:4:"cols";i:3;}}}}s:4:"rows";i:7;s:4:"cols";i:1;s:4:"size";s:17:"100%,400,,,,,auto";s:7:"options";a:3:{i:0;s:4:"100%";i:1;s:3:"400";i:6;s:4:"auto";}}}','size' => '100%,400,,,,,auto','style' => '','modified' => '1368705394',); + +$templ_data[] = array('name' => 'emailadmin.edit.signature','template' => '','lang' => '','group' => '0','version' => '1.7.004','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:2:{i:0;a:0:{}i:1;a:1:{s:1:"A";a:3:{s:4:"type";s:8:"groupbox";s:4:"size";s:1:"1";i:1;a:5:{s:4:"type";s:4:"grid";s:7:"options";a:0:{}s:4:"data";a:3:{i:0;a:0:{}i:1;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:37:"users can define their own signatures";}s:1:"B";a:3:{s:4:"type";s:8:"checkbox";s:4:"name";s:26:"ea_user_defined_signatures";s:4:"size";s:6:"yes,no";}}i:2;a:2:{s:1:"A";a:4:{s:4:"type";s:8:"htmlarea";s:4:"span";s:1:"2";s:4:"name";s:20:"ea_default_signature";s:4:"size";s:17:"advanced,,700,180";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}}s:4:"rows";i:2;s:4:"cols";i:2;}}}}s:4:"rows";i:1;s:4:"cols";i:1;s:4:"size";s:17:"100%,400,,,,,auto";s:7:"options";a:3:{i:0;s:4:"100%";i:1;s:3:"400";i:6;s:4:"auto";}}}','size' => '100%,400,,,,,auto','style' => '','modified' => '1270799878',); + +$templ_data[] = array('name' => 'emailadmin.edit.SMTP','template' => '','lang' => '','group' => '0','version' => '1.7.004','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:5:{i:0;a:1:{s:2:"h4";s:29:",!@smtpcapabilities=/forward/";}i:1;a:1:{s:1:"A";a:4:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"2";i:1;a:2:{s:4:"type";s:5:"label";s:5:"label";s:26:"Select type of SMTP Server";}i:2;a:4:{s:4:"type";s:6:"select";s:4:"name";s:12:"ea_smtp_type";s:5:"align";s:5:"right";s:8:"onchange";i:1;}}}i:2;a:1:{s:1:"A";a:4:{s:4:"type";s:8:"groupbox";s:4:"size";s:1:"1";s:5:"label";s:13:"SMTP settings";i:1;a:5:{s:4:"type";s:4:"grid";s:7:"options";a:0:{}s:4:"data";a:3:{i:0;a:0:{}i:1;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:34:"SMTP-Server hostname or IP address";}s:1:"B";a:2:{s:4:"type";s:4:"text";s:4:"name";s:14:"ea_smtp_server";}}i:2;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:16:"SMTP-Server Port";}s:1:"B";a:2:{s:4:"type";s:3:"int";s:4:"name";s:12:"ea_smtp_port";}}}s:4:"rows";i:2;s:4:"cols";i:2;}}}i:3;a:1:{s:1:"A";a:4:{s:4:"type";s:8:"groupbox";s:4:"size";s:1:"1";s:5:"label";s:19:"smtp authentication";i:1;a:5:{s:4:"type";s:4:"grid";s:4:"data";a:5:{i:0;a:0:{}i:1;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:13:"Use SMTP auth";}s:1:"B";a:2:{s:4:"type";s:6:"select";s:4:"name";s:12:"ea_smtp_auth";}}i:2;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:29:"send using this eMail-Address";}s:1:"B";a:2:{s:4:"type";s:4:"text";s:4:"name";s:18:"smtp_senders_email";}}i:3;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:8:"username";}s:1:"B";a:2:{s:4:"type";s:4:"text";s:4:"name";s:21:"ea_smtp_auth_username";}}i:4;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:8:"password";}s:1:"B";a:2:{s:4:"type";s:6:"passwd";s:4:"name";s:21:"ea_smtp_auth_password";}}}s:4:"rows";i:4;s:4:"cols";i:2;s:7:"options";a:0:{}}}}i:4;a:1:{s:1:"A";a:4:{s:4:"type";s:8:"groupbox";s:4:"size";s:1:"1";s:5:"label";s:12:"smtp options";i:1;a:5:{s:4:"type";s:4:"grid";s:7:"options";a:0:{}s:4:"data";a:2:{i:0;a:0:{}i:1;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:32:"user can edit forwarding address";}s:1:"B";a:3:{s:4:"type";s:8:"checkbox";s:4:"name";s:24:"ea_editforwardingaddress";s:4:"size";s:6:"yes,no";}}}s:4:"rows";i:1;s:4:"cols";i:2;}}}}s:4:"rows";i:4;s:4:"cols";i:1;s:4:"size";s:17:"100%,400,,,,,auto";s:7:"options";a:3:{i:0;s:4:"100%";i:1;s:3:"400";i:6;s:4:"auto";}}}','size' => '100%,400,,,,,auto','style' => '','modified' => '1274772869',); + +$templ_data[] = array('name' => 'emailadmin.edit.stationery','template' => '','lang' => '','group' => '0','version' => '1.7.003','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:2:{i:0;a:0:{}i:1;a:1:{s:1:"A";a:4:{s:4:"type";s:8:"groupbox";s:4:"size";s:1:"1";s:5:"label";s:16:"active templates";i:1;a:5:{s:4:"type";s:4:"grid";s:7:"options";a:0:{}s:4:"data";a:3:{i:0;a:0:{}i:1;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:44:"users can utilize these stationery templates";}s:1:"B";a:4:{s:4:"type";s:6:"select";s:4:"name";s:30:"ea_stationery_active_templates";i:1;a:3:{s:4:"type";s:3:"box";s:4:"name";s:30:"ea_stationery_active_templates";s:4:"size";s:3:",10";}s:4:"size";s:1:"5";}}i:2;a:2:{s:1:"A";a:6:{s:4:"type";s:4:"html";s:4:"span";s:1:"2";s:8:"readonly";s:1:"1";s:4:"help";s:27:"manage stationery templates";s:4:"name";s:27:"manage_stationery_templates";s:5:"align";s:5:"right";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}}s:4:"rows";i:2;s:4:"cols";i:2;}}}}s:4:"rows";i:1;s:4:"cols";i:1;s:4:"size";s:17:"100%,400,,,,,auto";s:7:"options";a:3:{i:0;s:4:"100%";i:1;s:3:"400";i:6;s:4:"auto";}}}','size' => '100%,400,,,,,auto','style' => '','modified' => '1255600503',); + +$templ_data[] = array('name' => 'emailadmin.index','template' => '','lang' => '','group' => '0','version' => '1.7.003','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"size";s:4:"100%";s:7:"options";a:1:{i:0;s:4:"100%";}s:4:"data";a:6:{i:0;a:2:{s:2:"h2";s:6:",!@msg";s:2:"h1";s:11:",!@subtitle";}i:1;a:1:{s:1:"A";a:6:{s:4:"type";s:4:"hbox";s:8:"readonly";s:1:"1";s:5:"align";s:6:"center";s:4:"size";s:1:"2";i:1;a:4:{s:4:"type";s:4:"html";s:4:"name";s:8:"subtitle";s:8:"readonly";s:1:"1";s:5:"align";s:6:"center";}i:2;a:3:{s:4:"type";s:4:"html";s:4:"name";s:13:"addJavaScript";s:8:"readonly";s:1:"1";}}}i:2;a:1:{s:1:"A";a:4:{s:4:"type";s:5:"label";s:4:"span";s:10:",redItalic";s:4:"name";s:3:"msg";s:5:"align";s:6:"center";}}i:3;a:1:{s:1:"A";a:5:{s:4:"type";s:6:"button";s:4:"name";s:10:"AddProfile";s:5:"label";s:3:"Add";s:5:"align";s:5:"right";s:7:"onclick";s:165:"window.open(egw::link(\'/index.php\',\'menuaction=emailadmin.emailadmin_ui.add\'),\'_blank\',\'dependent=yes,width=850,height=540,scrollbars=yes,status=yes\'); return false;";}}i:4;a:1:{s:1:"A";a:4:{s:4:"type";s:9:"nextmatch";s:4:"size";s:21:"emailadmin.index.rows";s:4:"span";s:3:"all";s:4:"name";s:2:"nm";}}i:5;a:1:{s:1:"A";a:6:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"3";s:5:"align";s:5:"right";i:1;a:5:{s:4:"type";s:6:"button";s:4:"name";s:6:"delete";s:4:"size";s:6:"delete";s:5:"label";s:6:"Delete";s:7:"onclick";s:34:"return confirm(\'Delete Profiles\');";}i:2;a:4:{s:4:"type";s:10:"buttononly";s:4:"span";s:15:",selectAllArrow";s:7:"onclick";s:71:"toggle_all(this.form,form::name(\'nm[rows][selected][]\')); return false;";s:4:"size";s:9:"arrow_ltr";}i:3;a:1:{s:4:"type";s:5:"label";}}}}s:4:"rows";i:5;s:4:"cols";i:1;}}','size' => '100%','style' => '.redItalic { color: red; font-style: italics; }','modified' => '1255529643',); + +$templ_data[] = array('name' => 'emailadmin.index.rows','template' => '','lang' => '','group' => '0','version' => '1.7.004','data' => 'a:1:{i:0;a:7:{s:4:"type";s:4:"grid";s:4:"data";a:3:{i:0;a:3:{s:2:"c1";s:2:"th";s:2:"c2";s:20:"$row_cont[class] row";s:1:"P";s:2:"1%";}i:1;a:16:{s:1:"A";a:3:{s:4:"type";s:20:"nextmatch-sortheader";s:5:"label";s:2:"ID";s:4:"name";s:13:"ea_profile_id";}s:1:"B";a:3:{s:4:"type";s:20:"nextmatch-sortheader";s:5:"label";s:11:"Description";s:4:"name";s:14:"ea_description";}s:1:"C";a:3:{s:4:"type";s:20:"nextmatch-sortheader";s:5:"label";s:10:"domainname";s:4:"name";s:17:"ea_default_domain";}s:1:"D";a:3:{s:4:"type";s:20:"nextmatch-sortheader";s:5:"label";s:16:"SMTP Server Name";s:4:"name";s:14:"ea_smtp_server";}s:1:"E";a:3:{s:4:"type";s:16:"nextmatch-header";s:5:"label";s:16:"SMTP Server Type";s:4:"name";s:12:"ea_smtp_type";}s:1:"F";a:3:{s:4:"type";s:20:"nextmatch-sortheader";s:5:"label";s:16:"SMTP Server Port";s:4:"name";s:12:"ea_smtp_port";}s:1:"G";a:3:{s:4:"type";s:20:"nextmatch-sortheader";s:5:"label";s:16:"IMAP Server Name";s:4:"name";s:14:"ea_imap_server";}s:1:"H";a:3:{s:4:"type";s:16:"nextmatch-header";s:5:"label";s:16:"IMAP Server Type";s:4:"name";s:12:"ea_imap_type";}s:1:"I";a:3:{s:4:"type";s:20:"nextmatch-sortheader";s:5:"label";s:16:"IMAP Server Port";s:4:"name";s:12:"ea_imap_port";}s:1:"J";a:3:{s:4:"type";s:16:"nextmatch-header";s:5:"label";s:22:"IMAP Server Login Type";s:4:"name";s:18:"ea_imap_login_type";}s:1:"K";a:3:{s:4:"type";s:16:"nextmatch-header";s:4:"name";s:10:"ea_appname";s:5:"label";s:11:"Application";}s:1:"L";a:3:{s:4:"type";s:16:"nextmatch-header";s:4:"name";s:8:"ea_group";s:5:"label";s:5:"Group";}s:1:"M";a:3:{s:4:"type";s:16:"nextmatch-header";s:4:"name";s:7:"ea_user";s:5:"label";s:4:"User";}s:1:"N";a:3:{s:4:"type";s:20:"nextmatch-sortheader";s:5:"label";s:5:"order";s:4:"name";s:8:"ea_order";}s:1:"O";a:3:{s:4:"type";s:20:"nextmatch-sortheader";s:5:"label";s:6:"Active";s:4:"name";s:9:"ea_active";}s:1:"P";a:4:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"2";i:1;a:2:{s:4:"type";s:5:"label";s:5:"label";s:6:"Action";}i:2;a:4:{s:4:"type";s:10:"buttononly";s:4:"size";s:5:"check";s:5:"label";s:10:"Select All";s:7:"onclick";s:61:"toggle_all(this.form,form::name(\'selected[]\')); return false;";}}}i:2;a:16:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:4:"name";s:21:"${row}[ea_profile_id]";}s:1:"B";a:2:{s:4:"type";s:5:"label";s:4:"name";s:22:"${row}[ea_description]";}s:1:"C";a:2:{s:4:"type";s:5:"label";s:4:"name";s:25:"${row}[ea_default_domain]";}s:1:"D";a:2:{s:4:"type";s:5:"label";s:4:"name";s:22:"${row}[ea_smtp_server]";}s:1:"E";a:3:{s:4:"type";s:6:"select";s:4:"name";s:20:"${row}[ea_smtp_type]";s:8:"readonly";s:1:"1";}s:1:"F";a:2:{s:4:"type";s:5:"label";s:4:"name";s:20:"${row}[ea_smtp_port]";}s:1:"G";a:2:{s:4:"type";s:5:"label";s:4:"name";s:22:"${row}[ea_imap_server]";}s:1:"H";a:3:{s:4:"type";s:6:"select";s:4:"name";s:20:"${row}[ea_imap_type]";s:8:"readonly";s:1:"1";}s:1:"I";a:2:{s:4:"type";s:5:"label";s:4:"name";s:20:"${row}[ea_imap_port]";}s:1:"J";a:2:{s:4:"type";s:5:"label";s:4:"name";s:26:"${row}[ea_imap_login_type]";}s:1:"K";a:3:{s:4:"type";s:6:"select";s:4:"name";s:18:"${row}[ea_appname]";s:8:"readonly";s:1:"1";}s:1:"L";a:4:{s:4:"type";s:14:"select-account";s:4:"name";s:16:"${row}[ea_group]";s:8:"readonly";s:1:"1";s:4:"size";s:7:",groups";}s:1:"M";a:4:{s:4:"type";s:14:"select-account";s:4:"name";s:15:"${row}[ea_user]";s:8:"readonly";s:1:"1";s:4:"size";s:9:",accounts";}s:1:"N";a:3:{s:4:"type";s:5:"label";s:4:"name";s:16:"${row}[ea_order]";s:7:"no_lang";s:1:"1";}s:1:"O";a:2:{s:4:"type";s:5:"label";s:4:"name";s:17:"${row}[ea_active]";}s:1:"P";a:6:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"4";i:1;a:6:{s:4:"type";s:6:"button";s:4:"size";s:4:"edit";s:5:"label";s:4:"Edit";s:4:"help";s:17:"Edit this Profile";s:4:"name";s:30:"edit[$row_cont[ea_profile_id]]";s:7:"onclick";s:205:"window.open(egw::link(\'/index.php\',\'menuaction=emailadmin.emailadmin_ui.edit&profileid=$row_cont[ea_profile_id]\'),\'ea_profile\',\'dependent=yes,width=850,height=540,scrollbars=yes,status=yes\'); return false;";}i:2;a:6:{s:4:"type";s:6:"button";s:4:"size";s:6:"delete";s:5:"label";s:6:"Delete";s:4:"name";s:32:"delete[$row_cont[ea_profile_id]]";s:7:"onclick";s:60:"return confirm(\'Do you really want to delete this Profile\');";s:4:"help";s:19:"Delete this Profile";}i:3;a:3:{s:4:"type";s:8:"checkbox";s:4:"size";s:24:"$row_cont[ea_profile_id]";s:4:"name";s:10:"selected[]";}i:4;a:1:{s:4:"type";s:5:"label";}}}}s:4:"rows";i:2;s:4:"cols";i:16;s:4:"size";s:4:"100%";s:5:"align";s:6:"center";s:7:"options";a:1:{i:0;s:4:"100%";}}}','size' => '100%','style' => '','modified' => '1353666452',); + +$templ_data[] = array('name' => 'emailadmin.stationery.sample','template' => '','lang' => '','group' => '0','version' => '1.7.003','data' => 'a:1:{i:0;a:5:{s:4:"type";s:4:"grid";s:4:"data";a:4:{i:0;a:0:{}i:1;a:1:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:5:"label";s:6:"Header";s:4:"span";s:7:",header";}}i:2;a:1:{s:1:"A";a:4:{s:5:"label";s:7:"message";s:4:"name";s:7:"message";s:4:"type";s:4:"html";s:4:"span";s:8:",message";}}i:3;a:1:{s:1:"A";a:4:{s:5:"label";s:9:"signature";s:4:"name";s:9:"signature";s:4:"type";s:4:"html";s:4:"span";s:10:",signature";}}}s:4:"cols";i:1;s:4:"rows";i:3;s:4:"size";s:21:"100%,,0,fullwidth,0,0";}}','size' => '100%,,0,fullwidth,0,0','style' => 'body { + color: #000000; + background-color: #eaeaea; + margin: 0; + padding: 0; +} + +table.fullwidth { + table-layout:fixed; + width: 100%; + margin: 0; + padding: 0; + border: 0; +} + +td.header { + background-color: #322f40; + color: #ffffff; + font-weight: bold; + margin: 0; + padding: 1em; + border-left: 6px solid #504a66; + border-bottom: 1px solid #504a66; +} + +td.message { + margin: 0.5em; + padding: 1em; + color: #333333; + font-family: "Verdana"; + font-size: 12px; +} + +td.signature { + color: #666666; + border-left: 5px solid #747474; + margin: 0.5em; + padding-left: 8px; +} + +img { + border: 0; + margin: 0; + padding: 0; +}','modified' => '1376902007',); + +$templ_data[] = array('name' => 'emailadmin.wizard','template' => '','lang' => '','group' => '0','version' => '1.9.001','data' => 'a:1:{i:0;a:5:{s:4:"type";s:4:"grid";s:4:"data";a:11:{i:0;a:4:{s:1:"A";s:3:"120";s:2:"c4";s:13:"@manual_class";s:2:"c6";s:13:"@manual_class";s:2:"c7";s:13:"@manual_class";}i:1;a:2:{s:1:"A";a:3:{s:4:"span";s:11:"all,message";s:4:"name";s:3:"msg";s:4:"type";s:5:"label";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}i:2;a:2:{s:1:"A";a:3:{s:4:"span";s:21:"all,emailadmin_header";s:4:"type";s:5:"label";s:5:"label";s:28:"Step 1: IMAP - incoming mail";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}i:3;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:13:"EMail address";}s:1:"B";a:4:{s:6:"needed";s:1:"1";s:4:"name";s:11:"ident_email";s:4:"size";s:6:"32,128";s:4:"type";s:9:"url-email";}}i:4;a:2:{s:1:"A";a:3:{s:3:"for";s:17:"acc_imap_username";s:4:"type";s:5:"label";s:5:"label";s:8:"Username";}s:1:"B";a:4:{s:4:"blur";s:31:"if different from EMail address";s:4:"size";s:6:"32,128";s:4:"name";s:17:"acc_imap_username";s:4:"type";s:4:"text";}}i:5;a:2:{s:1:"A";a:3:{s:3:"for";s:17:"acc_imap_password";s:4:"type";s:5:"label";s:5:"label";s:8:"Password";}s:1:"B";a:4:{s:6:"needed";s:1:"1";s:4:"name";s:17:"acc_imap_password";s:4:"size";s:6:"32,128";s:4:"type";s:6:"passwd";}}i:6;a:2:{s:1:"A";a:3:{s:3:"for";s:13:"acc_imap_host";s:4:"type";s:5:"label";s:5:"label";s:11:"IMAP server";}s:1:"B";a:4:{s:4:"blur";s:14:"Hostname or IP";s:4:"size";s:6:"32,128";s:4:"name";s:13:"acc_imap_host";s:4:"type";s:4:"text";}}i:7;a:2:{s:1:"A";a:3:{s:3:"for";s:12:"acc_imap_ssl";s:4:"type";s:5:"label";s:5:"label";s:17:"Secure connection";}s:1:"B";a:4:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"2";i:1;a:4:{s:8:"onchange";s:39:"app.emailadmin.wizard_imap_ssl_onchange";s:5:"class";s:14:"emailadmin_ssl";s:4:"name";s:12:"acc_imap_ssl";s:4:"type";s:6:"select";}i:2;a:5:{s:4:"type";s:7:"integer";s:5:"label";s:4:"Port";s:4:"name";s:13:"acc_imap_port";s:4:"size";s:1:",";s:4:"span";s:16:",emailadmin_port";}}}i:8;a:2:{s:1:"A";a:6:{s:4:"span";s:3:"all";s:4:"type";s:4:"hbox";s:4:"size";s:1:"3";i:1;a:4:{s:5:"label";s:8:"Continue";s:7:"onclick";s:28:"app.emailadmin.wizard_detect";s:4:"name";s:16:"button[continue]";s:4:"type";s:6:"button";}i:2;a:4:{s:5:"label";s:12:"Manual entry";s:7:"onclick";s:28:"app.emailadmin.wizard_manual";s:4:"name";s:14:"button[manual]";s:4:"type";s:6:"button";}i:3;a:4:{s:5:"label";s:6:"Cancel";s:7:"onclick";s:15:"window.close();";s:4:"name";s:14:"button[cancel]";s:4:"type";s:10:"buttononly";}}s:1:"B";a:1:{s:4:"type";s:5:"label";}}i:9;a:2:{s:1:"A";a:3:{s:4:"span";s:23:"all,emailadmin_progress";s:4:"type";s:5:"image";s:4:"name";s:19:"emailadmin/progress";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}i:10;a:2:{s:1:"A";a:4:{s:8:"readonly";s:4:"true";s:4:"span";s:3:"all";s:4:"name";s:6:"output";s:4:"type";s:8:"textarea";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}}s:4:"cols";i:2;s:4:"rows";i:10;s:4:"size";s:4:"100%";}}','size' => '100%','style' => '','modified' => '1383745616',); + +$templ_data[] = array('name' => 'emailadmin.wizard.folder','template' => '','lang' => '','group' => '0','version' => '1.9.001','data' => 'a:1:{i:0;a:4:{s:4:"type";s:4:"grid";s:4:"data";a:8:{i:0;a:1:{s:1:"A";s:3:"120";}i:1;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"span";s:11:"all,message";s:4:"name";s:3:"msg";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}i:2;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"span";s:21:"all,emailadmin_header";s:5:"label";s:14:"Step 2: Folder";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}i:3;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:18:",,,acc_folder_sent";s:5:"label";s:11:"Sent folder";}s:1:"B";a:3:{s:4:"type";s:6:"select";s:4:"size";s:14:"Select one ...";s:4:"name";s:15:"acc_folder_sent";}}i:4;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:19:",,,acc_folder_trash";s:5:"label";s:12:"Trash folder";}s:1:"B";a:3:{s:4:"type";s:6:"select";s:4:"size";s:14:"Select one ...";s:4:"name";s:16:"acc_folder_trash";}}i:5;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:19:",,,acc_folder_draft";s:5:"label";s:13:"Drafts folder";}s:1:"B";a:3:{s:4:"type";s:6:"select";s:4:"size";s:14:"Select one ...";s:4:"name";s:16:"acc_folder_draft";}}i:6;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:22:",,,acc_folder_template";s:5:"label";s:16:"Templates folder";}s:1:"B";a:3:{s:4:"type";s:6:"select";s:4:"size";s:14:"Select one ...";s:4:"name";s:19:"acc_folder_template";}}i:7;a:2:{s:1:"A";a:6:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"3";s:4:"span";s:3:"all";i:1;a:3:{s:4:"type";s:6:"button";s:5:"label";s:8:"Continue";s:4:"name";s:16:"button[continue]";}i:2;a:3:{s:4:"type";s:6:"button";s:5:"label";s:4:"Back";s:4:"name";s:12:"button[back]";}i:3;a:4:{s:4:"type";s:6:"button";s:5:"label";s:6:"Cancel";s:4:"name";s:14:"button[cancel]";s:7:"onclick";s:15:"window.close();";}}s:1:"B";a:1:{s:4:"type";s:5:"label";}}}s:4:"rows";i:7;s:4:"cols";i:2;}}','size' => '','style' => '','modified' => '1383134955',); + +$templ_data[] = array('name' => 'emailadmin.wizard.save','template' => '','lang' => '','group' => '0','version' => '1.9.001','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:20:{i:0;a:1:{s:1:"A";s:3:"120";}i:1;a:2:{s:1:"A";a:3:{s:4:"span";s:11:"all,message";s:4:"name";s:3:"msg";s:4:"type";s:5:"label";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}i:2;a:2:{s:1:"A";a:3:{s:4:"span";s:21:"all,emailadmin_header";s:4:"type";s:5:"label";s:5:"label";s:30:"Step 5: Check and save account";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}i:3;a:2:{s:1:"A";a:3:{s:3:"for";s:8:"acc_name";s:4:"type";s:5:"label";s:5:"label";s:15:"Name of account";}s:1:"B";a:4:{s:6:"needed";s:1:"1";s:4:"size";s:6:"32,128";s:4:"name";s:8:"acc_name";s:4:"type";s:4:"text";}}i:4;a:2:{s:1:"A";a:3:{s:3:"for";s:14:"ident_realname";s:4:"type";s:5:"label";s:5:"label";s:9:"Your name";}s:1:"B";a:4:{s:6:"needed";s:1:"1";s:4:"size";s:6:"32,128";s:4:"name";s:14:"ident_realname";s:4:"type";s:4:"text";}}i:5;a:2:{s:1:"A";a:3:{s:3:"for";s:9:"ident_org";s:4:"type";s:5:"label";s:5:"label";s:12:"Organisation";}s:1:"B";a:3:{s:4:"size";s:6:"32,128";s:4:"name";s:9:"ident_org";s:4:"type";s:4:"text";}}i:6;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"hrule";s:4:"span";s:3:"all";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}i:7;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:13:"EMail address";}s:1:"B";a:3:{s:8:"readonly";s:1:"1";s:4:"name";s:11:"ident_email";s:4:"type";s:9:"url-email";}}i:8;a:2:{s:1:"A";a:3:{s:3:"for";s:17:"acc_imap_username";s:4:"type";s:5:"label";s:5:"label";s:8:"Username";}s:1:"B";a:3:{s:8:"readonly";s:1:"1";s:4:"name";s:17:"acc_imap_username";s:4:"type";s:4:"text";}}i:9;a:2:{s:1:"A";a:3:{s:3:"for";s:13:"acc_imap_host";s:4:"type";s:5:"label";s:5:"label";s:11:"IMAP server";}s:1:"B";a:3:{s:8:"readonly";s:1:"1";s:4:"name";s:13:"acc_imap_host";s:4:"type";s:4:"text";}}i:10;a:2:{s:1:"A";a:3:{s:3:"for";s:12:"acc_imap_ssl";s:4:"type";s:5:"label";s:5:"label";s:17:"Secure connection";}s:1:"B";a:4:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"2";i:1;a:3:{s:8:"readonly";s:1:"1";s:4:"name";s:12:"acc_imap_ssl";s:4:"type";s:6:"select";}i:2;a:4:{s:4:"name";s:13:"acc_imap_port";s:4:"type";s:3:"int";s:5:"label";s:4:"Port";s:8:"readonly";s:1:"1";}}}i:11;a:2:{s:1:"A";a:3:{s:3:"for";s:15:"acc_folder_sent";s:4:"type";s:5:"label";s:5:"label";s:11:"Sent folder";}s:1:"B";a:3:{s:8:"readonly";s:1:"1";s:4:"name";s:15:"acc_folder_sent";s:4:"type";s:4:"text";}}i:12;a:2:{s:1:"A";a:3:{s:3:"for";s:16:"acc_folder_trash";s:4:"type";s:5:"label";s:5:"label";s:12:"Trash folder";}s:1:"B";a:3:{s:8:"readonly";s:1:"1";s:4:"name";s:16:"acc_folder_trash";s:4:"type";s:4:"text";}}i:13;a:2:{s:1:"A";a:3:{s:3:"for";s:16:"acc_folder_draft";s:4:"type";s:5:"label";s:5:"label";s:13:"Drafts folder";}s:1:"B";a:3:{s:8:"readonly";s:1:"1";s:4:"name";s:16:"acc_folder_draft";s:4:"type";s:4:"text";}}i:14;a:2:{s:1:"A";a:3:{s:3:"for";s:19:"acc_folder_template";s:4:"type";s:5:"label";s:5:"label";s:16:"Templates folder";}s:1:"B";a:3:{s:8:"readonly";s:1:"1";s:4:"name";s:19:"acc_folder_template";s:4:"type";s:4:"text";}}i:15;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:12:"Sieve server";}s:1:"B";a:2:{s:4:"type";s:5:"label";s:4:"name";s:14:"acc_sieve_host";}}i:16;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:17:"Secure connection";}s:1:"B";a:4:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"2";i:1;a:3:{s:8:"readonly";s:1:"1";s:4:"name";s:13:"acc_sieve_ssl";s:4:"type";s:6:"select";}i:2;a:4:{s:4:"name";s:14:"acc_sieve_port";s:4:"type";s:3:"int";s:5:"label";s:4:"Port";s:8:"readonly";s:1:"1";}}}i:17;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:11:"SMTP server";}s:1:"B";a:4:{s:4:"type";s:5:"label";i:1;a:3:{s:8:"readonly";s:1:"1";s:4:"name";s:12:"acc_smtp_ssl";s:4:"type";s:6:"select";}i:2;a:3:{s:4:"name";s:13:"acc_smtp_port";s:4:"type";s:5:"label";s:5:"label";s:4:"Port";}s:4:"name";s:13:"acc_smtp_host";}}i:18;a:2:{s:1:"A";a:3:{s:3:"for";s:12:"acc_imap_ssl";s:4:"type";s:5:"label";s:5:"label";s:17:"Secure connection";}s:1:"B";a:4:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"2";i:1;a:3:{s:8:"readonly";s:1:"1";s:4:"name";s:12:"acc_smtp_ssl";s:4:"type";s:6:"select";}i:2;a:4:{s:4:"name";s:13:"acc_smtp_port";s:4:"type";s:3:"int";s:5:"label";s:4:"Port";s:8:"readonly";s:1:"1";}}}i:19;a:2:{s:1:"A";a:6:{s:4:"span";s:3:"all";s:4:"type";s:4:"hbox";s:4:"size";s:1:"3";i:1;a:3:{s:5:"label";s:4:"Save";s:4:"name";s:12:"button[save]";s:4:"type";s:6:"button";}i:2;a:3:{s:5:"label";s:4:"Back";s:4:"name";s:12:"button[back]";s:4:"type";s:6:"button";}i:3;a:4:{s:5:"label";s:6:"Cancel";s:7:"onclick";s:15:"window.close();";s:4:"name";s:14:"button[cancel]";s:4:"type";s:6:"button";}}s:1:"B";a:1:{s:4:"type";s:5:"label";}}}s:4:"cols";i:2;s:4:"rows";i:19;s:4:"size";s:4:"100%";s:7:"options";a:1:{i:0;s:4:"100%";}}}','size' => '100%','style' => '','modified' => '1383145172',); + +$templ_data[] = array('name' => 'emailadmin.wizard.sieve','template' => '','lang' => '','group' => '0','version' => '1.9.001','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:9:{i:0;a:3:{s:1:"A";s:3:"120";s:2:"c4";s:13:"@manual_class";s:2:"c5";s:13:"@manual_class";}i:1;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"span";s:11:"all,message";s:4:"name";s:3:"msg";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}i:2;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"span";s:21:"all,emailadmin_header";s:5:"label";s:42:"Step 3: Sieve - server side mail filtering";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}i:3;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:20:",,,acc_sieve_enabled";s:5:"label";s:12:"Enable Sieve";}s:1:"B";a:2:{s:4:"type";s:11:"select-bool";s:4:"name";s:17:"acc_sieve_enabled";}}i:4;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:17:",,,acc_sieve_host";s:5:"label";s:12:"Sieve server";}s:1:"B";a:5:{s:4:"type";s:4:"text";s:4:"size";s:6:"32,128";s:4:"name";s:14:"acc_sieve_host";s:4:"blur";s:14:"Hostname or IP";s:8:"onchange";s:36:"app.emailadmin.wizard_sieve_onchange";}}i:5;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:16:",,,acc_sieve_ssl";s:5:"label";s:17:"Secure connection";}s:1:"B";a:4:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"2";i:1;a:4:{s:4:"type";s:6:"select";s:4:"name";s:13:"acc_sieve_ssl";s:8:"onchange";s:40:"app.emailadmin.wizard_sieve_ssl_onchange";s:4:"span";s:15:",emailadmin_ssl";}i:2;a:5:{s:4:"type";s:3:"int";s:4:"name";s:14:"acc_sieve_port";s:5:"label";s:4:"Port";s:8:"onchange";s:36:"app.emailadmin.wizard_sieve_onchange";s:4:"span";s:16:",emailadmin_port";}}}i:6;a:2:{s:1:"A";a:7:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"4";s:4:"span";s:3:"all";i:1;a:4:{s:4:"type";s:6:"button";s:5:"label";s:8:"Continue";s:4:"name";s:16:"button[continue]";s:7:"onclick";s:28:"app.emailadmin.wizard_detect";}i:2;a:3:{s:4:"type";s:6:"button";s:4:"name";s:12:"button[back]";s:5:"label";s:4:"Back";}i:3;a:4:{s:4:"type";s:6:"button";s:4:"name";s:14:"button[manual]";s:5:"label";s:12:"Manual entry";s:7:"onclick";s:28:"app.emailadmin.wizard_manual";}i:4;a:4:{s:4:"type";s:10:"buttononly";s:4:"name";s:14:"button[cancel]";s:5:"label";s:6:"Cancel";s:7:"onclick";s:15:"window.close();";}}s:1:"B";a:1:{s:4:"type";s:5:"label";}}i:7;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"image";s:4:"span";s:23:"all,emailadmin_progress";s:4:"name";s:19:"emailadmin/progress";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}i:8;a:2:{s:1:"A";a:4:{s:4:"type";s:8:"textarea";s:4:"span";s:3:"all";s:4:"name";s:12:"sieve_output";s:8:"readonly";s:1:"1";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}}s:4:"rows";i:8;s:4:"cols";i:2;s:4:"size";s:4:"100%";s:7:"options";a:1:{i:0;s:4:"100%";}}}','size' => '100%','style' => '','modified' => '1383389574',); + +$templ_data[] = array('name' => 'emailadmin.wizard.smtp','template' => '','lang' => '','group' => '0','version' => '1.9.001','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:10:{i:0;a:5:{s:1:"A";s:3:"120";s:2:"c5";s:13:"@manual_class";s:2:"c4";s:13:"@manual_class";s:2:"c3";s:13:"@manual_class";s:2:"c6";s:13:"@manual_class";}i:1;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"span";s:11:"all,message";s:4:"name";s:3:"msg";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}i:2;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"span";s:21:"all,emailadmin_header";s:5:"label";s:28:"Step 4: SMTP - outgoing mail";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}i:3;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:20:",,,acc_smtp_username";s:5:"label";s:8:"Username";}s:1:"B";a:4:{s:4:"type";s:4:"text";s:4:"size";s:6:"32,128";s:4:"name";s:17:"acc_smtp_username";s:4:"blur";s:26:"if authentication required";}}i:4;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:20:",,,acc_smtp_password";s:5:"label";s:8:"Password";}s:1:"B";a:3:{s:4:"type";s:6:"passwd";s:4:"size";s:6:"32,128";s:4:"name";s:17:"acc_smtp_password";}}i:5;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:16:",,,acc_smtp_host";s:5:"label";s:11:"SMTP server";}s:1:"B";a:4:{s:4:"type";s:4:"text";s:4:"size";s:6:"32,128";s:4:"name";s:13:"acc_smtp_host";s:4:"blur";s:14:"Hostname or IP";}}i:6;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:15:",,,acc_smtp_ssl";s:5:"label";s:17:"Secure connection";}s:1:"B";a:4:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"2";i:1;a:4:{s:4:"type";s:6:"select";s:4:"name";s:12:"acc_smtp_ssl";s:8:"onchange";s:39:"app.emailadmin.wizard_smtp_ssl_onchange";s:4:"span";s:15:",emailadmin_ssl";}i:2;a:4:{s:4:"type";s:3:"int";s:4:"name";s:13:"acc_smtp_port";s:5:"label";s:4:"Port";s:4:"span";s:16:",emailadmin_port";}}}i:7;a:2:{s:1:"A";a:7:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"4";s:4:"span";s:3:"all";i:1;a:4:{s:4:"type";s:6:"button";s:5:"label";s:8:"Continue";s:4:"name";s:16:"button[continue]";s:7:"onclick";s:28:"app.emailadmin.wizard_detect";}i:2;a:3:{s:4:"type";s:6:"button";s:4:"name";s:12:"button[back]";s:5:"label";s:4:"Back";}i:3;a:4:{s:4:"type";s:6:"button";s:4:"name";s:14:"button[manual]";s:5:"label";s:12:"Manual entry";s:7:"onclick";s:28:"app.emailadmin.wizard_manual";}i:4;a:4:{s:4:"type";s:10:"buttononly";s:4:"name";s:14:"button[cancel]";s:5:"label";s:6:"Cancel";s:7:"onclick";s:15:"window.close();";}}s:1:"B";a:1:{s:4:"type";s:5:"label";}}i:8;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"image";s:4:"span";s:23:"all,emailadmin_progress";s:4:"name";s:19:"emailadmin/progress";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}i:9;a:2:{s:1:"A";a:4:{s:4:"type";s:8:"textarea";s:4:"span";s:3:"all";s:4:"name";s:11:"smtp_output";s:8:"readonly";s:1:"1";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}}s:4:"rows";i:9;s:4:"cols";i:2;s:4:"size";s:4:"100%";s:7:"options";a:1:{i:0;s:4:"100%";}}}','size' => '100%','style' => '','modified' => '1383150426',); + diff --git a/emailadmin/setup/setup.inc.php b/emailadmin/setup/setup.inc.php new file mode 100644 index 0000000000..a25acdec76 --- /dev/null +++ b/emailadmin/setup/setup.inc.php @@ -0,0 +1,89 @@ + + * @author Ralf Becker + * @package emailadmin + * @subpackage setup + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @version $Id$ + */ + +$setup_info['emailadmin']['name'] = 'emailadmin'; +$setup_info['emailadmin']['title'] = 'EMailAdmin'; +$setup_info['emailadmin']['version'] = '14.1'; +$setup_info['emailadmin']['app_order'] = 10; +$setup_info['emailadmin']['enable'] = 2; + +$setup_info['emailadmin']['author'] = +$setup_info['emailadmin']['maintainer'] = array( + 'name' => 'Ralf Becker', + 'email' => 'rb@stylite.de', +); +$setup_info['emailadmin']['license'] = 'GPL'; +$setup_info['emailadmin']['description'] = + 'A central Mailserver management application for EGroupWare. Completely rewritten by Ralf Becker in 2013/14'; + +$setup_info['emailadmin']['tables'][] = 'egw_emailadmin'; +$setup_info['emailadmin']['tables'][] = 'egw_mailaccounts'; +$setup_info['emailadmin']['tables'][] = 'egw_ea_accounts'; +$setup_info['emailadmin']['tables'][] = 'egw_ea_credentials'; +$setup_info['emailadmin']['tables'][] = 'egw_ea_identities'; +$setup_info['emailadmin']['tables'][] = 'egw_ea_valid'; +$setup_info['emailadmin']['tables'][] = 'egw_ea_notifications'; + +/* The hooks this app includes, needed for hooks registration */ +$setup_info['emailadmin']['hooks']['edit_user'] = 'emailadmin_hooks::edit_user'; +$setup_info['emailadmin']['hooks']['deleteaccount'] = 'emailadmin_hooks::deleteaccount'; +$setup_info['emailadmin']['hooks']['addaccount'] = 'emailadmin_hooks::addaccount'; +$setup_info['emailadmin']['hooks']['editaccount'] = 'emailadmin_hooks::addaccount'; +$setup_info['emailadmin']['hooks']['deletegroup'] = 'emailadmin_hooks::deletegroup'; +$setup_info['emailadmin']['hooks']['changepassword'] = 'emailadmin_hooks::changepassword'; + +// SMTP and IMAP support +$setup_info['emailadmin']['hooks']['smtp_server_types'] = 'emailadmin_hooks::server_types'; +$setup_info['emailadmin']['hooks']['imap_server_types'] = 'emailadmin_hooks::server_types'; + +/* Dependencies for this app to work */ +$setup_info['emailadmin']['depends'][] = array( + 'appname' => 'phpgwapi', + 'versions' => Array('14.1') +); +$setup_info['emailadmin']['depends'][] = array( + 'appname' => 'etemplate', + 'versions' => Array('14.1') +); +// installation checks +$setup_info['emailadmin']['check_install'] = array( + '' => array( + 'func' => 'pear_check', + 'from' => 'EMailAdmin', + ), + 'Net_Sieve' => array( + 'func' => 'pear_check', + 'from' => 'EMailAdmin', + ), + 'pear.horde.org/Horde_Imap_Client' => array( + 'func' => 'pear_check', + 'from' => 'EMailAdmin', + 'version' => '2.16.0', + ), + 'pear.horde.org/Horde_Nls' => array( + 'func' => 'pear_check', + 'from' => 'EMailAdmin', + 'version' => '2.0.3', + ), + 'pear.horde.org/Horde_Mail' => array( + 'func' => 'pear_check', + 'from' => 'EMailAdmin', + 'version' => '2.1.2', + ), + 'pear.horde.org/Horde_Smtp' => array( + 'func' => 'pear_check', + 'from' => 'EMailAdmin', + 'version' => '1.3.0', + ), +); + diff --git a/emailadmin/setup/tables_current.inc.php b/emailadmin/setup/tables_current.inc.php new file mode 100644 index 0000000000..5843daaaf2 --- /dev/null +++ b/emailadmin/setup/tables_current.inc.php @@ -0,0 +1,166 @@ + + * @author Ralf Becker + * @package emailadmin + * @subpackage setup + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @version $Id$ + */ + +$phpgw_baseline = array( + 'egw_emailadmin' => array( + 'fd' => array( + 'ea_profile_id' => array('type' => 'auto','nullable' => False,'comment' => 'the id of the profile; in programm its used as its negative counterpart'), + 'ea_smtp_server' => array('type' => 'varchar','precision' => '80','comment' => 'smtp server name or ip-address'), + 'ea_smtp_type' => array('type' => 'varchar','precision' => '56','comment' => 'smtp server type; designed to specify the corresponding php class to be used/loaded'), + 'ea_smtp_port' => array('type' => 'int','precision' => '4','comment' => 'port to be used'), + 'ea_smtp_auth' => array('type' => 'varchar','precision' => '3','comment' => 'multistate flag to indicate authentication required'), + 'ea_editforwardingaddress' => array('type' => 'varchar','precision' => '3','comment' => 'yes/no flag to indicate if a user is allowed to edit its own forwardingaddresses; server side restrictions must be met, and a suitable smtp server type selected'), + 'ea_smtp_ldap_server' => array('type' => 'varchar','precision' => '80','comment' => 'unused'), + 'ea_smtp_ldap_basedn' => array('type' => 'varchar','precision' => '200','comment' => 'unused'), + 'ea_smtp_ldap_admindn' => array('type' => 'varchar','precision' => '200','comment' => 'unused'), + 'ea_smtp_ldap_adminpw' => array('type' => 'varchar','precision' => '30','comment' => 'unused'), + 'ea_smtp_ldap_use_default' => array('type' => 'varchar','precision' => '3','comment' => 'unused'), + 'ea_imap_server' => array('type' => 'varchar','precision' => '80','comment' => 'imap server name or ip address'), + 'ea_imap_type' => array('type' => 'varchar','precision' => '56','comment' => 'imap server type, designed to specify the corresponding mail class to be loaded/used'), + 'ea_imap_port' => array('type' => 'int','precision' => '4','comment' => 'imap server port'), + 'ea_imap_login_type' => array('type' => 'varchar','precision' => '20','comment' => 'logintype to be used for authentication vs. the imap server, for this profile'), + 'ea_imap_tsl_auth' => array('type' => 'varchar','precision' => '3','comment' => 'flag to indicate wether to use certificate validation; only affects secure connections'), + 'ea_imap_tsl_encryption' => array('type' => 'varchar','precision' => '3','comment' => 'wether to use encryption 0=none, 1=STARTTLS, 2=TLS, 3=SSL'), + 'ea_imap_enable_cyrus' => array('type' => 'varchar','precision' => '3','comment' => 'flag to indicate if we have some server/system integration for account/email management'), + 'ea_imap_admin_user' => array('type' => 'varchar','precision' => '40','comment' => 'use this username for authentication on administrative purposes; or timed actions (sieve) for a user'), + 'ea_imap_admin_pw' => array('type' => 'varchar','precision' => '40','comment' => 'use this password for authentication on administrative purposes; or timed actions (sieve) for a user'), + 'ea_imap_enable_sieve' => array('type' => 'varchar','precision' => '3','comment' => 'flag to indicate that sieve support is assumed, and may be allowed to be utilized by the users affected by this profile'), + 'ea_imap_sieve_server' => array('type' => 'varchar','precision' => '80','comment' => 'sieve server name or ip-address'), + 'ea_imap_sieve_port' => array('type' => 'int','precision' => '4','comment' => 'sieve server port'), + 'ea_description' => array('type' => 'varchar','precision' => '200','comment' => 'textual descriptor used for readable distinction of profiles'), + 'ea_default_domain' => array('type' => 'varchar','precision' => '100','comment' => 'default domain string, used when vmailmanager is used as auth type for imap (also for smtp if auth is required)'), + 'ea_organisation_name' => array('type' => 'varchar','precision' => '100','comment' => 'textual organization string, may be used in mail header'), + 'ea_user_defined_identities' => array('type' => 'varchar','precision' => '3','comment' => 'yes/no flag to indicate if this profile is allowing the utiliszation of user defined identities'), + 'ea_user_defined_accounts' => array('type' => 'varchar','precision' => '3','comment' => 'yes/no flag to indicate if this profile is allowing the utilization of user defined mail accounts'), + 'ea_order' => array('type' => 'int','precision' => '4','comment' => 'helper to define the order of the profiles'), + 'ea_appname' => array('type' => 'varchar','precision' => '80','comment' => 'appname the profile is to be used for; of no practical use, as of my knowledge, was designed to allow notification to use a specific profile'), + 'ea_group' => array('type' => 'varchar','precision' => '80','comment' => 'the usergroup (primary) the given profile should be applied to','meta' => 'group'), + 'ea_user' => array('type' => 'varchar','precision' => '80','comment' => 'the user the given profile should be applied to','meta' => 'user'), + 'ea_active' => array('type' => 'int','precision' => '4','comment' => 'flag to indicate that a profile is active'), + 'ea_smtp_auth_username' => array('type' => 'varchar','precision' => '128','comment' => 'depending on smtp auth type, use this username for authentication; may hold a semicolon separated emailaddress, to specify the emailaddress to be used on sending e.g.:username;email@address.nfo'), + 'ea_smtp_auth_password' => array('type' => 'varchar','precision' => '80','comment' => 'depending on smtp auth type, the password to be used for authentication'), + 'ea_user_defined_signatures' => array('type' => 'varchar','precision' => '3','comment' => 'flag to indicate, that this profile allows its users to edit and use own signatures (rights to preferences-app needed)'), + 'ea_default_signature' => array('type' => 'text','comment' => 'the default signature (text or html)'), + 'ea_imap_auth_username' => array('type' => 'varchar','precision' => '80','comment' => 'depending on the imap auth type use this username for authentication purposes'), + 'ea_imap_auth_password' => array('type' => 'varchar','precision' => '80','comment' => 'depending on the imap auth type use this password for authentication purposes'), + 'ea_stationery_active_templates' => array('type' => 'text','comment' => 'stationery templates available for the profile') + ), + 'pk' => array('ea_profile_id'), + 'fk' => array(), + 'ix' => array('ea_appname','ea_group'), + 'uc' => array() + ), + 'egw_mailaccounts' => array( + 'fd' => array( + 'mail_id' => array('type' => 'auto','nullable' => False,'comment' => 'the id'), + 'account_id' => array('type' => 'int','precision' => '4','nullable' => False,'comment' => 'account id of the owner, can be user AND group','meta' => 'account'), + 'mail_type' => array('type' => 'int','precision' => '1','nullable' => False,'comment' => '0=active, 1=alias, 2=forward, 3=forwardOnly, 4=quota'), + 'mail_value' => array('type' => 'varchar','precision' => '128','nullable' => False,'comment' => 'the value (that should be) corresponding to the mail_type') + ), + 'pk' => array('mail_id'), + 'fk' => array(), + 'ix' => array('mail_value',array('account_id','mail_type')), + 'uc' => array() + ), + 'egw_ea_accounts' => array( + 'fd' => array( + 'acc_id' => array('type' => 'auto','nullable' => False), + 'acc_name' => array('type' => 'varchar','precision' => '80','comment' => 'description'), + 'ident_id' => array('type' => 'int','precision' => '4','nullable' => False,'comment' => 'standard identity'), + 'acc_imap_host' => array('type' => 'varchar','precision' => '128','nullable' => False,'comment' => 'imap hostname'), + 'acc_imap_ssl' => array('type' => 'int','precision' => '1','nullable' => False,'default' => '0','comment' => '0=none, 1=starttls, 2=tls, 3=ssl, &8=validate certificate'), + 'acc_imap_port' => array('type' => 'int','precision' => '4','nullable' => False,'default' => '143','comment' => 'imap port'), + 'acc_sieve_enabled' => array('type' => 'bool','default' => '0','comment' => 'sieve enabled'), + 'acc_sieve_host' => array('type' => 'varchar','precision' => '128','comment' => 'sieve host, default imap_host'), + 'acc_sieve_port' => array('type' => 'int','precision' => '4','default' => '4190'), + 'acc_folder_sent' => array('type' => 'varchar','precision' => '128','comment' => 'sent folder'), + 'acc_folder_trash' => array('type' => 'varchar','precision' => '128','comment' => 'trash folder'), + 'acc_folder_draft' => array('type' => 'varchar','precision' => '128','comment' => 'draft folder'), + 'acc_folder_template' => array('type' => 'varchar','precision' => '128','comment' => 'template folder'), + 'acc_smtp_host' => array('type' => 'varchar','precision' => '128','comment' => 'smtp hostname'), + 'acc_smtp_ssl' => array('type' => 'int','precision' => '1','nullable' => False,'default' => '0','comment' => '0=none, 1=starttls, 2=tls, 3=ssl, &8=validate certificate'), + 'acc_smtp_port' => array('type' => 'int','precision' => '4','nullable' => False,'default' => '25','comment' => 'smtp port'), + 'acc_smtp_type' => array('type' => 'varchar','precision' => '32','default' => 'emailadmin_smtp','comment' => 'smtp class to use'), + 'acc_imap_type' => array('type' => 'varchar','precision' => '32','default' => 'emailadmin_imap','comment' => 'imap class to use'), + 'acc_imap_logintype' => array('type' => 'varchar','precision' => '20','comment' => 'standard, vmailmgr, admin, uidNumber'), + 'acc_domain' => array('type' => 'varchar','precision' => '100','comment' => 'domain name'), + 'acc_further_identities' => array('type' => 'bool','nullable' => False,'default' => '1','comment' => '0=no, 1=yes'), + 'acc_user_editable' => array('type' => 'bool','nullable' => False,'default' => '1','comment' => '0=no, 1=yes'), + 'acc_sieve_ssl' => array('type' => 'int','precision' => '1','default' => '1','comment' => '0=none, 1=starttls, 2=tls, 3=ssl, &8=validate certificate'), + 'acc_modified' => array('type' => 'timestamp','nullable' => False,'default' => 'current_timestamp'), + 'acc_modifier' => array('type' => 'int','meta' => 'user','precision' => '4'), + 'acc_smtp_auth_session' => array('type' => 'bool','comment' => '0=no, 1=yes, use username/pw from current user'), + 'acc_folder_junk' => array('type' => 'varchar','precision' => '128','comment' => 'junk folder'), + 'acc_imap_default_quota' => array('type' => 'int','precision' => '4','comment' => 'default quota, if no user specific one set'), + 'acc_imap_timeout' => array('type' => 'int','precision' => '2','comment' => 'timeout for imap connection') + ), + 'pk' => array('acc_id'), + 'fk' => array(), + 'ix' => array(), + 'uc' => array() + ), + 'egw_ea_credentials' => array( + 'fd' => array( + 'cred_id' => array('type' => 'auto','nullable' => False), + 'acc_id' => array('type' => 'int','precision' => '4','nullable' => False,'comment' => 'into egw_ea_accounts'), + 'cred_type' => array('type' => 'int','precision' => '1','nullable' => False,'comment' => '&1=imap, &2=smtp, &4=admin'), + 'account_id' => array('type' => 'int','meta' => 'user','precision' => '4','nullable' => False,'comment' => 'account_id or 0=all'), + 'cred_username' => array('type' => 'varchar','precision' => '80','nullable' => False,'comment' => 'username'), + 'cred_password' => array('type' => 'varchar','precision' => '80','comment' => 'password encrypted'), + 'cred_pw_enc' => array('type' => 'int','precision' => '1','default' => '0','comment' => '0=not, 1=user pw, 2=system') + ), + 'pk' => array('cred_id'), + 'fk' => array(), + 'ix' => array(), + 'uc' => array(array('acc_id','account_id','cred_type')) + ), + 'egw_ea_identities' => array( + 'fd' => array( + 'ident_id' => array('type' => 'auto','nullable' => False), + 'acc_id' => array('type' => 'int','precision' => '4','nullable' => False,'comment' => 'for which account'), + 'ident_realname' => array('type' => 'varchar','precision' => '128','nullable' => False,'comment' => 'real name'), + 'ident_email' => array('type' => 'varchar','precision' => '128','comment' => 'email address'), + 'ident_org' => array('type' => 'varchar','precision' => '128','comment' => 'organisation'), + 'ident_signature' => array('type' => 'text','comment' => 'signature text'), + 'account_id' => array('type' => 'int','meta' => 'account','precision' => '4','nullable' => False,'default' => '0','comment' => '0=all users of give mail account'), + 'ident_name' => array('type' => 'varchar','precision' => '128','comment' => 'name of identity to display') + ), + 'pk' => array('ident_id'), + 'fk' => array(), + 'ix' => array(), + 'uc' => array() + ), + 'egw_ea_valid' => array( + 'fd' => array( + 'acc_id' => array('type' => 'int','precision' => '4','nullable' => False), + 'account_id' => array('type' => 'int','meta' => 'account','precision' => '4','nullable' => False) + ), + 'pk' => array(), + 'fk' => array(), + 'ix' => array(array('acc_id','account_id'),array('account_id','acc_id')), + 'uc' => array() + ), + 'egw_ea_notifications' => array( + 'fd' => array( + 'notif_id' => array('type' => 'auto','nullable' => False), + 'acc_id' => array('type' => 'int','precision' => '4','nullable' => False,'comment' => 'mail account'), + 'account_id' => array('type' => 'int','meta' => 'user','precision' => '4','nullable' => False,'comment' => 'user account'), + 'notif_folder' => array('type' => 'varchar','precision' => '255','nullable' => False,'comment' => 'folder name') + ), + 'pk' => array('notif_id'), + 'fk' => array(), + 'ix' => array(array('account_id','acc_id')), + 'uc' => array() + ) +); diff --git a/emailadmin/setup/tables_update.inc.php b/emailadmin/setup/tables_update.inc.php new file mode 100644 index 0000000000..c7bfd40048 --- /dev/null +++ b/emailadmin/setup/tables_update.inc.php @@ -0,0 +1,870 @@ + + * @author Ralf Becker + * @package emailadmin + * @subpackage setup + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @version $Id$ + */ + +function emailadmin_upgrade0_0_3() +{ + $GLOBALS['egw_setup']->oProc->AddColumn('phpgw_emailadmin','smtpType', array('type' => 'int', 'precision' => 4)); + + return $setup_info['emailadmin']['currentver'] = '0.0.4'; +} + + +function emailadmin_upgrade0_0_4() +{ + $GLOBALS['egw_setup']->oProc->AddColumn('phpgw_emailadmin','defaultDomain', array('type' => 'varchar', 'precision' => 100)); + + return $setup_info['emailadmin']['currentver'] = '0.0.5'; +} + + +function emailadmin_upgrade0_0_5() +{ + $GLOBALS['egw_setup']->oProc->AddColumn('phpgw_emailadmin','organisationName', array('type' => 'varchar', 'precision' => 100)); + $GLOBALS['egw_setup']->oProc->AddColumn('phpgw_emailadmin','userDefinedAccounts', array('type' => 'varchar', 'precision' => 3)); + + return $setup_info['emailadmin']['currentver'] = '0.0.6'; +} + + +function emailadmin_upgrade0_0_6() +{ + $GLOBALS['egw_setup']->oProc->AddColumn('phpgw_emailadmin','oldimapcclient',array( + 'type' => 'varchar', + 'precision' => '3' + )); + + return $GLOBALS['setup_info']['emailadmin']['currentver'] = '0.0.007'; +} + + +function emailadmin_upgrade0_0_007() +{ + $GLOBALS['egw_setup']->oProc->RenameColumn('phpgw_emailadmin','oldimapcclient','imapoldcclient'); + + return $GLOBALS['setup_info']['emailadmin']['currentver'] = '0.0.008'; +} + + +function emailadmin_upgrade0_0_008() +{ + return $GLOBALS['setup_info']['emailadmin']['currentver'] = '1.0.0'; +} + + +function emailadmin_upgrade1_0_0() +{ + $GLOBALS['egw_setup']->oProc->AddColumn('phpgw_emailadmin','editforwardingaddress',array( + 'type' => 'varchar', + 'precision' => '3' + )); + + return $GLOBALS['setup_info']['emailadmin']['currentver'] = '1.0.1'; +} + + +function emailadmin_upgrade1_0_1() +{ + $GLOBALS['egw_setup']->oProc->AddColumn('phpgw_emailadmin','ea_order', array('type' => 'int', 'precision' => 4)); + + return $GLOBALS['setup_info']['emailadmin']['currentver'] = '1.0.2'; +} + + +function emailadmin_upgrade1_0_2() +{ + $GLOBALS['egw_setup']->oProc->AddColumn('phpgw_emailadmin','ea_appname', array('type' => 'varchar','precision' => '80')); + $GLOBALS['egw_setup']->oProc->AddColumn('phpgw_emailadmin','ea_group', array('type' => 'varchar','precision' => '80')); + + return $GLOBALS['setup_info']['emailadmin']['currentver'] = '1.0.3'; +} + + +function emailadmin_upgrade1_0_3() +{ + $GLOBALS['egw_setup']->oProc->RenameTable('phpgw_emailadmin','egw_emailadmin'); + + return $GLOBALS['setup_info']['emailadmin']['currentver'] = '1.2'; +} + + +function emailadmin_upgrade1_2() +{ + $GLOBALS['egw_setup']->oProc->RenameColumn('egw_emailadmin','profileID','ea_profile_id'); + $GLOBALS['egw_setup']->oProc->RenameColumn('egw_emailadmin','smtpServer','ea_smtp_server'); + $GLOBALS['egw_setup']->oProc->RenameColumn('egw_emailadmin','smtpType','ea_smtp_type'); + $GLOBALS['egw_setup']->oProc->RenameColumn('egw_emailadmin','smtpPort','ea_smtp_port'); + $GLOBALS['egw_setup']->oProc->RenameColumn('egw_emailadmin','smtpAuth','ea_smtp_auth'); + $GLOBALS['egw_setup']->oProc->RenameColumn('egw_emailadmin','editforwardingaddress','ea_editforwardingaddress'); + $GLOBALS['egw_setup']->oProc->RenameColumn('egw_emailadmin','smtpLDAPServer','ea_smtp_ldap_server'); + $GLOBALS['egw_setup']->oProc->RenameColumn('egw_emailadmin','smtpLDAPBaseDN','ea_smtp_ldap_basedn'); + $GLOBALS['egw_setup']->oProc->RenameColumn('egw_emailadmin','smtpLDAPAdminDN','ea_smtp_ldap_admindn'); + $GLOBALS['egw_setup']->oProc->RenameColumn('egw_emailadmin','smtpLDAPAdminPW','ea_smtp_ldap_adminpw'); + $GLOBALS['egw_setup']->oProc->RenameColumn('egw_emailadmin','smtpLDAPUseDefault','ea_smtp_ldap_use_default'); + $GLOBALS['egw_setup']->oProc->RenameColumn('egw_emailadmin','imapServer','ea_imap_server'); + $GLOBALS['egw_setup']->oProc->RenameColumn('egw_emailadmin','imapType','ea_imap_type'); + $GLOBALS['egw_setup']->oProc->RenameColumn('egw_emailadmin','imapPort','ea_imap_port'); + $GLOBALS['egw_setup']->oProc->RenameColumn('egw_emailadmin','imapLoginType','ea_imap_login_type'); + $GLOBALS['egw_setup']->oProc->RenameColumn('egw_emailadmin','imapTLSAuthentication','ea_imap_tsl_auth'); + $GLOBALS['egw_setup']->oProc->RenameColumn('egw_emailadmin','imapTLSEncryption','ea_imap_tsl_encryption'); + $GLOBALS['egw_setup']->oProc->RenameColumn('egw_emailadmin','imapEnableCyrusAdmin','ea_imap_enable_cyrus'); + $GLOBALS['egw_setup']->oProc->RenameColumn('egw_emailadmin','imapAdminUsername','ea_imap_admin_user'); + $GLOBALS['egw_setup']->oProc->RenameColumn('egw_emailadmin','imapAdminPW','ea_imap_admin_pw'); + $GLOBALS['egw_setup']->oProc->RenameColumn('egw_emailadmin','imapEnableSieve','ea_imap_enable_sieve'); + $GLOBALS['egw_setup']->oProc->RenameColumn('egw_emailadmin','imapSieveServer','ea_imap_sieve_server'); + $GLOBALS['egw_setup']->oProc->RenameColumn('egw_emailadmin','imapSievePort','ea_imap_sieve_port'); + $GLOBALS['egw_setup']->oProc->RenameColumn('egw_emailadmin','description','ea_description'); + $GLOBALS['egw_setup']->oProc->RenameColumn('egw_emailadmin','defaultDomain','ea_default_domain'); + $GLOBALS['egw_setup']->oProc->RenameColumn('egw_emailadmin','organisationName','ea_organisation_name'); + $GLOBALS['egw_setup']->oProc->RenameColumn('egw_emailadmin','userDefinedAccounts','ea_user_defined_accounts'); + $GLOBALS['egw_setup']->oProc->RenameColumn('egw_emailadmin','imapoldcclient','ea_imapoldcclient'); + + return $GLOBALS['setup_info']['emailadmin']['currentver'] = '1.2.001'; +} + + +function emailadmin_upgrade1_2_001() +{ + /* done by RefreshTable() anyway + $GLOBALS['egw_setup']->oProc->AddColumn('egw_emailadmin','ea_smtp_auth_username',array( + 'type' => 'varchar', + 'precision' => '80' + ));*/ + /* done by RefreshTable() anyway + $GLOBALS['egw_setup']->oProc->AddColumn('egw_emailadmin','ea_smtp_auth_password',array( + 'type' => 'varchar', + 'precision' => '80' + ));*/ + $GLOBALS['egw_setup']->oProc->RefreshTable('egw_emailadmin',array( + 'fd' => array( + 'ea_profile_id' => array('type' => 'auto','nullable' => False), + 'ea_smtp_server' => array('type' => 'varchar','precision' => '80'), + 'ea_smtp_type' => array('type' => 'int','precision' => '4'), + 'ea_smtp_port' => array('type' => 'int','precision' => '4'), + 'ea_smtp_auth' => array('type' => 'varchar','precision' => '3'), + 'ea_editforwardingaddress' => array('type' => 'varchar','precision' => '3'), + 'ea_smtp_ldap_server' => array('type' => 'varchar','precision' => '80'), + 'ea_smtp_ldap_basedn' => array('type' => 'varchar','precision' => '200'), + 'ea_smtp_ldap_admindn' => array('type' => 'varchar','precision' => '200'), + 'ea_smtp_ldap_adminpw' => array('type' => 'varchar','precision' => '30'), + 'ea_smtp_ldap_use_default' => array('type' => 'varchar','precision' => '3'), + 'ea_imap_server' => array('type' => 'varchar','precision' => '80'), + 'ea_imap_type' => array('type' => 'int','precision' => '4'), + 'ea_imap_port' => array('type' => 'int','precision' => '4'), + 'ea_imap_login_type' => array('type' => 'varchar','precision' => '20'), + 'ea_imap_tsl_auth' => array('type' => 'varchar','precision' => '3'), + 'ea_imap_tsl_encryption' => array('type' => 'varchar','precision' => '3'), + 'ea_imap_enable_cyrus' => array('type' => 'varchar','precision' => '3'), + 'ea_imap_admin_user' => array('type' => 'varchar','precision' => '40'), + 'ea_imap_admin_pw' => array('type' => 'varchar','precision' => '40'), + 'ea_imap_enable_sieve' => array('type' => 'varchar','precision' => '3'), + 'ea_imap_sieve_server' => array('type' => 'varchar','precision' => '80'), + 'ea_imap_sieve_port' => array('type' => 'int','precision' => '4'), + 'ea_description' => array('type' => 'varchar','precision' => '200'), + 'ea_default_domain' => array('type' => 'varchar','precision' => '100'), + 'ea_organisation_name' => array('type' => 'varchar','precision' => '100'), + 'ea_user_defined_accounts' => array('type' => 'varchar','precision' => '3'), + 'ea_imapoldcclient' => array('type' => 'varchar','precision' => '3'), + 'ea_order' => array('type' => 'int','precision' => '4'), + 'ea_appname' => array('type' => 'varchar','precision' => '80'), + 'ea_group' => array('type' => 'varchar','precision' => '80'), + 'ea_smtp_auth_username' => array('type' => 'varchar','precision' => '80'), + 'ea_smtp_auth_password' => array('type' => 'varchar','precision' => '80') + ), + 'pk' => array('ea_profile_id'), + 'fk' => array(), + 'ix' => array('ea_appname','ea_group'), + 'uc' => array() + )); + + return $GLOBALS['setup_info']['emailadmin']['currentver'] = '1.2.002'; +} + + +function emailadmin_upgrade1_2_002() +{ + return $GLOBALS['setup_info']['emailadmin']['currentver'] = '1.4'; +} + + +function emailadmin_upgrade1_4() +{ + $GLOBALS['egw_setup']->oProc->AddColumn('egw_emailadmin','ea_user_defined_signatures',array( + 'type' => 'varchar', + 'precision' => '3' + )); + $GLOBALS['egw_setup']->oProc->AddColumn('egw_emailadmin','ea_default_signature',array( + 'type' => 'varchar', + 'precision' => '255' + )); + + return $GLOBALS['setup_info']['emailadmin']['currentver'] = '1.4.001'; +} + + +function emailadmin_upgrade1_4_001() +{ + $GLOBALS['egw_setup']->oProc->AddColumn('egw_emailadmin','ea_user_defined_identities',array( + 'type' => 'varchar', + 'precision' => '3' + )); + + return $GLOBALS['setup_info']['emailadmin']['currentver'] = '1.5.001'; +} + + +function emailadmin_upgrade1_5_001() +{ + $GLOBALS['egw_setup']->oProc->AddColumn('egw_emailadmin','ea_user',array( + 'type' => 'varchar', + 'precision' => '80' + )); + $GLOBALS['egw_setup']->oProc->AddColumn('egw_emailadmin','ea_active',array( + 'type' => 'int', + 'precision' => '4' + )); + $GLOBALS['phpgw_setup']->oProc->query("UPDATE egw_emailadmin set ea_user='0', ea_active=1",__LINE__,__FILE__); + return $GLOBALS['setup_info']['emailadmin']['currentver'] = '1.5.002'; +} + + +function emailadmin_upgrade1_5_002() +{ + $GLOBALS['egw_setup']->oProc->AddColumn('egw_emailadmin','ea_imap_auth_username',array( + 'type' => 'varchar', + 'precision' => '80' + )); + $GLOBALS['egw_setup']->oProc->AddColumn('egw_emailadmin','ea_imap_auth_password',array( + 'type' => 'varchar', + 'precision' => '80' + )); + return $GLOBALS['setup_info']['emailadmin']['currentver'] = '1.5.003'; +} + + +function emailadmin_upgrade1_5_003() +{ + return $GLOBALS['setup_info']['emailadmin']['currentver'] = '1.5.004'; +} + + +function emailadmin_upgrade1_5_004() +{ + return $GLOBALS['setup_info']['emailadmin']['currentver'] = '1.6'; +} + + +function emailadmin_upgrade1_6() +{ + $GLOBALS['egw_setup']->oProc->AlterColumn('egw_emailadmin','ea_default_signature',array( + 'type' => 'text' + )); + + return $GLOBALS['setup_info']['emailadmin']['currentver'] = '1.6.001'; +} + +function emailadmin_upgrade1_6_001() +{ + $GLOBALS['egw_setup']->oProc->AddColumn('egw_emailadmin','ea_stationery_active_templates',array( + 'type' => 'text' + )); + + return $GLOBALS['setup_info']['emailadmin']['currentver'] = '1.8'; // was '1.7.003'; +} + +function emailadmin_upgrade1_7_003() +{ + $GLOBALS['egw_setup']->oProc->AlterColumn('egw_emailadmin','ea_imap_type',array( + 'type' => 'varchar', + 'precision' => 56, + )); + $GLOBALS['egw_setup']->oProc->AlterColumn('egw_emailadmin','ea_smtp_type',array( + 'type' => 'varchar', + 'precision' => 56, + )); + foreach (array('1'=>'defaultsmtp', '2'=>'postfixldap', '3'=>'postfixinetorgperson', '4'=>'smtpplesk', '5' =>'postfixdbmailuser') as $id => $newtype) + { + $GLOBALS['egw_setup']->oProc->query('update egw_emailadmin set ea_smtp_type=\''.$newtype.'\' where ea_smtp_type=\''.$id.'\'',__LINE__,__FILE__); + } + foreach (array('2'=>'defaultimap', '3'=>'cyrusimap', '4'=>'dbmailqmailuser', '5'=>'pleskimap', '6' =>'dbmaildbmailuser') as $id => $newtype) + { + $GLOBALS['egw_setup']->oProc->query('update egw_emailadmin set ea_imap_type=\''.$newtype.'\' where ea_imap_type=\''.$id.'\'',__LINE__,__FILE__); + } + return $GLOBALS['setup_info']['emailadmin']['currentver'] = '1.9.001'; // was '1.7.004'; +} + +function emailadmin_upgrade1_8() +{ + emailadmin_upgrade1_7_003(); + + return $GLOBALS['setup_info']['emailadmin']['currentver'] = '1.9.001'; +} + +function emailadmin_upgrade1_7_004() +{ + return $GLOBALS['setup_info']['emailadmin']['currentver'] = '1.9.001'; +} + +function emailadmin_upgrade1_9_001() +{ + $GLOBALS['egw_setup']->oProc->RefreshTable('egw_emailadmin',array( + 'fd' => array( + 'ea_profile_id' => array('type' => 'auto','nullable' => False), + 'ea_smtp_server' => array('type' => 'varchar','precision' => '80'), + 'ea_smtp_type' => array('type' => 'varchar','precision' => '56'), + 'ea_smtp_port' => array('type' => 'int','precision' => '4'), + 'ea_smtp_auth' => array('type' => 'varchar','precision' => '3'), + 'ea_editforwardingaddress' => array('type' => 'varchar','precision' => '3'), + 'ea_smtp_ldap_server' => array('type' => 'varchar','precision' => '80'), + 'ea_smtp_ldap_basedn' => array('type' => 'varchar','precision' => '200'), + 'ea_smtp_ldap_admindn' => array('type' => 'varchar','precision' => '200'), + 'ea_smtp_ldap_adminpw' => array('type' => 'varchar','precision' => '30'), + 'ea_smtp_ldap_use_default' => array('type' => 'varchar','precision' => '3'), + 'ea_imap_server' => array('type' => 'varchar','precision' => '80'), + 'ea_imap_type' => array('type' => 'varchar','precision' => '56'), + 'ea_imap_port' => array('type' => 'int','precision' => '4'), + 'ea_imap_login_type' => array('type' => 'varchar','precision' => '20'), + 'ea_imap_tsl_auth' => array('type' => 'varchar','precision' => '3'), + 'ea_imap_tsl_encryption' => array('type' => 'varchar','precision' => '3'), + 'ea_imap_enable_cyrus' => array('type' => 'varchar','precision' => '3'), + 'ea_imap_admin_user' => array('type' => 'varchar','precision' => '40'), + 'ea_imap_admin_pw' => array('type' => 'varchar','precision' => '40'), + 'ea_imap_enable_sieve' => array('type' => 'varchar','precision' => '3'), + 'ea_imap_sieve_server' => array('type' => 'varchar','precision' => '80'), + 'ea_imap_sieve_port' => array('type' => 'int','precision' => '4'), + 'ea_description' => array('type' => 'varchar','precision' => '200'), + 'ea_default_domain' => array('type' => 'varchar','precision' => '100'), + 'ea_organisation_name' => array('type' => 'varchar','precision' => '100'), + 'ea_user_defined_identities' => array('type' => 'varchar','precision' => '3'), + 'ea_user_defined_accounts' => array('type' => 'varchar','precision' => '3'), + 'ea_order' => array('type' => 'int','precision' => '4'), + 'ea_appname' => array('type' => 'varchar','precision' => '80'), + 'ea_group' => array('type' => 'varchar','precision' => '80'), + 'ea_user' => array('type' => 'varchar','precision' => '80'), + 'ea_active' => array('type' => 'int','precision' => '4'), + 'ea_smtp_auth_username' => array('type' => 'varchar','precision' => '80'), + 'ea_smtp_auth_password' => array('type' => 'varchar','precision' => '80'), + 'ea_user_defined_signatures' => array('type' => 'varchar','precision' => '3'), + 'ea_default_signature' => array('type' => 'text'), + 'ea_imap_auth_username' => array('type' => 'varchar','precision' => '80'), + 'ea_imap_auth_password' => array('type' => 'varchar','precision' => '80'), + 'ea_stationery_active_templates' => array('type' => 'text') + ), + 'pk' => array('ea_profile_id'), + 'fk' => array(), + 'ix' => array('ea_appname','ea_group'), + 'uc' => array() + )); + return $GLOBALS['setup_info']['emailadmin']['currentver'] = '1.9.002'; +} + +function emailadmin_upgrade1_9_002() +{ + // convert serialized stationery templates setting to eTemplate store style + foreach($GLOBALS['egw_setup']->db->query('SELECT ea_profile_id,ea_stationery_active_templates FROM egw_emailadmin + WHERE ea_stationery_active_templates IS NOT NULL',__LINE__,__FILE__) as $row) + { + if(is_array(($templates=unserialize($row['ea_stationery_active_templates'])))) + { + $GLOBALS['egw_setup']->db->query('UPDATE egw_emailadmin SET ea_stationery_active_templates="'.implode(',',$templates).'"' + .' WHERE ea_profile_id='.(int)$row['ea_profile_id'],__LINE__,__FILE__); + } + unset($templates); + } + + return $GLOBALS['setup_info']['emailadmin']['currentver'] = '1.9.003'; +} + +function emailadmin_upgrade1_9_003() +{ + $GLOBALS['egw_setup']->oProc->AlterColumn('egw_emailadmin','ea_smtp_auth_username',array( + 'type' => 'varchar', + 'precision' => '128', + )); + + return $GLOBALS['setup_info']['emailadmin']['currentver'] = '1.9.004'; +} + +function emailadmin_upgrade1_9_004() +{ + return $GLOBALS['setup_info']['emailadmin']['currentver'] = '1.9.005'; +} + +function emailadmin_upgrade1_9_005() +{ + $GLOBALS['egw_setup']->oProc->CreateTable('egw_mailaccounts',array( + 'fd' => array( + 'mail_id' => array('type' => 'auto','nullable' => False), + 'account_id' => array('type' => 'int','precision' => '4','nullable' => False), + 'mail_type' => array('type' => 'int','precision' => '1','nullable' => False,'comment' => '0=active, 1=alias, 2=forward, 3=forwardOnly, 4=quota'), + 'mail_value' => array('type' => 'varchar','precision' => '128','nullable' => False) + ), + 'pk' => array('mail_id'), + 'fk' => array(), + 'ix' => array('mail_value',array('account_id','mail_type')), + 'uc' => array() + )); + + return $GLOBALS['setup_info']['emailadmin']['currentver'] = '1.9.006'; +} + + +function emailadmin_upgrade1_9_006() +{ + $GLOBALS['egw_setup']->oProc->CreateTable('egw_ea_accounts',array( + 'fd' => array( + 'acc_id' => array('type' => 'auto','nullable' => False), + 'acc_name' => array('type' => 'varchar','precision' => '80','comment' => 'description'), + 'ident_id' => array('type' => 'int','precision' => '4','nullable' => False,'comment' => 'standard identity'), + 'acc_imap_host' => array('type' => 'varchar','precision' => '128','nullable' => False,'comment' => 'imap hostname'), + 'acc_imap_ssl' => array('type' => 'int','precision' => '1','nullable' => False,'default' => '0','comment' => '0=none, 1=starttls, 2=tls, 3=ssl, &8=validate certificate'), + 'acc_imap_port' => array('type' => 'int','precision' => '4','nullable' => False,'default' => '143','comment' => 'imap port'), + 'acc_sieve_enabled' => array('type' => 'bool','default' => '0','comment' => 'sieve enabled'), + 'acc_sieve_host' => array('type' => 'varchar','precision' => '128','comment' => 'sieve host, default imap_host'), + 'acc_sieve_port' => array('type' => 'int','precision' => '4','default' => '4190'), + 'acc_folder_sent' => array('type' => 'varchar','precision' => '128','comment' => 'sent folder'), + 'acc_folder_trash' => array('type' => 'varchar','precision' => '128','comment' => 'trash folder'), + 'acc_folder_draft' => array('type' => 'varchar','precision' => '128','comment' => 'draft folder'), + 'acc_folder_template' => array('type' => 'varchar','precision' => '128','comment' => 'template folder'), + 'acc_smtp_host' => array('type' => 'varchar','precision' => '128','comment' => 'smtp hostname'), + 'acc_smtp_ssl' => array('type' => 'int','precision' => '1','nullable' => False,'default' => '0','comment' => '0=none, 1=starttls, 2=tls, 3=ssl, &8=validate certificate'), + 'acc_smtp_port' => array('type' => 'int','precision' => '4','nullable' => False,'default' => '25','comment' => 'smtp port'), + 'acc_smtp_type' => array('type' => 'varchar','precision' => '32','default' => 'emailadmin_smtp','comment' => 'smtp class to use'), + 'acc_imap_type' => array('type' => 'varchar','precision' => '32','default' => 'emailadmin_imap','comment' => 'imap class to use'), + 'acc_imap_logintype' => array('type' => 'varchar','precision' => '20','comment' => 'standard, vmailmgr, admin, uidNumber'), + 'acc_domain' => array('type' => 'varchar','precision' => '100','comment' => 'domain name'), + 'acc_further_identities' => array('type' => 'bool','nullable' => False,'default' => '1','comment' => '0=no, 1=yes'), + 'acc_user_editable' => array('type' => 'bool','nullable' => False,'default' => '1','comment' => '0=no, 1=yes'), + 'acc_sieve_ssl' => array('type' => 'int','precision' => '1','default' => '1','comment' => '0=none, 1=starttls, 2=tls, 3=ssl, &8=validate certificate'), + 'acc_modified' => array('type' => 'timestamp','nullable' => False,'default' => 'current_timestamp'), + 'acc_modifier' => array('type' => 'int','meta' => 'user','precision' => '4'), + 'acc_smtp_auth_session' => array('type' => 'bool','comment' => '0=no, 1=yes, use username/pw from current user') + ), + 'pk' => array('acc_id'), + 'fk' => array(), + 'ix' => array(), + 'uc' => array() + )); + + return $GLOBALS['setup_info']['emailadmin']['currentver'] = '1.9.007'; +} + + +function emailadmin_upgrade1_9_007() +{ + $GLOBALS['egw_setup']->oProc->CreateTable('egw_ea_credentials',array( + 'fd' => array( + 'cred_id' => array('type' => 'auto','nullable' => False), + 'acc_id' => array('type' => 'int','precision' => '4','nullable' => False,'comment' => 'into egw_ea_accounts'), + 'cred_type' => array('type' => 'int','precision' => '1','nullable' => False,'comment' => '&1=imap, &2=smtp, &4=admin'), + 'account_id' => array('type' => 'int','meta' => 'user','precision' => '4','nullable' => False,'comment' => 'account_id or 0=all'), + 'cred_username' => array('type' => 'varchar','precision' => '80','nullable' => False,'comment' => 'username'), + 'cred_password' => array('type' => 'varchar','precision' => '80','comment' => 'password encrypted'), + 'cred_pw_enc' => array('type' => 'int','precision' => '1','default' => '0','comment' => '0=not, 1=user pw, 2=system') + ), + 'pk' => array('cred_id'), + 'fk' => array(), + 'ix' => array(), + 'uc' => array(array('acc_id','account_id','cred_type')) + )); + + return $GLOBALS['setup_info']['emailadmin']['currentver'] = '1.9.008'; +} + + +function emailadmin_upgrade1_9_008() +{ + $GLOBALS['egw_setup']->oProc->CreateTable('egw_ea_identities',array( + 'fd' => array( + 'ident_id' => array('type' => 'auto','nullable' => False), + 'acc_id' => array('type' => 'int','precision' => '4','nullable' => False,'comment' => 'for which account'), + 'ident_realname' => array('type' => 'varchar','precision' => '128','nullable' => False,'comment' => 'real name'), + 'ident_email' => array('type' => 'varchar','precision' => '128','comment' => 'email address'), + 'ident_org' => array('type' => 'varchar','precision' => '128','comment' => 'organisation'), + 'ident_signature' => array('type' => 'text','comment' => 'signature text'), + 'account_id' => array('type' => 'int','meta' => 'account','precision' => '4','nullable' => False,'default' => '0','comment' => '0=all users of give mail account') + ), + 'pk' => array('ident_id'), + 'fk' => array(), + 'ix' => array(), + 'uc' => array() + )); + + return $GLOBALS['setup_info']['emailadmin']['currentver'] = '1.9.009'; +} + + +function emailadmin_upgrade1_9_009() +{ + $GLOBALS['egw_setup']->oProc->CreateTable('egw_ea_valid',array( + 'fd' => array( + 'acc_id' => array('type' => 'int','precision' => '4','nullable' => False), + 'account_id' => array('type' => 'int','meta' => 'account','precision' => '4','nullable' => False) + ), + 'pk' => array(), + 'fk' => array(), + 'ix' => array(array('account_id','acc_id')), + 'uc' => array(array('acc_id','account_id')) + )); + + return $GLOBALS['setup_info']['emailadmin']['currentver'] = '1.9.010'; +} + + +/** + * Migrate eMailAdmin profiles to accounts + */ +function emailadmin_upgrade1_9_010() +{ + static $prot2ssl = array('ssl' => 3, 'tls' => 2, 'starttls' => 1); + $acc_ids = array(); + // migrate personal felamimail accounts, identities and signatures + try { + $db = $GLOBALS['egw_setup']->db; + foreach($db->select('egw_emailadmin', '*', array('ea_active' => 1), __LINE__, __FILE__, false, + // order general profiles first, then group profiles and user specific ones last + 'ORDER BY ea_group IS NULL AND ea_user IS NULL DESC,ea_group IS NULL,ea_user', 'emailadmin') as $row) + { + $owner = $row['ea_user'] > 0 ? (int)$row['ea_user'] : ($row['ea_group'] ? -abs($row['ea_group']) : 0); + + // always store general profiles and group profiles + // only store user profiles if they contain at least an imap_host + // otherwise store just the credentials for that user and account of his primary group or first general account + if ($owner <= 0 || $owner > 0 && !empty($row['ea_imap_host']) || + !($acc_id = ($primary_group = accounts::id2name($owner, 'account_primary_group')) && + isset($acc_ids[$primary_group]) ? $acc_ids[$primary_group] : $acc_ids['0'])) + { + $prefs = new preferences($owner); + $all_prefs = $prefs->read_repository(); + $pref_values = $all_prefs['felamimail']; + + // create standard identity for account + $identity = array( + 'acc_id' => 0, + 'ident_realname' => '', + 'ident_email' => '', + 'ident_org' => $row['ea_organisation_name'], + 'ident_signature' => $row['ea_default_signature'], + ); + $db->insert('egw_ea_identities', $identity, false, __LINE__, __FILE__, 'emailadmin'); + $ident_id = $db->get_last_insert_id('egw_ea_identities', 'ident_id'); + + // create account + $smtp_ssl = 0; + if (strpos($row['ea_smtp_server'], '://') !== false) + { + list($prot, $row['ea_smtp_server']) = explode('://', $row['ea_smtp_server']); + $smtp_ssl = (int)$prot2ssl[$prot]; + } + $account = array( + 'acc_name' => $row['ea_description'], + 'ident_id' => $ident_id, + 'acc_imap_type' => emailadmin_account::getIcClass($row['ea_imap_type']), + 'acc_imap_logintype' => $row['ea_imap_login_type'], + 'acc_imap_host' => $row['ea_imap_server'], + 'acc_imap_ssl' => $row['ea_imap_tsl_encryption'] | ($row['ea_imap_tsl_auth'] === 'yes' ? 8 : 0), + 'acc_imap_port' => $row['ea_imap_port'], + 'acc_sieve_enabled' => $row['ea_imap_enable_sieve'] == 'yes', + 'acc_sieve_host' => $row['ea_imap_sieve_server'], + 'acc_sieve_ssl' => $row['fm_ic_sieve_port'] == 5190 ? 2 : 1, + 'acc_sieve_port' => $row['ea_imap_sieve_port'], + 'acc_folder_sent' => $pref_values['sentFolder'], + 'acc_folder_trash' => $pref_values['trashFolder'], + 'acc_folder_draft' => $pref_values['draftFolder'], + 'acc_folder_template' => $pref_values['templateFolder'], + 'acc_smtp_type' => $row['ea_smtp_type'], + 'acc_smtp_host' => $row['ea_smtp_server'], + 'acc_smtp_ssl' => $smtp_ssl, + 'acc_smtp_port' => $row['ea_smtp_port'], + 'acc_domain' => $row['ea_default_domain'], + 'acc_further_identities' => $row['ea_user_defined_identities'] == 'yes' || + $row['ea_user_defined_signatures'] == 'yes', // both together are now called identities + 'acc_user_editable' => false, + 'acc_smtp_auth_session' => $row['ea_smtp_auth'] == 'ann' || + $row['ea_smtp_auth'] == 'yes' && empty($row['ea_smtp_auth_username']), + ); + $db->insert('egw_ea_accounts', $account, false, __LINE__, __FILE__, 'emailadmin'); + $acc_id = $db->get_last_insert_id('egw_ea_accounts', 'acc_id'); + + // remember acc_id by owner, to be able to base credential only profiles on them + if ($owner <= 0 && !isset($acc_ids[$owner])) $acc_ids[(string)$owner] = $acc_id; + + // update above created identity with account acc_id + $db->update('egw_ea_identities', array('acc_id' => $acc_id), array('ident_id' => $ident_id), __LINE__, __FILE__, 'emailadmin'); + + // make account valid for given owner + $db->insert('egw_ea_valid', array( + 'acc_id' => $acc_id, + 'account_id' => $owner, + ), false, __LINE__, __FILE__, 'emailadmin'); + } + // credentials are either stored for specific user or all users (account_id=0), not group-specific + if (!($owner > 0)) $owner = 0; + // add imap credentials if necessary + $cred_type = $row['ea_smpt_auth'] != 'no' && $row['ea_imap_auth_username'] && $row['ea_smtp_auth_username'] && + $row['ea_imap_auth_username'] == $row['ea_smtp_auth_username'] && + $row['ea_imap_auth_password'] == $row['ea_smtp_auth_password'] ? 3 : 1; + if ($row['ea_imap_auth_username']) + { + emailadmin_credentials::write($acc_id, $row['ea_imap_auth_username'], $row['ea_imap_auth_password'], $cred_type, $owner); + } + // add smtp credentials if necessary and different from imap + if ($row['ea_smpt_auth'] != 'no' && !empty($row['ea_smtp_auth_username']) && $cred_type != 3) + { + emailadmin_credentials::write($acc_id, $row['ea_smtp_auth_username'], $row['ea_smtp_auth_password'], 2, $owner); + } + // add admin credentials + if ($row['ea_imap_enable_cyrus'] == 'yes' && !empty($row['ea_imap_admin_user'])) + { + emailadmin_credentials::write($acc_id, $row['ea_imap_admin_user'], $row['ea_imap_admin_pw'], 8, $owner); + } + } + // ToDo: migrate all not yet via personal fmail profiles migrated signatures + } + catch(Exception $e) { + // ignore all errors, eg. because FMail is not installed + echo "

".$e->getMessage()."

\n"; + } + return $GLOBALS['setup_info']['emailadmin']['currentver'] = '1.9.011'; +} + +/** + * Helper function for 1.9.011 upgrade to query standard identity of a given user + * + * There's no defined standard identity, we simply pick first one we find + * sorting results by: + * - prefering identical email or same-domain + * - prefering personal accounts over all-user ones + * - for all-user ones we prefer a personal identitiy (account_id!=0) + * + * @param int $account_id + * @param string $email optional email to be used to find matching account/identity with identical email or same domain + * @return array with ident_* and acc_id + */ +function emailadmin_std_identity($account_id, $email=null) +{ + if ($email) list(, $domain) = explode('@', $email); + return $GLOBALS['egw_setup']->db->select('egw_ea_accounts', 'egw_ea_identities.*', + 'egw_ea_valid.account_id IN (0,'.(int)$account_id.') AND egw_ea_identities.account_id IN (0,'.(int)$account_id.')', + __LINE__, __FILE__, 0, + 'ORDER BY '.($email ? + 'ident_email='.$GLOBALS['egw_setup']->db->quote($email).' DESC,'. + 'ident_email LIKE '.$GLOBALS['egw_setup']->db->quote('%@'.$domain).' DESC,' : ''). + 'egw_ea_identities.account_id DESC,egw_ea_valid.account_id DESC', 'emailadmin', 1, + 'JOIN egw_ea_valid ON egw_ea_accounts.acc_id=egw_ea_valid.acc_id '. + 'JOIN egw_ea_identities ON egw_ea_accounts.acc_id=egw_ea_identities.acc_id')->fetch(); +} + +/** + * Migrate (personal) FMail accounts to eMailAdmin accounts + */ +function emailadmin_upgrade1_9_011() +{ + static $prot2ssl = array('ssl' => 3, 'tls' => 2, 'starttls' => 1); + $std_identity = null; + // migrate personal felamimail accounts, identities and signatures + $db = $GLOBALS['egw_setup']->db; + if (in_array('egw_felamimail_accounts', $db->table_names(true))) + { + try { + // migrate real fmail accounts, but not yet identities (fm_ic_hostname is NULL) + foreach($db->select('egw_felamimail_accounts', '*', 'fm_ic_hostname IS NOT NULL', __LINE__, __FILE__, false, '', 'felamimail') as $row) + { + $prefs = new preferences($row['fm_owner']); + $all_prefs = $prefs->read_repository(); + $pref_values = $all_prefs['felamimail']; + + // create standard identity for account + $identity = array( + 'acc_id' => 0, + 'ident_realname' => $row['fm_realname'], + 'ident_email' => $row['fm_emailaddress'], + 'ident_org' => $row['fm_organisation'], + 'ident_signature' => $db->select('egw_felamimail_signatures', 'fm_signature', array( + 'fm_signatureid' => $row['fm_signatureid'], + ), __LINE__, __FILE__, false, '', 'felamimail')->fetchColumn(), + ); + $db->insert('egw_ea_identities', $identity, false, __LINE__, __FILE__, 'emailadmin'); + $ident_id = $db->get_last_insert_id('egw_ea_identities', 'ident_id'); + + // create account + $og_ssl = 0; + if (strpos($row['fm_og_hostname'], '://') !== false) + { + list($prot, $row['fm_og_hostname']) = explode('://', $row['fm_og_hostname']); + $og_ssl = (int)$prot2ssl[$prot]; + } + $account = array( + 'acc_name' => $row['fm_emailaddress'], + 'ident_id' => $ident_id, + 'acc_imap_host' => $row['fm_ic_hostname'], + 'acc_imap_ssl' => $row['fm_ic_encryption'] | ($row['fm_ic_validatecertificate'] ? 8 : 0), + 'acc_imap_port' => $row['fm_ic_port'], + 'acc_sieve_enabled' => $row['fm_ic_enable_sieve'], + 'acc_sieve_host' => $row['fm_ic_sieve_server'], + 'acc_sieve_ssl' => $row['fm_ic_sieve_port'] == 5190 ? 2 : 1, + 'acc_sieve_port' => $row['fm_ic_sieve_port'], + 'acc_folder_sent' => $row['fm_ic_sentfolder'] ? $row['fm_ic_sentfolder'] : $pref_values['sentFolder'], + 'acc_folder_trash' => $row['fm_ic_trashfolder'] ? $row['fm_ic_trashfolder'] : $pref_values['trashFolder'], + 'acc_folder_draft' => $row['fm_ic_draftfolder'] ? $row['fm_ic_draftfolder'] : $pref_values['draftFolder'], + 'acc_folder_template' => $row['fm_ic_templatefolder'] ? $row['fm_ic_templatefolder'] : $pref_values['templateFolder'], + 'acc_smtp_host' => $row['fm_og_hostname'], + 'acc_smtp_ssl' => $og_ssl, + 'acc_smtp_port' => $row['fm_og_port'], + ); + $db->insert('egw_ea_accounts', $account, false, __LINE__, __FILE__, 'emailadmin'); + $acc_id = $db->get_last_insert_id('egw_ea_accounts', 'acc_id'); + $identity['acc_id'] = $acc_id; + if (!isset($std_identity[$row['fm_owner']])) $std_identity[$row['fm_owner']] = $identity; + // update above created identity with account acc_id + $db->update('egw_ea_identities', array('acc_id' => $acc_id), array('ident_id' => $ident_id), __LINE__, __FILE__, 'emailadmin'); + + // make account valid for given owner + $db->insert('egw_ea_valid', array( + 'acc_id' => $acc_id, + 'account_id' => $row['fm_owner'], + ), false, __LINE__, __FILE__, 'emailadmin'); + + // add imap credentials + $cred_type = $row['fm_og_smtpauth'] && $row['fm_ic_username'] == $row['fm_og_username'] && + $row['fm_ic_password'] == $row['fm_og_password'] ? 3 : 1; + emailadmin_credentials::write($acc_id, $row['fm_ic_username'], $row['fm_ic_password'], $cred_type, $row['fm_owner']); + // add smtp credentials if necessary and different from imap + if ($row['fm_og_smtpauth'] && $cred_type != 3) + { + emailadmin_credentials::write($acc_id, $row['fm_og_username'], $row['fm_og_password'], 2, $row['fm_owner']); + } + } + + // migrate fmail identities (fm_ic_hostname is NULL), not real fmail account done above + foreach($db->select('egw_felamimail_accounts', '*', 'fm_ic_hostname IS NULL', __LINE__, __FILE__, false, '', 'felamimail') as $row) + { + if (!($std_identity = emailadmin_std_identity($row['fm_owner']))) + { + continue; // no account found to add identity to + } + // create standard identity for account + $identity = array( + 'acc_id' => $std_identity['acc_id'], + 'ident_realname' => $row['fm_realname'], + 'ident_email' => $row['fm_emailaddress'], + 'ident_org' => $row['fm_organisation'], + 'ident_signature' => $db->select('egw_felamimail_signatures', 'fm_signature', array( + 'fm_signatureid' => $row['fm_signatureid'], + ), __LINE__, __FILE__, false, '', 'felamimail')->fetchColumn(), + 'account_id' => $row['fm_owner'], + ); + $db->insert('egw_ea_identities', $identity, false, __LINE__, __FILE__, 'emailadmin'); + } + + // migrate all not yet as standard-signatures migrated signatures to identities of first migrated fmail profile + // completing them with realname, email and org from standard-signature of given account + foreach($db->select('egw_felamimail_signatures', '*', + "fm_signatureid NOT IN (SELECT fm_signatureid FROM egw_felamimail_accounts)", + __LINE__, __FILE__, false, '', 'felamimail') as $row) + { + if (!($std_identity = emailadmin_std_identity($row['fm_accountid']))) + { + continue; // ignore signatures for whos owner (fm_accountid!) we have no personal profile + } + $identity = $std_identity; unset($identity['ident_id']); + $identity['ident_realname'] = $std_identity['ident_realname'].' ('.$row['fm_description'].')'; + $identity['ident_signature'] = $row['fm_signature']; + $identity['account_id'] = $row['fm_accountid']; + $db->insert('egw_ea_identities', $identity, false, __LINE__, __FILE__, 'emailadmin'); + } + } + catch(Exception $e) { + // ignore all errors, eg. because FMail is not installed + echo "

".$e->getMessage()."

\n"; + } + } + return $GLOBALS['setup_info']['emailadmin']['currentver'] = '1.9.015'; +} + +function emailadmin_upgrade1_9_015() +{ + $GLOBALS['egw_setup']->oProc->AddColumn('egw_ea_accounts','acc_folder_junk',array( + 'type' => 'varchar', + 'precision' => '128', + 'comment' => 'junk folder' + )); + $GLOBALS['egw_setup']->oProc->AddColumn('egw_ea_accounts','acc_imap_default_quota',array( + 'type' => 'int', + 'precision' => '4', + 'comment' => 'default quota, if no user specific one set' + )); + $GLOBALS['egw_setup']->oProc->AddColumn('egw_ea_accounts','acc_imap_timeout',array( + 'type' => 'int', + 'precision' => '2', + 'comment' => 'timeout for imap connection' + )); + + return $GLOBALS['setup_info']['emailadmin']['currentver'] = '1.9.016'; +} + + +function emailadmin_upgrade1_9_016() +{ + $GLOBALS['egw_setup']->oProc->AddColumn('egw_ea_identities','ident_name',array( + 'type' => 'varchar', + 'precision' => '128', + 'comment' => 'name of identity to display' + )); + + return $GLOBALS['setup_info']['emailadmin']['currentver'] = '1.9.017'; +} + + +function emailadmin_upgrade1_9_017() +{ + $GLOBALS['egw_setup']->oProc->CreateTable('egw_ea_notifications',array( + 'fd' => array( + 'notif_id' => array('type' => 'auto','nullable' => False), + 'acc_id' => array('type' => 'int','precision' => '4','nullable' => False,'comment' => 'mail account'), + 'account_id' => array('type' => 'int','meta' => 'user','precision' => '4','nullable' => False,'comment' => 'user account'), + 'notif_folder' => array('type' => 'varchar','precision' => '255','nullable' => False,'comment' => 'folder name') + ), + 'pk' => array('notif_id'), + 'fk' => array(), + 'ix' => array(array('account_id','acc_id')), + 'uc' => array() + )); + + return $GLOBALS['setup_info']['emailadmin']['currentver'] = '1.9.018'; +} + + +function emailadmin_upgrade1_9_018() +{ + $GLOBALS['egw_setup']->oProc->AlterColumn('egw_ea_accounts','acc_smtp_type',array( + 'type' => 'varchar', + 'precision' => '32', + 'default' => 'emailadmin_smtp', + 'comment' => 'smtp class to use' + )); + $GLOBALS['egw_setup']->oProc->AlterColumn('egw_ea_accounts','acc_imap_type',array( + 'type' => 'varchar', + 'precision' => '32', + 'default' => 'emailadmin_imap', + 'comment' => 'imap class to use' + )); + + return $GLOBALS['setup_info']['emailadmin']['currentver'] = '1.9.019'; +} + + +function emailadmin_upgrade1_9_019() +{ + return $GLOBALS['setup_info']['emailadmin']['currentver'] = '14.1'; +} diff --git a/emailadmin/templates/default/account.xet b/emailadmin/templates/default/account.xet new file mode 100644 index 0000000000..4cf485cffb --- /dev/null +++ b/emailadmin/templates/default/account.xet @@ -0,0 +1,336 @@ + + + + + + + + + +