summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordavehauenstein <davehauenstein@d4e419ec-0920-11de-bbfd-a7c1bc4c261e>2009-04-15 22:06:42 +0000
committerdavehauenstein <davehauenstein@d4e419ec-0920-11de-bbfd-a7c1bc4c261e>2009-04-15 22:06:42 +0000
commitc53d8d026021f97075fb2f4940ba22793c38fb6e (patch)
treec5d7865bc0fd226ecc7525d7de8a1d0b0c5c89fd
parenta414fcee8e7dc733443c6a971162d0dd9d0e723f (diff)
downloadreadability-simple-c53d8d026021f97075fb2f4940ba22793c38fb6e.tar.bz2
readability-simple-c53d8d026021f97075fb2f4940ba22793c38fb6e.zip
added toolbar; functionality includes refresh button to get back to original page, print article, email a link to the article with a personal note
git-svn-id: http://arc90labs-readability.googlecode.com/svn/trunk@31 d4e419ec-0920-11de-bbfd-a7c1bc4c261e
-rw-r--r--close.html10
-rwxr-xr-xcss/readability-print.css4
-rwxr-xr-xcss/readability.css75
-rw-r--r--email.php427
-rw-r--r--images/email-head.gifbin0 -> 884 bytes
-rw-r--r--images/email-readability.gifbin0 -> 561 bytes
-rw-r--r--images/read-email.pngbin0 -> 734 bytes
-rw-r--r--images/read-print.pngbin0 -> 741 bytes
-rw-r--r--images/read-refresh.pngbin0 -> 874 bytes
-rw-r--r--images/twitter.pngbin0 -> 510 bytes
-rwxr-xr-xjs/readability.js56
-rw-r--r--lib/Zend/Exception.php30
-rw-r--r--lib/Zend/Filter.php115
-rw-r--r--lib/Zend/Filter/Alnum.php115
-rw-r--r--lib/Zend/Filter/Alpha.php115
-rw-r--r--lib/Zend/Filter/BaseName.php50
-rw-r--r--lib/Zend/Filter/Digits.php82
-rw-r--r--lib/Zend/Filter/Dir.php50
-rw-r--r--lib/Zend/Filter/Exception.php37
-rw-r--r--lib/Zend/Filter/File/LowerCase.php84
-rw-r--r--lib/Zend/Filter/File/Rename.php282
-rw-r--r--lib/Zend/Filter/File/UpperCase.php84
-rw-r--r--lib/Zend/Filter/HtmlEntities.php122
-rw-r--r--lib/Zend/Filter/Inflector.php497
-rw-r--r--lib/Zend/Filter/Input.php926
-rw-r--r--lib/Zend/Filter/Int.php50
-rw-r--r--lib/Zend/Filter/Interface.php40
-rw-r--r--lib/Zend/Filter/PregReplace.php156
-rw-r--r--lib/Zend/Filter/RealPath.php50
-rw-r--r--lib/Zend/Filter/StringToLower.php76
-rw-r--r--lib/Zend/Filter/StringToUpper.php76
-rw-r--r--lib/Zend/Filter/StringTrim.php97
-rw-r--r--lib/Zend/Filter/StripNewlines.php48
-rw-r--r--lib/Zend/Filter/StripTags.php294
-rw-r--r--lib/Zend/Filter/Word/CamelCaseToDash.php44
-rw-r--r--lib/Zend/Filter/Word/CamelCaseToSeparator.php49
-rw-r--r--lib/Zend/Filter/Word/CamelCaseToUnderscore.php44
-rw-r--r--lib/Zend/Filter/Word/DashToCamelCase.php44
-rw-r--r--lib/Zend/Filter/Word/DashToSeparator.php42
-rw-r--r--lib/Zend/Filter/Word/DashToUnderscore.php45
-rw-r--r--lib/Zend/Filter/Word/Separator/Abstract.php76
-rw-r--r--lib/Zend/Filter/Word/SeparatorToCamelCase.php52
-rw-r--r--lib/Zend/Filter/Word/SeparatorToDash.php46
-rw-r--r--lib/Zend/Filter/Word/SeparatorToSeparator.php129
-rw-r--r--lib/Zend/Filter/Word/UnderscoreToCamelCase.php44
-rw-r--r--lib/Zend/Filter/Word/UnderscoreToDash.php45
-rw-r--r--lib/Zend/Filter/Word/UnderscoreToSeparator.php45
-rw-r--r--lib/Zend/Loader.php263
-rw-r--r--lib/Zend/Loader/Exception.php35
-rw-r--r--lib/Zend/Loader/PluginLoader.php464
-rw-r--r--lib/Zend/Loader/PluginLoader/Exception.php38
-rw-r--r--lib/Zend/Loader/PluginLoader/Interface.php74
-rw-r--r--lib/Zend/Mail.php1042
-rw-r--r--lib/Zend/Mail/Exception.php37
-rw-r--r--lib/Zend/Mail/Message.php112
-rw-r--r--lib/Zend/Mail/Message/File.php96
-rw-r--r--lib/Zend/Mail/Message/Interface.php55
-rw-r--r--lib/Zend/Mail/Part.php489
-rw-r--r--lib/Zend/Mail/Part/File.php198
-rw-r--r--lib/Zend/Mail/Part/Interface.php136
-rw-r--r--lib/Zend/Mail/Protocol/Abstract.php385
-rw-r--r--lib/Zend/Mail/Protocol/Exception.php39
-rw-r--r--lib/Zend/Mail/Protocol/Imap.php837
-rw-r--r--lib/Zend/Mail/Protocol/Pop3.php471
-rw-r--r--lib/Zend/Mail/Protocol/Smtp.php443
-rw-r--r--lib/Zend/Mail/Protocol/Smtp/Auth/Crammd5.php108
-rw-r--r--lib/Zend/Mail/Protocol/Smtp/Auth/Login.php98
-rw-r--r--lib/Zend/Mail/Protocol/Smtp/Auth/Plain.php96
-rw-r--r--lib/Zend/Mail/Storage.php39
-rw-r--r--lib/Zend/Mail/Storage/Abstract.php366
-rw-r--r--lib/Zend/Mail/Storage/Exception.php39
-rw-r--r--lib/Zend/Mail/Storage/Folder.php236
-rw-r--r--lib/Zend/Mail/Storage/Folder/Interface.php60
-rw-r--r--lib/Zend/Mail/Storage/Folder/Maildir.php265
-rw-r--r--lib/Zend/Mail/Storage/Folder/Mbox.php264
-rw-r--r--lib/Zend/Mail/Storage/Imap.php644
-rw-r--r--lib/Zend/Mail/Storage/Maildir.php475
-rw-r--r--lib/Zend/Mail/Storage/Mbox.php447
-rw-r--r--lib/Zend/Mail/Storage/Pop3.php328
-rw-r--r--lib/Zend/Mail/Storage/Writable/Interface.php108
-rw-r--r--lib/Zend/Mail/Storage/Writable/Maildir.php1049
-rw-r--r--lib/Zend/Mail/Transport/Abstract.php350
-rw-r--r--lib/Zend/Mail/Transport/Exception.php39
-rw-r--r--lib/Zend/Mail/Transport/Sendmail.php170
-rw-r--r--lib/Zend/Mail/Transport/Smtp.php241
-rw-r--r--lib/Zend/Mime.php365
-rw-r--r--lib/Zend/Mime/Decode.php243
-rw-r--r--lib/Zend/Mime/Exception.php36
-rw-r--r--lib/Zend/Mime/Message.php285
-rw-r--r--lib/Zend/Mime/Part.php216
-rw-r--r--lib/Zend/Registry.php207
-rw-r--r--lib/Zend/Validate.php171
-rw-r--r--lib/Zend/Validate/Abstract.php358
-rw-r--r--lib/Zend/Validate/Alnum.php120
-rw-r--r--lib/Zend/Validate/Alpha.php120
-rw-r--r--lib/Zend/Validate/Barcode.php99
-rw-r--r--lib/Zend/Validate/Barcode/Ean13.php112
-rw-r--r--lib/Zend/Validate/Barcode/UpcA.php100
-rw-r--r--lib/Zend/Validate/Between.php200
-rw-r--r--lib/Zend/Validate/Ccnum.php111
-rw-r--r--lib/Zend/Validate/Date.php235
-rw-r--r--lib/Zend/Validate/Digits.php100
-rw-r--r--lib/Zend/Validate/EmailAddress.php247
-rw-r--r--lib/Zend/Validate/Exception.php37
-rw-r--r--lib/Zend/Validate/File/Count.php275
-rw-r--r--lib/Zend/Validate/File/Crc32.php179
-rw-r--r--lib/Zend/Validate/File/ExcludeExtension.php94
-rw-r--r--lib/Zend/Validate/File/ExcludeMimeType.php91
-rw-r--r--lib/Zend/Validate/File/Exists.php203
-rw-r--r--lib/Zend/Validate/File/Extension.php234
-rw-r--r--lib/Zend/Validate/File/FilesSize.php163
-rw-r--r--lib/Zend/Validate/File/Hash.php195
-rw-r--r--lib/Zend/Validate/File/ImageSize.php370
-rw-r--r--lib/Zend/Validate/File/IsCompressed.php133
-rw-r--r--lib/Zend/Validate/File/IsImage.php137
-rw-r--r--lib/Zend/Validate/File/Md5.php183
-rw-r--r--lib/Zend/Validate/File/MimeType.php283
-rw-r--r--lib/Zend/Validate/File/NotExists.php84
-rw-r--r--lib/Zend/Validate/File/Sha1.php181
-rw-r--r--lib/Zend/Validate/File/Size.php404
-rw-r--r--lib/Zend/Validate/File/Upload.php234
-rw-r--r--lib/Zend/Validate/Float.php75
-rw-r--r--lib/Zend/Validate/GreaterThan.php114
-rw-r--r--lib/Zend/Validate/Hex.php74
-rw-r--r--lib/Zend/Validate/Hostname.php444
-rw-r--r--lib/Zend/Validate/Hostname/At.php50
-rw-r--r--lib/Zend/Validate/Hostname/Ch.php50
-rw-r--r--lib/Zend/Validate/Hostname/De.php58
-rw-r--r--lib/Zend/Validate/Hostname/Fi.php50
-rw-r--r--lib/Zend/Validate/Hostname/Hu.php50
-rw-r--r--lib/Zend/Validate/Hostname/Interface.php52
-rw-r--r--lib/Zend/Validate/Hostname/Li.php50
-rw-r--r--lib/Zend/Validate/Hostname/No.php52
-rw-r--r--lib/Zend/Validate/Hostname/Se.php50
-rw-r--r--lib/Zend/Validate/Identical.php117
-rw-r--r--lib/Zend/Validate/InArray.php138
-rw-r--r--lib/Zend/Validate/Int.php73
-rw-r--r--lib/Zend/Validate/Interface.php71
-rw-r--r--lib/Zend/Validate/Ip.php71
-rw-r--r--lib/Zend/Validate/LessThan.php113
-rw-r--r--lib/Zend/Validate/NotEmpty.php70
-rw-r--r--lib/Zend/Validate/Regex.php125
-rw-r--r--lib/Zend/Validate/StringLength.php223
-rw-r--r--log.txt0
144 files changed, 24331 insertions, 33 deletions
diff --git a/close.html b/close.html
new file mode 100644
index 0000000..dca0447
--- /dev/null
+++ b/close.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <title>Closing</title>
+ </head>
+ <body onLoad="document.forms['close'].submit();">
+ <form name="close" action="./close.html" method="get" /></form>
+ </body>
+</html> \ No newline at end of file
diff --git a/css/readability-print.css b/css/readability-print.css
index 83aa757..a4c8cb3 100755
--- a/css/readability-print.css
+++ b/css/readability-print.css
@@ -1,5 +1,7 @@
-
#readInner {
width: 100% !important;
font-size: 12pt;
+}
+#readTools {
+ display: none;
} \ No newline at end of file
diff --git a/css/readability.css b/css/readability.css
index 15292d0..f7e8f41 100755
--- a/css/readability.css
+++ b/css/readability.css
@@ -3,38 +3,31 @@
/*
#readOverlay {
- background-image: none;
-
-
- background: #eee;
+ background-image: none;
+ background: #eee;
font-family:Georgia,"Century Schoolbook","Times New Roman",Times,serif;
}
*/
-
#readOverlay {
display: block;
position: absolute;
top: 0;
left: 0;
- z-index: 250000;
width: 100%;
}
-
#readInner {
text-align: left;
- line-height: 1.4em;
+ line-height: 1.4em;
margin: 1em auto;
max-width: 800px;
-
}
#readInner a {
color: blue;
- text-decoration: underline;
+ text-decoration: underline;
}
-
#readInner * {
- margin-bottom: 16px;
+ margin-bottom: 16px;
border: none;
background: none;
}
@@ -43,20 +36,19 @@
margin-right: 12px;
margin-bottom: 12px;
}
-
#readInner h1 {
display: block;
width: 100%;
border-bottom: 1px solid #333;
font-size: 1.2em;
}
-#readInner blockquote {
+#readInner blockquote {
margin-left: 3em;
margin-right: 3em;
}
#readFooter {
display: block;
- border-top: 1px solid #333;
+ border-top: 1px solid #333;
text-align: center;
}
div.footer-right {
@@ -67,17 +59,36 @@ div.footer-right {
margin-top: 18px
}
span.version {
- display: none;
+ display: none;
}
+/* Article Tools */
+#readTools {
+ width: 34px;
+ height: 150px;
+ position: fixed;
+ z-index: 100;
+ top: 10px;
+ left: 10px;
+}
+#readTools a {
+ margin-bottom: 8px;
+ display: block;
+}
+#readTools a img {
+ border: none;
+}
/* ---------------- USER-CONFIGURABLE STYLING --------------------- */
+
/* ---------------- USER-CONFIGURABLE STYLING --------------------- */
+
/* Size options */
+
.size-small {
font-size: 12px;
}
.size-medium {
- font-size: 18px;
+ font-size: 18px;
}
.size-large {
font-size: 26px;
@@ -85,14 +96,13 @@ span.version {
.size-x-large {
font-size: 34px;
}
-
/* Style options */
+
.style-novel {
font-family:"Palatino Linotype", "Book Antiqua", Palatino, serif;
background: #F4F3DB;
color: #222;
}
-
.style-ebook {
font-family:Arial, Helvetica, sans-serif;
background: #eee;
@@ -102,24 +112,22 @@ span.version {
font-family: "Arial Black", Gadget, sans-serif;
font-weight: normal;
}
-
.style-newspaper {
font-family:"Times New Roman", Times, serif;
- background: #F7F7F7;
+ background: #FFF;
color: #222;
}
.style-newspaper h1 {
text-transform:capitalize;
font-family: Georgia, "Times New Roman", Times, serif;
}
-
.style-terminal {
font-family: "Lucida Console", Monaco, monospace;
background: #1D4E2C;
color: #C6FFC6;
}
-
/* Margin Options */
+
.margin-x-wide {
width: 35%;
}
@@ -135,6 +143,7 @@ span.version {
/* ---------------- USER-CONFIGURABLE STYLING --------------------- */
/* ------ DEBUG ------- */
+
.bug-green {
background: #BBF9B0;
border: 4px solid green;
@@ -143,8 +152,24 @@ span.version {
background: red;
}
.bug-yellow {
- background: #FFFF8E;
+ background: #FFFF8E;
}
.bug-blue {
- background: #BFDFFF;
+ background: #BFDFFF;
}
+
+/* ---------------- EMAIL POP UP --------------------- */
+
+#email-container{
+ position: fixed;
+ top: 60px;
+ left: 50%;
+ margin: 0 0 0 -240px;
+ padding: 0;
+ width: 480px;
+ height: 460px;
+ border: solid 3px #666;
+ background-color: #fff;
+ z-index: 100 !important;
+ overflow: hidden;
+} \ No newline at end of file
diff --git a/email.php b/email.php
new file mode 100644
index 0000000..a83b97a
--- /dev/null
+++ b/email.php
@@ -0,0 +1,427 @@
+<?php
+ set_include_path( get_include_path() . PATH_SEPARATOR . 'lib');
+ session_start();
+
+ require_once 'Zend/Filter.php';
+ require_once 'Zend/Filter/StripTags.php';
+ require_once 'Zend/Filter/StringTrim.php';
+
+ $filters = new Zend_Filter();
+ $filters->addFilter(new Zend_Filter_StripTags())
+ ->addFilter(new Zend_Filter_StringTrim());
+
+ $pageTitle = array_key_exists('pageTitle', $_GET) ? $filters->filter($_GET['pageTitle']) : '';
+ $pageUrl = array_key_exists('pageUrl', $_GET) ? $filters->filter($_GET['pageUrl']) : '';
+ $page = 'form';
+ $errors = array();
+
+ if('post' == strtolower($_SERVER['REQUEST_METHOD']))
+ {
+ // someone sent over an invalid
+ if(!Readability::hasValidParams())
+ {
+ Readability::logMessage("ERROR:Someone tried to send a request with an invalid set of parameters.");
+ die();
+ }
+
+ require_once 'Zend/Validate/EmailAddress.php';
+
+ //FILTER DATA
+
+ $from = $filters->filter($_POST['from']);
+ $to = $filters->filter($_POST['to']);
+ $to = array_map('trim', split(',', $to));
+ $note = $filters->filter($_POST['note']);
+ $key = $filters->filter($_POST['key']);
+
+ if(!Readability::validateSecureKey($key))
+ {
+ $errors[] = 'key';
+ Readability::logMessage("ERROR:Someone tried to send an email with an invalid key.");
+ }
+
+ // VALIDATE DATA
+
+ $emailValidator = new Zend_Validate_EmailAddress();
+
+ if(!$emailValidator->isValid($_POST['from']))
+ {
+ $errors[] = 'from';
+ }
+
+ if(count($to) == 0)
+ {
+ $errors[] = 'to';
+ }
+ else
+ {
+ foreach($to as $toAddress)
+ {
+ if(!$emailValidator->isValid($toAddress))
+ {
+ $errors[] = 'to';
+ break;
+ }
+ }
+ }
+
+ // NO ERRORS SEND EMAIL
+ if(count($errors) == 0)
+ {
+ // store the from address so it's saved for future use
+ setcookie("from", $from, time()+3600*24*7*4, "/");
+
+ require_once 'Zend/Mail.php';
+ require_once 'Zend/Mail/Transport/Smtp.php';
+
+ $mailer = new Zend_Mail_Transport_Smtp('smtp.googlemail.com', Array(
+ 'auth' => 'login',
+ 'username' => 'readability@arc90.com',
+ 'password' => 'arc90inc',
+ 'ssl' => 'ssl',
+ 'port' => 465,
+ ));
+ $mailer->EOL = "\r\n"; // gmail is fussy about this
+ Zend_Mail::setDefaultTransport($mailer);
+
+ $body = '<html><head>';
+ $body = '</head>';
+ $body = '<body>';
+ $body .= '<div style="font-size: 15px;">';
+ $body .= '<p>This page was sent to you by: '.$from.'</p>';
+ $body .= '<p>Message from sender: </p><p><blockquote>'.stripslashes($note).'</blockquote></p>';
+ $body .= '<p>Just click this link: <a href="'.$pageUrl.'">'.$pageTitle.'</a></p>';
+ $body .= '<hr />';
+ $body .= '<p style="font-size: 90%;">Sent from <a href="http://lab.arc90.com/experiments/readability/">Readability</a> | An <a href="http://www.arc90.com">Arc90</a> lab experiment<p>';
+ $body .= '</div>';
+ $body .= '</body></html>';
+
+ $mail = new Zend_Mail();
+ $mail->setBodyHtml($body);
+ $mail->setFrom($from);
+
+ foreach($to as $toAddress)
+ {
+ $mail->addTo($toAddress);
+ }
+
+ $mail->setSubject("Arc90 Readability: {$pageTitle}");
+
+ try
+ {
+ if(!$mail->send())
+ {
+ Readability::logMessage("ERROR:There was an error sending the email. [to:".implode(', ', $to).", from:{$from}, notes:{$note}, pageUrl: {$pageUrl}, pageTitle: {$pageTitle}]");
+ }
+ else
+ {
+ $page = 'complete';
+ }
+ }
+ catch(Exception $e)
+ {
+ Readability::logMessage("ERROR:There was an exception sending the email. [to:".implode(', ', $to).", from:{$from}, notes:{$note}, pageUrl: {$pageUrl}, pageTitle: {$pageTitle}]");
+ Readability::logMessage("ERROR:".$e->getMessage());
+ }
+
+ //header('location: close.html');
+ }
+ } // end of: if method == POST
+
+ elseif('get' == strtolower($_SERVER['REQUEST_METHOD']))
+ {
+ $_SESSION['secureKey'] = Readability::generateSecureKey();
+ }
+
+ class Readability
+ {
+ public static function isError($field, $errors)
+ {
+ if(in_array($field, $errors))
+ {
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ public static function getErrorClass($field, $errors)
+ {
+ if(in_array($field, $errors))
+ {
+ return 'class = "error"';
+ }
+ return '';
+ }
+
+ public static function getParam($param)
+ {
+ if(isset($_POST) && array_key_exists($param, $_POST))
+ {
+ return $_POST[$param];
+ }
+ elseif(isset($_COOKIE) && array_key_exists($param, $_COOKIE))
+ {
+ return $_COOKIE[$param];
+ }
+ return '';
+ }
+
+ public static function logMessage($message)
+ {
+ $logFile = dirname(__FILE__) . '/log.txt';
+
+ $handle = @fopen($logFile, 'a');
+ if(is_resource($handle))
+ {
+ $message = date('Y-m-d G:i:s') . ' :: ' . $message . "\n";
+ fwrite($handle, $message);
+ fclose($handle);
+ }
+ }
+
+ public static function generateSecureKey($length = 8)
+ {
+ $sucureKey = "";
+ $possible = "012*3456)789b(cdfg#hjkmn@pqrs!tvwx[yz";
+
+ for($x=0; $x < $length; $x++)
+ {
+ $char = substr($possible, mt_rand(0, strlen($possible)-1), 1);
+
+ if (!strstr($sucureKey, $char))
+ {
+ $sucureKey .= $char;
+ }
+ }
+ return $sucureKey;
+ }
+
+ /**
+ * this adds a small (very small) level of security
+ *
+ * @param string $testKey
+ * @return void
+ * @author David Hauenstein
+ */
+ public static function validateSecureKey($testKey)
+ {
+ if(!array_key_exists('secureKey', $_SESSION))
+ {
+ $_SESSION['secureKey'] = self::generateSecureKey();
+ return false;
+ }
+ else
+ {
+ if($testKey != $_SESSION['secureKey'])
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static function emailAsLinks($addresses)
+ {
+ $toReturn = '';
+ foreach($addresses as $address)
+ {
+ $toReturn .= '<a href="mailto:'.$address.'">' . $address . '</a>, ';
+ }
+ return substr($toReturn, 0, strlen($toReturn)-2);
+ }
+
+ public static function hasValidParams()
+ {
+ $requiredParams = array('from', 'to', 'note', 'key');
+ $sentParams = array_keys($_POST);
+ foreach($requiredParams as $required)
+ {
+ if(!in_array($required, $sentParams))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+?>
+<?= '<?' ?>xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+ <head>
+ <title>Readability</title>
+ <script type="text/javascript" charset="utf-8">
+ window.onload = function(){
+ document.getElementById('cancel-email').onclick = function(){
+ window.location = 'http://davehauenstein.com/readability/close.html';
+ return false;
+ };
+ document.getElementById('send-email').onclick = function(){
+ document.getElementById('send-email-form').submit();
+ return false;
+ };
+ };
+ <?php if($page == "complete"){ ?>
+ timer = setTimeout(function(){
+ window.location = 'close.html';
+ }, 3000);
+ <?php } ?>
+ </script>
+ <style type="text/css" media="screen">
+ *{
+ margin: 0;
+ }
+ #email-container{
+ font-size: 14px;
+ margin: 0;
+ padding: 0;
+ width: 480px;
+ height: 450px;
+ font-family: times, serif;
+ background-color: #fff;
+ }
+ h2{
+ margin: 0 0 10px;
+ background: url(http://lab.arc90.com/experiments/readability/images/email-head.gif) #e2e3e4 no-repeat 15px center;
+ text-indent: -99999px;
+ height: 40px;
+ }
+ form{
+ padding-right: 20px;
+ }
+ label{
+ font-size: 20px;
+ padding-right: 10px;
+ display: block;
+ float: left;
+ width: 100px;
+ text-align: right;
+ }
+ input,
+ textarea{
+ padding: 5px;
+ width: 330px;
+ font-family: times, serif;
+ font-size: 14px;
+ border: solid 1px #999;
+ }
+ input.error{
+ border: solid 2px #c33;
+ }
+ p.error{
+ color: #c33;
+ font-size: 14px;
+ }
+ .helper{
+ font-size: 12px;
+ margin-top: 3px;
+ color: #666;
+ }
+ .details{
+ font-style: italic;
+ font-size: 15px;
+ }
+ .helper,
+ .details{
+ margin-left: 110px; /* add label width + label padding-right */
+ }
+ .section{
+ margin-top: 15px;
+ clear: both;
+ }
+ #note{
+ <?php if(count($errors) > 0){ ?>
+ height: 100px;
+ <?php } else { ?>
+ height: 140px;
+ <?php } ?>
+ }
+ #send-email,
+ #cancel-email{
+ padding: 2px 2px;
+ font-family: times, serif;
+ background-color: #e7e8e9;
+ font-size: 17px;
+ border: solid 2px #666;
+ cursor: pointer;
+ }
+ #send-email{
+ margin-left: 180px;
+ font-weight: bold;
+ }
+ #cancel-email{
+ margin-left: 10px;
+ }
+ .logo{
+ position: absolute;
+ left: 10px;
+ bottom: 10px;
+ }
+ #complete{
+ padding: 20px;
+ }
+ #complete p{
+ margin: 0 0 10px;
+ font-size: 16px;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="email-container">
+ <h2>Email Page</h2>
+
+ <?php if($page == 'form'){ ?>
+ <form action="" method="post" accept-charset="utf-8" id="send-email-form">
+ <div class="section">
+ <label for="from">From :</label>
+ <input type="text" name="from" id="from" value="<?php echo Readability::getParam('from') ?>" <?php echo Readability::getErrorClass('from', $errors); ?> />
+ <?php if(Readability::isError('from', $errors)){ ?>
+ <p class="helper error">
+ This field should be a valid email address.
+ </p>
+ <?php } ?>
+ </div>
+ <div class="section">
+ <label for="to">To :</label>
+ <input type="text" name="to" id="to" value="<?php echo Readability::getParam('to') ?>" <?php echo Readability::getErrorClass('to', $errors); ?> />
+ <?php if(Readability::isError('to', $errors)){ ?>
+ <p class="helper error">
+ Please ensure that all addresses are valid email adderesses.
+ </p>
+ <?php } ?>
+ <p class="helper">
+ Seperate multiple recipients with commas.
+ </p>
+ </div>
+ <div class="section">
+ <label>Sending :</label>
+ <p class="details">
+ <?= $pageTitle ?>
+ </p>
+ </div>
+ <div class="section">
+ <label for="note">Note :</label>
+ <textarea name="note" id="note" rows="8" cols="40"><?php echo Readability::getParam('note') ?></textarea>
+ </div>
+ <div class="section">
+ <button id="send-email">Email Page</button>
+ <button id="cancel-email">Cancel</button>
+ </div>
+ <img src="http://lab.arc90.com/experiments/readability/images/email-readability.gif" alt="Readability" class="logo" />
+ <input type="hidden" name="pageUrl" value="<?= $pageUrl; ?>" id="pageUrl" />
+ <input type="hidden" name="pageTitle" value="<?= $pageTitle; ?>" id="pageTitle" />
+ <input type="hidden" name="key" value="<?= $_SESSION['secureKey']; ?>" id="key" />
+ </form>
+ <?php }else if($page == "complete"){ ?>
+ <div id="complete">
+ <p>
+ Thanks for using Readability!
+ </p>
+ <p>
+ A link to this page has been sent to <?php echo Readability::emailAsLinks($to) ?>
+ </p>
+ </div>
+ <?php } ?>
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/images/email-head.gif b/images/email-head.gif
new file mode 100644
index 0000000..1f08afc
--- /dev/null
+++ b/images/email-head.gif
Binary files differ
diff --git a/images/email-readability.gif b/images/email-readability.gif
new file mode 100644
index 0000000..f8465ab
--- /dev/null
+++ b/images/email-readability.gif
Binary files differ
diff --git a/images/read-email.png b/images/read-email.png
new file mode 100644
index 0000000..0bc865b
--- /dev/null
+++ b/images/read-email.png
Binary files differ
diff --git a/images/read-print.png b/images/read-print.png
new file mode 100644
index 0000000..8790be3
--- /dev/null
+++ b/images/read-print.png
Binary files differ
diff --git a/images/read-refresh.png b/images/read-refresh.png
new file mode 100644
index 0000000..320d3f5
--- /dev/null
+++ b/images/read-refresh.png
Binary files differ
diff --git a/images/twitter.png b/images/twitter.png
new file mode 100644
index 0000000..06a8485
--- /dev/null
+++ b/images/twitter.png
Binary files differ
diff --git a/js/readability.js b/js/readability.js
index 934ffb8..97cd2bb 100755
--- a/js/readability.js
+++ b/js/readability.js
@@ -1,9 +1,12 @@
-var readabilityVersion = "0.3";
+var readabilityVersion = "0.4";
+var emailSrc = 'http://lab.arc90.com/experiments/readability/email.php';
+var iframeLoads = 0;
(function(){
var objOverlay = document.createElement("div");
var objinnerDiv = document.createElement("div");
-
+ var articleTools = document.createElement("DIV");
+
objOverlay.id = "readOverlay";
objinnerDiv.id = "readInner";
@@ -12,7 +15,20 @@ var readabilityVersion = "0.3";
objOverlay.className = readStyle;
objinnerDiv.className = readMargin + " " + readSize;
+ // Set up tools widget
+
+ // NOTE THE IMAGE URL'S HERE !!!!!!!!!!!!!!!!!
+ // NOTE THE IMAGE URL'S HERE !!!!!!!!!!!!!!!!!
+ // NOTE THE IMAGE URL'S HERE !!!!!!!!!!!!!!!!!
+ articleTools.id = "readTools";
+ articleTools.innerHTML = "\
+ <a href='#' onclick='return window.location.reload()'><img src='http://lab.arc90.com/experiments/readability/images/read-refresh.png' title='Reload original page'></a>\
+ <a href='#' onclick='javascript:window.print();'><img src='http://lab.arc90.com/experiments/readability/images/read-print.png' title='Print page'></a>\
+ <a href='#' onclick='emailBox(); return false;'><img src='http://lab.arc90.com/experiments/readability/images/read-email.png' title='Email page'></a>\
+ ";
+
objinnerDiv.appendChild(grabArticle()); // Get the article and place it inside the inner Div
+ objOverlay.appendChild(articleTools);
objOverlay.appendChild(objinnerDiv); // Insert the inner div into the overlay
// For totally hosed HTML, add body node that can't be found because of bad HTML or something.
@@ -113,25 +129,24 @@ function grabArticle() {
topDiv = clean(topDiv, "h2");
topDiv = clean(topDiv, "iframe");
+
// Add the footer and contents:
articleFooter.id = "readFooter";
articleFooter.innerHTML = "\
<a href='http://www.arc90.com'><img src='http://lab.arc90.com/experiments/readability/images/footer.png'></a>\
<div class='footer-right' >\
- <a href='#' onclick='return window.location.reload()'>Reload Page &raquo;</a>\
<span class='version'>Readability version " + readabilityVersion + "</span>\
</div>\
";
-
+
articleContent.appendChild(topDiv);
articleContent.appendChild(articleFooter);
-
+
return articleContent;
}
// Get the inner text of a node - cross browser compatibly.
-function getInnerText(e)
-{
+function getInnerText(e) {
if (navigator.appName == "Microsoft Internet Explorer")
return e.innerText;
else
@@ -210,3 +225,30 @@ function clean(e, tags, minWords) {
return e;
}
+function emailBox() {
+ var emailContainer = document.getElementById('email-container');
+ if(null != emailContainer)
+ {
+ return;
+ }
+
+ var emailContainer = document.createElement('div');
+ emailContainer.setAttribute('id', 'email-container');
+ emailContainer.innerHTML = '<iframe src="'+emailSrc + '?pageUrl='+escape(window.location)+'&pageTitle='+escape(document.title)+'" scrolling="no" onload="removeFrame()" style="width:480px; height: 460px; border: 0;"></iframe>';
+
+ document.body.appendChild(emailContainer);
+}
+
+function removeFrame()
+{
+ ++iframeLoads;
+ if(iframeLoads >= 6)
+ {
+ var emailContainer = document.getElementById('email-container');
+ if(null != emailContainer) {
+ emailContainer.parentNode.removeChild(emailContainer);
+ }
+ // reset the count
+ iframeLoads = 0;
+ }
+} \ No newline at end of file
diff --git a/lib/Zend/Exception.php b/lib/Zend/Exception.php
new file mode 100644
index 0000000..599d8a0
--- /dev/null
+++ b/lib/Zend/Exception.php
@@ -0,0 +1,30 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+
+/**
+ * @category Zend
+ * @package Zend
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Exception extends Exception
+{}
+
diff --git a/lib/Zend/Filter.php b/lib/Zend/Filter.php
new file mode 100644
index 0000000..23bec21
--- /dev/null
+++ b/lib/Zend/Filter.php
@@ -0,0 +1,115 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Filter.php 8434 2008-02-27 19:15:13Z darby $
+ */
+
+
+/**
+ * @see Zend_Filter_Interface
+ */
+require_once 'Zend/Filter/Interface.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Filter implements Zend_Filter_Interface
+{
+ /**
+ * Filter chain
+ *
+ * @var array
+ */
+ protected $_filters = array();
+
+ /**
+ * Adds a filter to the end of the chain
+ *
+ * @param Zend_Filter_Interface $filter
+ * @return Zend_Filter Provides a fluent interface
+ */
+ public function addFilter(Zend_Filter_Interface $filter)
+ {
+ $this->_filters[] = $filter;
+ return $this;
+ }
+
+ /**
+ * Returns $value filtered through each filter in the chain
+ *
+ * Filters are run in the order in which they were added to the chain (FIFO)
+ *
+ * @param mixed $value
+ * @return mixed
+ */
+ public function filter($value)
+ {
+ $valueFiltered = $value;
+ foreach ($this->_filters as $filter) {
+ $valueFiltered = $filter->filter($valueFiltered);
+ }
+ return $valueFiltered;
+ }
+
+ /**
+ * Returns a value filtered through a specified filter class, without requiring separate
+ * instantiation of the filter object.
+ *
+ * The first argument of this method is a data input value, that you would have filtered.
+ * The second argument is a string, which corresponds to the basename of the filter class,
+ * relative to the Zend_Filter namespace. This method automatically loads the class,
+ * creates an instance, and applies the filter() method to the data input. You can also pass
+ * an array of constructor arguments, if they are needed for the filter class.
+ *
+ * @param mixed $value
+ * @param string $classBaseName
+ * @param array $args OPTIONAL
+ * @param array|string $namespaces OPTIONAL
+ * @return mixed
+ * @throws Zend_Filter_Exception
+ */
+ public static function get($value, $classBaseName, array $args = array(), $namespaces = array())
+ {
+ require_once 'Zend/Loader.php';
+ $namespaces = array_merge(array('Zend_Filter'), (array) $namespaces);
+ foreach ($namespaces as $namespace) {
+ $className = $namespace . '_' . ucfirst($classBaseName);
+ try {
+ @Zend_Loader::loadClass($className);
+ } catch (Zend_Exception $ze) {
+ continue;
+ }
+ $class = new ReflectionClass($className);
+ if ($class->implementsInterface('Zend_Filter_Interface')) {
+ if ($class->hasMethod('__construct')) {
+ $object = $class->newInstanceArgs($args);
+ } else {
+ $object = $class->newInstance();
+ }
+ return $object->filter($value);
+ }
+ }
+ require_once 'Zend/Filter/Exception.php';
+ throw new Zend_Filter_Exception("Filter class not found from basename '$classBaseName'");
+ }
+}
diff --git a/lib/Zend/Filter/Alnum.php b/lib/Zend/Filter/Alnum.php
new file mode 100644
index 0000000..a50cddd
--- /dev/null
+++ b/lib/Zend/Filter/Alnum.php
@@ -0,0 +1,115 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Alnum.php 12751 2008-11-21 18:30:48Z yoshida@zend.co.jp $
+ */
+
+
+/**
+ * @see Zend_Filter_Interface
+ */
+require_once 'Zend/Filter/Interface.php';
+/**
+ * @see Zend_Locale
+ */
+require_once 'Zend/Locale.php';
+
+/**
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Filter_Alnum implements Zend_Filter_Interface
+{
+ /**
+ * Whether to allow white space characters; off by default
+ *
+ * @var boolean
+ */
+ public $allowWhiteSpace;
+
+ /**
+ * Is PCRE is compiled with UTF-8 and Unicode support
+ *
+ * @var mixed
+ **/
+ protected static $_unicodeEnabled;
+
+ /**
+ * Locale in browser.
+ *
+ * @var Zend_Locale object
+ */
+ protected $_locale;
+
+ /**
+ * The Alphabet means english alphabet.
+ *
+ * @var boolean
+ */
+ protected static $_meansEnglishAlphabet;
+
+ /**
+ * Sets default option values for this instance
+ *
+ * @param boolean $allowWhiteSpace
+ * @return void
+ */
+ public function __construct($allowWhiteSpace = false)
+ {
+ $this->allowWhiteSpace = (boolean) $allowWhiteSpace;
+ if (null === self::$_unicodeEnabled) {
+ self::$_unicodeEnabled = (@preg_match('/\pL/u', 'a')) ? true : false;
+ }
+
+ if (null === self::$_meansEnglishAlphabet) {
+ $this->_locale = new Zend_Locale('auto');
+ self::$_meansEnglishAlphabet = in_array($this->_locale->getLanguage(),
+ array('ja', 'ko', 'zh')
+ );
+ }
+
+ }
+
+ /**
+ * Defined by Zend_Filter_Interface
+ *
+ * Returns the string $value, removing all but alphabetic and digit characters
+ *
+ * @param string $value
+ * @return string
+ */
+ public function filter($value)
+ {
+ $whiteSpace = $this->allowWhiteSpace ? '\s' : '';
+ if (!self::$_unicodeEnabled) {
+ // POSIX named classes are not supported, use alternative a-zA-Z0-9 match
+ $pattern = '/[^a-zA-Z0-9' . $whiteSpace . ']/';
+ } else if (self::$_meansEnglishAlphabet) {
+ //The Alphabet means english alphabet.
+ $pattern = '/[^a-zA-Z0-9' . $whiteSpace . ']/u';
+ } else {
+ //The Alphabet means each language's alphabet.
+ $pattern = '/[^\p{L}\p{N}' . $whiteSpace . ']/u';
+ }
+
+ return preg_replace($pattern, '', (string) $value);
+ }
+}
diff --git a/lib/Zend/Filter/Alpha.php b/lib/Zend/Filter/Alpha.php
new file mode 100644
index 0000000..adfaafe
--- /dev/null
+++ b/lib/Zend/Filter/Alpha.php
@@ -0,0 +1,115 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Alpha.php 12751 2008-11-21 18:30:48Z yoshida@zend.co.jp $
+ */
+
+
+/**
+ * @see Zend_Filter_Interface
+ */
+require_once 'Zend/Filter/Interface.php';
+/**
+ * @see Zend_Locale
+ */
+require_once 'Zend/Locale.php';
+
+/**
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Filter_Alpha implements Zend_Filter_Interface
+{
+ /**
+ * Whether to allow white space characters; off by default
+ *
+ * @var boolean
+ */
+ public $allowWhiteSpace;
+
+ /**
+ * Is PCRE is compiled with UTF-8 and Unicode support
+ *
+ * @var mixed
+ **/
+ protected static $_unicodeEnabled;
+
+ /**
+ * Locale in browser.
+ *
+ * @var Zend_Locale object
+ */
+ protected $_locale;
+
+ /**
+ * The Alphabet means english alphabet.
+ *
+ * @var boolean
+ */
+ protected static $_meansEnglishAlphabet;
+
+ /**
+ * Sets default option values for this instance
+ *
+ * @param boolean $allowWhiteSpace
+ * @return void
+ */
+ public function __construct($allowWhiteSpace = false)
+ {
+ $this->allowWhiteSpace = (boolean) $allowWhiteSpace;
+ if (null === self::$_unicodeEnabled) {
+ self::$_unicodeEnabled = (@preg_match('/\pL/u', 'a')) ? true : false;
+ }
+
+ if (null === self::$_meansEnglishAlphabet) {
+ $this->_locale = new Zend_Locale('auto');
+ self::$_meansEnglishAlphabet = in_array($this->_locale->getLanguage(),
+ array('ja', 'ko', 'zh')
+ );
+ }
+
+ }
+
+ /**
+ * Defined by Zend_Filter_Interface
+ *
+ * Returns the string $value, removing all but alphabetic characters
+ *
+ * @param string $value
+ * @return string
+ */
+ public function filter($value)
+ {
+ $whiteSpace = $this->allowWhiteSpace ? '\s' : '';
+ if (!self::$_unicodeEnabled) {
+ // POSIX named classes are not supported, use alternative a-zA-Z match
+ $pattern = '/[^a-zA-Z' . $whiteSpace . ']/';
+ } else if (self::$_meansEnglishAlphabet) {
+ //The Alphabet means english alphabet.
+ $pattern = '/[^a-zA-Z' . $whiteSpace . ']/u';
+ } else {
+ //The Alphabet means each language's alphabet.
+ $pattern = '/[^\p{L}' . $whiteSpace . ']/u';
+ }
+
+ return preg_replace($pattern, '', (string) $value);
+ }
+}
diff --git a/lib/Zend/Filter/BaseName.php b/lib/Zend/Filter/BaseName.php
new file mode 100644
index 0000000..a0652ab
--- /dev/null
+++ b/lib/Zend/Filter/BaseName.php
@@ -0,0 +1,50 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: BaseName.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Filter_Interface
+ */
+require_once 'Zend/Filter/Interface.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Filter_BaseName implements Zend_Filter_Interface
+{
+ /**
+ * Defined by Zend_Filter_Interface
+ *
+ * Returns basename($value)
+ *
+ * @param string $value
+ * @return string
+ */
+ public function filter($value)
+ {
+ return basename((string) $value);
+ }
+}
diff --git a/lib/Zend/Filter/Digits.php b/lib/Zend/Filter/Digits.php
new file mode 100644
index 0000000..e062e1a
--- /dev/null
+++ b/lib/Zend/Filter/Digits.php
@@ -0,0 +1,82 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Digits.php 8731 2008-03-10 15:08:03Z darby $
+ */
+
+
+/**
+ * @see Zend_Filter_Interface
+ */
+require_once 'Zend/Filter/Interface.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Filter_Digits implements Zend_Filter_Interface
+{
+ /**
+ * Is PCRE is compiled with UTF-8 and Unicode support
+ *
+ * @var mixed
+ **/
+ protected static $_unicodeEnabled;
+
+ /**
+ * Class constructor
+ *
+ * Checks if PCRE is compiled with UTF-8 and Unicode support
+ *
+ * @return void
+ */
+ public function __construct()
+ {
+ if (null === self::$_unicodeEnabled) {
+ self::$_unicodeEnabled = (@preg_match('/\pL/u', 'a')) ? true : false;
+ }
+ }
+
+ /**
+ * Defined by Zend_Filter_Interface
+ *
+ * Returns the string $value, removing all but digit characters
+ *
+ * @param string $value
+ * @return string
+ */
+ public function filter($value)
+ {
+ if (!self::$_unicodeEnabled) {
+ // POSIX named classes are not supported, use alternative 0-9 match
+ $pattern = '/[^0-9]/';
+ } else if (extension_loaded('mbstring')) {
+ // Filter for the value with mbstring
+ $pattern = '/[^[:digit:]]/';
+ } else {
+ // Filter for the value without mbstring
+ $pattern = '/[\p{^N}]/';
+ }
+
+ return preg_replace($pattern, '', (string) $value);
+ }
+}
diff --git a/lib/Zend/Filter/Dir.php b/lib/Zend/Filter/Dir.php
new file mode 100644
index 0000000..a08534e
--- /dev/null
+++ b/lib/Zend/Filter/Dir.php
@@ -0,0 +1,50 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Dir.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Filter_Interface
+ */
+require_once 'Zend/Filter/Interface.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Filter_Dir implements Zend_Filter_Interface
+{
+ /**
+ * Defined by Zend_Filter_Interface
+ *
+ * Returns dirname($value)
+ *
+ * @param string $value
+ * @return string
+ */
+ public function filter($value)
+ {
+ return dirname((string) $value);
+ }
+}
diff --git a/lib/Zend/Filter/Exception.php b/lib/Zend/Filter/Exception.php
new file mode 100644
index 0000000..3142c81
--- /dev/null
+++ b/lib/Zend/Filter/Exception.php
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Exception.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Exception
+ */
+require_once 'Zend/Exception.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Filter_Exception extends Zend_Exception
+{}
diff --git a/lib/Zend/Filter/File/LowerCase.php b/lib/Zend/Filter/File/LowerCase.php
new file mode 100644
index 0000000..98abab2
--- /dev/null
+++ b/lib/Zend/Filter/File/LowerCase.php
@@ -0,0 +1,84 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: $
+ */
+
+/**
+ * @see Zend_Filter_StringToLower
+ */
+require_once 'Zend/Filter/StringToLower.php';
+
+/**
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Filter_File_LowerCase extends Zend_Filter_StringToLower
+{
+ /**
+ * Adds options to the filter at initiation
+ *
+ * @param string $options
+ */
+ public function __construct($options = null)
+ {
+ if (!empty($options)) {
+ $this->setEncoding($options);
+ }
+ }
+
+ /**
+ * Defined by Zend_Filter_Interface
+ *
+ * Does a lowercase on the content of the given file
+ *
+ * @param string $value Full path of file to change
+ * @return string The given $value
+ * @throws Zend_Filter_Exception
+ */
+ public function filter($value)
+ {
+ if (!file_exists($value)) {
+ require_once 'Zend/Filter/Exception.php';
+ throw new Zend_Filter_Exception("File '$value' not found");
+ }
+
+ if (!is_writable($value)) {
+ require_once 'Zend/Filter/Exception.php';
+ throw new Zend_Filter_Exception("File '$value' is not writable");
+ }
+
+ $content = file_get_contents($value);
+ if (!$content) {
+ require_once 'Zend/Filter/Exception.php';
+ throw new Zend_Filter_Exception("Problem while reading file '$value'");
+ }
+
+ $content = parent::filter($content);
+ $result = file_put_contents($value, $content);
+
+ if (!$result) {
+ require_once 'Zend/Filter/Exception.php';
+ throw new Zend_Filter_Exception("Problem while writing file '$value'");
+ }
+
+ return $value;
+ }
+}
diff --git a/lib/Zend/Filter/File/Rename.php b/lib/Zend/Filter/File/Rename.php
new file mode 100644
index 0000000..718443f
--- /dev/null
+++ b/lib/Zend/Filter/File/Rename.php
@@ -0,0 +1,282 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: $
+ */
+
+/**
+ * @see Zend_Filter_Interface
+ */
+require_once 'Zend/Filter/Interface.php';
+
+/**
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Filter_File_Rename implements Zend_Filter_Interface
+{
+ /**
+ * Internal array of array(source, target, overwrite)
+ */
+ protected $_files = array();
+
+ /**
+ * Class constructor
+ *
+ * Options argument may be either a string, a Zend_Config object, or an array.
+ * If an array or Zend_Config object, it accepts the following keys:
+ * 'source' => Source filename or directory which will be renamed
+ * 'target' => Target filename or directory, the new name of the sourcefile
+ * 'overwrite' => Shall existing files be overwritten ?
+ *
+ * @param string|array $options Target file or directory to be renamed
+ * @param string $target Source filename or directory (deprecated)
+ * @param bool $overwrite Should existing files be overwritten (deprecated)
+ * @return void
+ */
+ public function __construct($options)
+ {
+ if ($options instanceof Zend_Config) {
+ $options = $options->toArray();
+ } elseif (is_string($options)) {
+ $options = array('target' => $options);
+ } elseif (!is_array($options)) {
+ require_once 'Zend/Filter/Exception.php';
+ throw new Zend_Filter_Exception('Invalid options argument provided to filter');
+ }
+
+ if (1 < func_num_args()) {
+ trigger_error('Support for multiple arguments is deprecated in favor of a single options array', E_USER_NOTICE);
+ $argv = func_get_args();
+ array_shift($argv);
+ $source = array_shift($argv);
+ $overwrite = false;
+ if (!empty($argv)) {
+ $overwrite = array_shift($argv);
+ }
+ $options['source'] = $source;
+ $options['overwrite'] = $overwrite;
+ }
+
+ $this->setFile($options);
+ }
+
+ /**
+ * Returns the files to rename and their new name and location
+ *
+ * @return array
+ */
+ public function getFile()
+ {
+ return $this->_files;
+ }
+
+ /**
+ * Sets a new file or directory as target, deleting existing ones
+ *
+ * Array accepts the following keys:
+ * 'source' => Source filename or directory which will be renamed
+ * 'target' => Target filename or directory, the new name of the sourcefile
+ * 'overwrite' => Shall existing files be overwritten ?
+ *
+ * @param string|array $options Old file or directory to be rewritten
+ * @return Zend_Filter_File_Rename
+ */
+ public function setFile($options)
+ {
+ $this->_files = array();
+ $this->addFile($options);
+
+ return $this;
+ }
+
+ /**
+ * Adds a new file or directory as target to the existing ones
+ *
+ * Array accepts the following keys:
+ * 'source' => Source filename or directory which will be renamed
+ * 'target' => Target filename or directory, the new name of the sourcefile
+ * 'overwrite' => Shall existing files be overwritten ?
+ *
+ * @param string|array $options Old file or directory to be rewritten
+ * @return Zend_Filter_File_Rename
+ */
+ public function addFile($options)
+ {
+ if (is_string($options)) {
+ $options = array('target' => $options);
+ } elseif (!is_array($options)) {
+ require_once 'Zend/Filter/Exception.php';
+ throw new Zend_Filter_Exception ('Invalid options to rename filter provided');
+ }
+
+ $this->_convertOptions($options);
+
+ return $this;
+ }
+
+ /**
+ * Defined by Zend_Filter_Interface
+ *
+ * Renames the file $value to the new name set before
+ * Returns the file $value, removing all but digit characters
+ *
+ * @param string $value Full path of file to change
+ * @return string The new filename which has been set, or false when there were errors
+ */
+ public function filter($value)
+ {
+ $file = $this->_getFileName($value);
+ if ($file['source'] == $file['target']) {
+ return $value;
+ }
+
+ if (!file_exists($file['source'])) {
+ return $value;
+ }
+
+ if (($file['overwrite'] == true) and (file_exists($file['target']))) {
+ unlink($file['target']);
+ }
+
+ if (file_exists($file['target'])) {
+ require_once 'Zend/Filter/Exception.php';
+ throw new Zend_Filter_Exception(sprintf("File '%s' could not be renamed. It already exists.", $value));
+ }
+
+ $result = rename($file['source'], $file['target']);
+
+ if ($result === true) {
+ return $file['target'];
+ }
+
+ require_once 'Zend/Filter/Exception.php';
+ throw new Zend_Filter_Exception(sprintf("File '%s' could not be renamed. An error occured while processing the file.", $value));
+ }
+
+ /**
+ * Internal method for creating the file array
+ * Supports single and nested arrays
+ *
+ * @param array $options
+ * @return array
+ */
+ protected function _convertOptions($options) {
+ $files = array();
+ foreach ($options as $key => $value) {
+ if (is_array($value)) {
+ $this->_convertOptions($value);
+ continue;
+ }
+
+ switch ($key) {
+ case "source":
+ $files['source'] = (string) $value;
+ break;
+
+ case 'target' :
+ $files['target'] = (string) $value;
+ break;
+
+ case 'overwrite' :
+ $files['overwrite'] = (boolean) $value;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (empty($files)) {
+ return $this;
+ }
+
+ if (empty($files['source'])) {
+ $files['source'] = '*';
+ }
+
+ if (empty($files['target'])) {
+ $files['target'] = '*';
+ }
+
+ if (empty($files['overwrite'])) {
+ $files['overwrite'] = false;
+ }
+
+ $found = false;
+ foreach ($this->_files as $key => $value) {
+ if ($value['source'] == $files['source']) {
+ $this->_files[$key] = $files;
+ $found = true;
+ }
+ }
+
+ if (!$found) {
+ $count = count($this->_files);
+ $this->_files[$count] = $files;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Internal method to resolve the requested source
+ * and return all other related parameters
+ *
+ * @param string $file Filename to get the informations for
+ * @return array
+ */
+ protected function _getFileName($file)
+ {
+ $rename = array();
+ foreach ($this->_files as $value) {
+ if ($value['source'] == '*') {
+ if (!isset($rename['source'])) {
+ $rename = $value;
+ $rename['source'] = $file;
+ }
+ }
+
+ if ($value['source'] == $file) {
+ $rename = $value;
+ }
+ }
+
+ if (!isset($rename['source'])) {
+ return $file;
+ }
+
+ if (!isset($rename['target']) or ($rename['target'] == '*')) {
+ $rename['target'] = $rename['source'];
+ }
+
+ if (is_dir($rename['target'])) {
+ $name = basename($rename['source']);
+ $last = $rename['target'][strlen($rename['target']) - 1];
+ if (($last != '/') and ($last != '\\')) {
+ $rename['target'] .= DIRECTORY_SEPARATOR;
+ }
+
+ $rename['target'] .= $name;
+ }
+
+ return $rename;
+ }
+}
diff --git a/lib/Zend/Filter/File/UpperCase.php b/lib/Zend/Filter/File/UpperCase.php
new file mode 100644
index 0000000..9501e9e
--- /dev/null
+++ b/lib/Zend/Filter/File/UpperCase.php
@@ -0,0 +1,84 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: $
+ */
+
+/**
+ * @see Zend_Filter_StringToUpper
+ */
+require_once 'Zend/Filter/StringToUpper.php';
+
+/**
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Filter_File_UpperCase extends Zend_Filter_StringToUpper
+{
+ /**
+ * Adds options to the filter at initiation
+ *
+ * @param string $options
+ */
+ public function __construct($options = null)
+ {
+ if (!empty($options)) {
+ $this->setEncoding($options);
+ }
+ }
+
+ /**
+ * Defined by Zend_Filter_Interface
+ *
+ * Does a lowercase on the content of the given file
+ *
+ * @param string $value Full path of file to change
+ * @return string The given $value
+ * @throws Zend_Filter_Exception
+ */
+ public function filter($value)
+ {
+ if (!file_exists($value)) {
+ require_once 'Zend/Filter/Exception.php';
+ throw new Zend_Filter_Exception("File '$value' not found");
+ }
+
+ if (!is_writable($value)) {
+ require_once 'Zend/Filter/Exception.php';
+ throw new Zend_Filter_Exception("File '$value' is not writable");
+ }
+
+ $content = file_get_contents($value);
+ if (!$content) {
+ require_once 'Zend/Filter/Exception.php';
+ throw new Zend_Filter_Exception("Problem while reading file '$value'");
+ }
+
+ $content = parent::filter($content);
+ $result = file_put_contents($value, $content);
+
+ if (!$result) {
+ require_once 'Zend/Filter/Exception.php';
+ throw new Zend_Filter_Exception("Problem while writing file '$value'");
+ }
+
+ return $value;
+ }
+}
diff --git a/lib/Zend/Filter/HtmlEntities.php b/lib/Zend/Filter/HtmlEntities.php
new file mode 100644
index 0000000..e9887a2
--- /dev/null
+++ b/lib/Zend/Filter/HtmlEntities.php
@@ -0,0 +1,122 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: HtmlEntities.php 11783 2008-10-09 17:38:54Z andries $
+ */
+
+
+/**
+ * @see Zend_Filter_Interface
+ */
+require_once 'Zend/Filter/Interface.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Filter_HtmlEntities implements Zend_Filter_Interface
+{
+ /**
+ * Corresponds to second htmlentities() argument
+ *
+ * @var integer
+ */
+ protected $_quoteStyle;
+
+ /**
+ * Corresponds to third htmlentities() argument
+ *
+ * @var string
+ */
+ protected $_charSet;
+
+ /**
+ * Sets filter options
+ *
+ * @param integer $quoteStyle
+ * @param string $charSet
+ * @return void
+ */
+ public function __construct($quoteStyle = ENT_COMPAT, $charSet = 'ISO-8859-1')
+ {
+ $this->_quoteStyle = $quoteStyle;
+ $this->_charSet = $charSet;
+ }
+
+ /**
+ * Returns the quoteStyle option
+ *
+ * @return integer
+ */
+ public function getQuoteStyle()
+ {
+ return $this->_quoteStyle;
+ }
+
+ /**
+ * Sets the quoteStyle option
+ *
+ * @param integer $quoteStyle
+ * @return Zend_Filter_HtmlEntities Provides a fluent interface
+ */
+ public function setQuoteStyle($quoteStyle)
+ {
+ $this->_quoteStyle = $quoteStyle;
+ return $this;
+ }
+
+ /**
+ * Returns the charSet option
+ *
+ * @return string
+ */
+ public function getCharSet()
+ {
+ return $this->_charSet;
+ }
+
+ /**
+ * Sets the charSet option
+ *
+ * @param string $charSet
+ * @return Zend_Filter_HtmlEntities Provides a fluent interface
+ */
+ public function setCharSet($charSet)
+ {
+ $this->_charSet = $charSet;
+ return $this;
+ }
+
+ /**
+ * Defined by Zend_Filter_Interface
+ *
+ * Returns the string $value, converting characters to their corresponding HTML entity
+ * equivalents where they exist
+ *
+ * @param string $value
+ * @return string
+ */
+ public function filter($value)
+ {
+ return htmlentities((string) $value, $this->_quoteStyle, $this->_charSet);
+ }
+}
diff --git a/lib/Zend/Filter/Inflector.php b/lib/Zend/Filter/Inflector.php
new file mode 100644
index 0000000..4e7e493
--- /dev/null
+++ b/lib/Zend/Filter/Inflector.php
@@ -0,0 +1,497 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Inflector.php 12501 2008-11-10 16:28:31Z matthew $
+ */
+
+/**
+ * @see Zend_Filter
+ * @see Zend_Filter_Interface
+ */
+require_once 'Zend/Filter.php';
+
+/**
+ * @see Zend_Loader_PluginLoader
+ */
+require_once 'Zend/Loader/PluginLoader.php';
+
+/**
+ * Filter chain for string inflection
+ *
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Filter_Inflector implements Zend_Filter_Interface
+{
+ /**
+ * @var Zend_Loader_PluginLoader_Interface
+ */
+ protected $_pluginLoader = null;
+
+ /**
+ * @var string
+ */
+ protected $_target = null;
+
+ /**
+ * @var bool
+ */
+ protected $_throwTargetExceptionsOn = true;
+
+ /**
+ * @var string
+ */
+ protected $_targetReplacementIdentifier = ':';
+
+ /**
+ * @var array
+ */
+ protected $_rules = array();
+
+ /**
+ * Constructor
+ *
+ * @param string $target
+ * @param array $rules
+ */
+ public function __construct($target = null, Array $rules = array(), $throwTargetExceptionsOn = null, $targetReplacementIdentifer = null)
+ {
+ if ($target instanceof Zend_Config) {
+ $this->setConfig($target);
+ } else {
+ if ((null !== $target) && is_string($target)) {
+ $this->setTarget($target);
+ }
+
+ if (null !== $rules) {
+ $this->addRules($rules);
+ }
+
+ if ($throwTargetExceptionsOn !== null) {
+ $this->setThrowTargetExceptionsOn($throwTargetExceptionsOn);
+ }
+
+ if ($targetReplacementIdentifer != '') {
+ $this->setTargetReplacementIdentifier($targetReplacementIdentifer);
+ }
+ }
+ }
+
+ /**
+ * Retreive PluginLoader
+ *
+ * @return Zend_Loader_PluginLoader_Interface
+ */
+ public function getPluginLoader()
+ {
+ if (!$this->_pluginLoader instanceof Zend_Loader_PluginLoader_Interface) {
+ $this->_pluginLoader = new Zend_Loader_PluginLoader(array('Zend_Filter_' => 'Zend/Filter/'), __CLASS__);
+ }
+
+ return $this->_pluginLoader;
+ }
+
+ /**
+ * Set PluginLoader
+ *
+ * @param Zend_Loader_PluginLoader_Interface $pluginLoader
+ * @return Zend_Filter_Inflector
+ */
+ public function setPluginLoader(Zend_Loader_PluginLoader_Interface $pluginLoader)
+ {
+ $this->_pluginLoader = $pluginLoader;
+ return $this;
+ }
+
+ /**
+ * Use Zend_Config object to set object state
+ *
+ * @param Zend_Config $config
+ * @return Zend_Filter_Inflector
+ */
+ public function setConfig(Zend_Config $config)
+ {
+ foreach ($config as $key => $value) {
+ switch ($key) {
+ case 'target':
+ $this->setTarget($value);
+ break;
+ case 'filterPrefixPath':
+ if (is_scalar($value)) {
+ break;
+ }
+ $paths = $value->toArray();
+ foreach ($paths as $prefix => $path) {
+ $this->addFilterPrefixPath($prefix, $path);
+ }
+ break;
+ case 'throwTargetExceptionsOn':
+ $this->setThrowTargetExceptionsOn($value);
+ break;
+ case 'targetReplacementIdentifier':
+ $this->setTargetReplacementIdentifier($value);
+ break;
+ case 'rules':
+ $this->addRules($value->toArray());
+ break;
+ default:
+ break;
+ }
+ }
+ return $this;
+ }
+
+ /**
+ * Convienence method to add prefix and path to PluginLoader
+ *
+ * @param string $prefix
+ * @param string $path
+ * @return Zend_Filter_Inflector
+ */
+ public function addFilterPrefixPath($prefix, $path)
+ {
+ $this->getPluginLoader()->addPrefixPath($prefix, $path);
+ return $this;
+ }
+
+ /**
+ * Set Whether or not the inflector should throw an exception when a replacement
+ * identifier is still found within an inflected target.
+ *
+ * @param bool $throwTargetExceptions
+ * @return Zend_Filter_Inflector
+ */
+ public function setThrowTargetExceptionsOn($throwTargetExceptionsOn)
+ {
+ $this->_throwTargetExceptionsOn = ($throwTargetExceptionsOn == true) ? true : false;
+ return $this;
+ }
+
+ /**
+ * Will exceptions be thrown?
+ *
+ * @return bool
+ */
+ public function isThrowTargetExceptionsOn()
+ {
+ return $this->_throwTargetExceptionsOn;
+ }
+
+ /**
+ * Set the Target Replacement Identifier, by default ':'
+ *
+ * @param string $targetReplacementIdentifier
+ * @return Zend_Filter_Inflector
+ */
+ public function setTargetReplacementIdentifier($targetReplacementIdentifier)
+ {
+ $this->_targetReplacementIdentifier = (string) $targetReplacementIdentifier;
+ return $this;
+ }
+
+ /**
+ * Get Target Replacement Identifier
+ *
+ * @return string
+ */
+ public function getTargetReplacementIdentifier()
+ {
+ return $this->_targetReplacementIdentifier;
+ }
+
+ /**
+ * Set a Target
+ * ex: 'scripts/:controller/:action.:suffix'
+ *
+ * @param string
+ * @return Zend_Filter_Inflector
+ */
+ public function setTarget($target)
+ {
+ $this->_target = (string) $target;
+ return $this;
+ }
+
+ /**
+ * Retrieve target
+ *
+ * @return string
+ */
+ public function getTarget()
+ {
+ return $this->_target;
+ }
+
+ /**
+ * Set Target Reference
+ *
+ * @param reference $target
+ * @return Zend_Filter_Inflector
+ */
+ public function setTargetReference(&$target)
+ {
+ $this->_target =& $target;
+ return $this;
+ }
+
+ /**
+ * SetRules() is the same as calling addRules() with the exception that it
+ * clears the rules before adding them.
+ *
+ * @param array $rules
+ * @return Zend_Filter_Inflector
+ */
+ public function setRules(Array $rules)
+ {
+ $this->clearRules();
+ $this->addRules($rules);
+ return $this;
+ }
+
+ /**
+ * AddRules(): multi-call to setting filter rules.
+ *
+ * If prefixed with a ":" (colon), a filter rule will be added. If not
+ * prefixed, a static replacement will be added.
+ *
+ * ex:
+ * array(
+ * ':controller' => array('CamelCaseToUnderscore','StringToLower'),
+ * ':action' => array('CamelCaseToUnderscore','StringToLower'),
+ * 'suffix' => 'phtml'
+ * );
+ *
+ * @param array
+ * @return Zend_Filter_Inflector
+ */
+ public function addRules(Array $rules)
+ {
+ $keys = array_keys($rules);
+ foreach ($keys as $spec) {
+ if ($spec[0] == ':') {
+ $this->addFilterRule($spec, $rules[$spec]);
+ } else {
+ $this->setStaticRule($spec, $rules[$spec]);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get rules
+ *
+ * By default, returns all rules. If a $spec is provided, will return those
+ * rules if found, false otherwise.
+ *
+ * @param string $spec
+ * @return array|false
+ */
+ public function getRules($spec = null)
+ {
+ if (null !== $spec) {
+ $spec = $this->_normalizeSpec($spec);
+ if (isset($this->_rules[$spec])) {
+ return $this->_rules[$spec];
+ }
+ return false;
+ }
+
+ return $this->_rules;
+ }
+
+ /**
+ * getRule() returns a rule set by setFilterRule(), a numeric index must be provided
+ *
+ * @param string $spec
+ * @param int $index
+ * @return Zend_Filter_Interface|false
+ */
+ public function getRule($spec, $index)
+ {
+ $spec = $this->_normalizeSpec($spec);
+ if (isset($this->_rules[$spec]) && is_array($this->_rules[$spec])) {
+ if (isset($this->_rules[$spec][$index])) {
+ return $this->_rules[$spec][$index];
+ }
+ }
+ return false;
+ }
+
+ /**
+ * ClearRules() clears the rules currently in the inflector
+ *
+ * @return Zend_Filter_Inflector
+ */
+ public function clearRules()
+ {
+ $this->_rules = array();
+ return $this;
+ }
+
+ /**
+ * Set a filtering rule for a spec. $ruleSet can be a string, Filter object
+ * or an array of strings or filter objects.
+ *
+ * @param string $spec
+ * @param array|string|Zend_Filter_Interface $ruleSet
+ * @return Zend_Filter_Inflector
+ */
+ public function setFilterRule($spec, $ruleSet)
+ {
+ $spec = $this->_normalizeSpec($spec);
+ $this->_rules[$spec] = array();
+ return $this->addFilterRule($spec, $ruleSet);
+ }
+
+ /**
+ * Add a filter rule for a spec
+ *
+ * @param mixed $spec
+ * @param mixed $ruleSet
+ * @return void
+ */
+ public function addFilterRule($spec, $ruleSet)
+ {
+ $spec = $this->_normalizeSpec($spec);
+ if (!isset($this->_rules[$spec])) {
+ $this->_rules[$spec] = array();
+ }
+ if (!is_array($ruleSet)) {
+ $ruleSet = array($ruleSet);
+ }
+ foreach ($ruleSet as $rule) {
+ $this->_rules[$spec][] = $this->_getRule($rule);
+ }
+ return $this;
+ }
+
+ /**
+ * Set a static rule for a spec. This is a single string value
+ *
+ * @param string $name
+ * @param string $value
+ * @return Zend_Filter_Inflector
+ */
+ public function setStaticRule($name, $value)
+ {
+ $name = $this->_normalizeSpec($name);
+ $this->_rules[$name] = (string) $value;
+ return $this;
+ }
+
+ /**
+ * Set Static Rule Reference.
+ *
+ * This allows a consuming class to pass a property or variable
+ * in to be referenced when its time to build the output string from the
+ * target.
+ *
+ * @param string $name
+ * @param mixed $reference
+ * @return Zend_Filter_Inflector
+ */
+ public function setStaticRuleReference($name, &$reference)
+ {
+ $name = $this->_normalizeSpec($name);
+ $this->_rules[$name] =& $reference;
+ return $this;
+ }
+
+ /**
+ * Inflect
+ *
+ * @param string|array $source
+ * @return string
+ */
+ public function filter($source)
+ {
+ // clean source
+ foreach ( (array) $source as $sourceName => $sourceValue) {
+ $source[ltrim($sourceName, ':')] = $sourceValue;
+ }
+
+ $pregQuotedTargetReplacementIdentifier = preg_quote($this->_targetReplacementIdentifier, '#');
+ $processedParts = array();
+
+ foreach ($this->_rules as $ruleName => $ruleValue) {
+ if (isset($source[$ruleName])) {
+ if (is_string($ruleValue)) {
+ // overriding the set rule
+ $processedParts['#'.$pregQuotedTargetReplacementIdentifier.$ruleName.'#'] = str_replace('\\', '\\\\', $source[$ruleName]);
+ } elseif (is_array($ruleValue)) {
+ $processedPart = $source[$ruleName];
+ foreach ($ruleValue as $ruleFilter) {
+ $processedPart = $ruleFilter->filter($processedPart);
+ }
+ $processedParts['#'.$pregQuotedTargetReplacementIdentifier.$ruleName.'#'] = str_replace('\\', '\\\\', $processedPart);
+ }
+ } elseif (is_string($ruleValue)) {
+ $processedParts['#'.$pregQuotedTargetReplacementIdentifier.$ruleName.'#'] = str_replace('\\', '\\\\', $ruleValue);
+ }
+ }
+
+ // all of the values of processedParts would have been str_replace('\\', '\\\\', ..)'d to disable preg_replace backreferences
+ $inflectedTarget = preg_replace(array_keys($processedParts), array_values($processedParts), $this->_target);
+
+ if ($this->_throwTargetExceptionsOn && (preg_match('#(?='.$pregQuotedTargetReplacementIdentifier.'[A-Za-z]{1})#', $inflectedTarget) == true)) {
+ require_once 'Zend/Filter/Exception.php';
+ throw new Zend_Filter_Exception('A replacement identifier ' . $this->_targetReplacementIdentifier . ' was found inside the inflected target, perhaps a rule was not satisfied with a target source? Unsatisfied inflected target: ' . $inflectedTarget);
+ }
+
+ return $inflectedTarget;
+ }
+
+ /**
+ * Normalize spec string
+ *
+ * @param string $spec
+ * @return string
+ */
+ protected function _normalizeSpec($spec)
+ {
+ return ltrim((string) $spec, ':&');
+ }
+
+ /**
+ * Resolve named filters and convert them to filter objects.
+ *
+ * @param string $rule
+ * @return Zend_Filter_Interface
+ */
+ protected function _getRule($rule)
+ {
+ if ($rule instanceof Zend_Filter_Interface) {
+ return $rule;
+ }
+
+ $rule = (string) $rule;
+
+ $className = $this->getPluginLoader()->load($rule);
+ $ruleObject = new $className();
+ if (!$ruleObject instanceof Zend_Filter_Interface) {
+ require_once 'Zend/Filter/Exception.php';
+ throw new Zend_Filter_Exception('No class named ' . $rule . ' implementing Zend_Filter_Interface could be found');
+ }
+
+ return $ruleObject;
+ }
+
+}
diff --git a/lib/Zend/Filter/Input.php b/lib/Zend/Filter/Input.php
new file mode 100644
index 0000000..a6782a3
--- /dev/null
+++ b/lib/Zend/Filter/Input.php
@@ -0,0 +1,926 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Input.php 8856 2008-03-16 11:28:48Z thomas $
+ */
+
+/**
+ * @see Zend_Loader
+ */
+require_once 'Zend/Loader.php';
+
+/**
+ * @see Zend_Filter
+ */
+require_once 'Zend/Filter.php';
+
+/**
+ * @see Zend_Validate
+ */
+require_once 'Zend/Validate.php';
+
+/**
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Filter_Input
+{
+
+ const ALLOW_EMPTY = 'allowEmpty';
+ const BREAK_CHAIN = 'breakChainOnFailure';
+ const DEFAULT_VALUE = 'default';
+ const MESSAGES = 'messages';
+ const ESCAPE_FILTER = 'escapeFilter';
+ const FIELDS = 'fields';
+ const FILTER = 'filter';
+ const FILTER_CHAIN = 'filterChain';
+ const MISSING_MESSAGE = 'missingMessage';
+ const INPUT_NAMESPACE = 'inputNamespace';
+ const NOT_EMPTY_MESSAGE = 'notEmptyMessage';
+ const PRESENCE = 'presence';
+ const PRESENCE_OPTIONAL = 'optional';
+ const PRESENCE_REQUIRED = 'required';
+ const RULE = 'rule';
+ const RULE_WILDCARD = '*';
+ const VALIDATE = 'validate';
+ const VALIDATOR = 'validator';
+ const VALIDATOR_CHAIN = 'validatorChain';
+ const VALIDATOR_CHAIN_COUNT = 'validatorChainCount';
+
+ /**
+ * @var array Input data, before processing.
+ */
+ protected $_data = array();
+
+ /**
+ * @var array Association of rules to filters.
+ */
+ protected $_filterRules = array();
+
+ /**
+ * @var array Association of rules to validators.
+ */
+ protected $_validatorRules = array();
+
+ /**
+ * @var array After processing data, this contains mapping of valid fields
+ * to field values.
+ */
+ protected $_validFields = array();
+
+ /**
+ * @var array After processing data, this contains mapping of validation
+ * rules that did not pass validation to the array of messages returned
+ * by the validator chain.
+ */
+ protected $_invalidMessages = array();
+
+ /**
+ * @var array After processing data, this contains mapping of validation
+ * rules that did not pass validation to the array of error identifiers
+ * returned by the validator chain.
+ */
+ protected $_invalidErrors = array();
+
+ /**
+ * @var array After processing data, this contains mapping of validation
+ * rules in which some fields were missing to the array of messages
+ * indicating which fields were missing.
+ */
+ protected $_missingFields = array();
+
+ /**
+ * @var array After processing, this contains a copy of $_data elements
+ * that were not mentioned in any validation rule.
+ */
+ protected $_unknownFields = array();
+
+ /**
+ * @var Zend_Filter_Interface The filter object that is run on values
+ * returned by the getEscaped() method.
+ */
+ protected $_defaultEscapeFilter = null;
+
+ /**
+ * Plugin loaders
+ * @var array
+ */
+ protected $_loaders = array();
+
+ /**
+ * @var array Default values to use when processing filters and validators.
+ */
+ protected $_defaults = array(
+ self::ALLOW_EMPTY => false,
+ self::BREAK_CHAIN => false,
+ self::ESCAPE_FILTER => 'HtmlEntities',
+ self::MISSING_MESSAGE => "Field '%field%' is required by rule '%rule%', but the field is missing",
+ self::NOT_EMPTY_MESSAGE => "You must give a non-empty value for field '%field%'",
+ self::PRESENCE => self::PRESENCE_OPTIONAL
+ );
+
+ /**
+ * @var boolean Set to False initially, this is set to True after the
+ * input data have been processed. Reset to False in setData() method.
+ */
+ protected $_processed = false;
+
+ /**
+ * @param array $filterRules
+ * @param array $validatorRules
+ * @param array $data OPTIONAL
+ * @param array $options OPTIONAL
+ */
+ public function __construct($filterRules, $validatorRules, array $data = null, array $options = null)
+ {
+ if ($options) {
+ $this->setOptions($options);
+ }
+
+ $this->_filterRules = (array) $filterRules;
+ $this->_validatorRules = (array) $validatorRules;
+
+ if ($data) {
+ $this->setData($data);
+ }
+ }
+
+ /**
+ * @param mixed $namespaces
+ * @return Zend_Filter_Input
+ * @deprecated since 1.5.0RC1 - use addFilterPrefixPath() or addValidatorPrefixPath instead.
+ */
+ public function addNamespace($namespaces)
+ {
+ if (!is_array($namespaces)) {
+ $namespaces = array($namespaces);
+ }
+
+ foreach ($namespaces as $namespace) {
+ $prefix = $namespace;
+ $path = str_replace('_', DIRECTORY_SEPARATOR, $prefix);
+ $this->addFilterPrefixPath($prefix, $path);
+ $this->addValidatorPrefixPath($prefix, $path);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Add prefix path for all elements
+ *
+ * @param string $prefix
+ * @param string $path
+ * @return Zend_Filter_Input
+ */
+ public function addFilterPrefixPath($prefix, $path)
+ {
+ $this->getPluginLoader(self::FILTER)->addPrefixPath($prefix, $path);
+
+ return $this;
+ }
+
+ /**
+ * Add prefix path for all elements
+ *
+ * @param string $prefix
+ * @param string $path
+ * @return Zend_Filter_Input
+ */
+ public function addValidatorPrefixPath($prefix, $path)
+ {
+ $this->getPluginLoader(self::VALIDATE)->addPrefixPath($prefix, $path);
+
+ return $this;
+ }
+
+ /**
+ * Set plugin loaders for use with decorators and elements
+ *
+ * @param Zend_Loader_PluginLoader_Interface $loader
+ * @param string $type 'filter' or 'validate'
+ * @return Zend_Filter_Input
+ * @throws Zend_Filter_Exception on invalid type
+ */
+ public function setPluginLoader(Zend_Loader_PluginLoader_Interface $loader, $type)
+ {
+ $type = strtolower($type);
+ switch ($type) {
+ case self::FILTER:
+ case self::VALIDATE:
+ $this->_loaders[$type] = $loader;
+ return $this;
+ default:
+ require_once 'Zend/Filter/Exception.php';
+ throw new Zend_Filter_Exception(sprintf('Invalid type "%s" provided to setPluginLoader()', $type));
+ }
+
+ return $this;
+ }
+
+ /**
+ * Retrieve plugin loader for given type
+ *
+ * $type may be one of:
+ * - filter
+ * - validator
+ *
+ * If a plugin loader does not exist for the given type, defaults are
+ * created.
+ *
+ * @param string $type 'filter' or 'validate'
+ * @return Zend_Loader_PluginLoader_Interface
+ * @throws Zend_Filter_Exception on invalid type
+ */
+ public function getPluginLoader($type)
+ {
+ $type = strtolower($type);
+ if (!isset($this->_loaders[$type])) {
+ switch ($type) {
+ case self::FILTER:
+ $prefixSegment = 'Zend_Filter_';
+ $pathSegment = 'Zend/Filter/';
+ break;
+ case self::VALIDATE:
+ $prefixSegment = 'Zend_Validate_';
+ $pathSegment = 'Zend/Validate/';
+ break;
+ default:
+ require_once 'Zend/Filter/Exception.php';
+ throw new Zend_Filter_Exception(sprintf('Invalid type "%s" provided to getPluginLoader()', $type));
+ }
+
+ require_once 'Zend/Loader/PluginLoader.php';
+ $this->_loaders[$type] = new Zend_Loader_PluginLoader(
+ array($prefixSegment => $pathSegment)
+ );
+ }
+
+ return $this->_loaders[$type];
+ }
+
+ /**
+ * @return array
+ */
+ public function getMessages()
+ {
+ $this->_process();
+ return array_merge($this->_invalidMessages, $this->_missingFields);
+ }
+
+ /**
+ * @return array
+ */
+ public function getErrors()
+ {
+ $this->_process();
+ return $this->_invalidErrors;
+ }
+
+ /**
+ * @return array
+ */
+ public function getInvalid()
+ {
+ $this->_process();
+ return $this->_invalidMessages;
+ }
+
+ /**
+ * @return array
+ */
+ public function getMissing()
+ {
+ $this->_process();
+ return $this->_missingFields;
+ }
+
+ /**
+ * @return array
+ */
+ public function getUnknown()
+ {
+ $this->_process();
+ return $this->_unknownFields;
+ }
+
+ /**
+ * @param string $fieldName OPTIONAL
+ * @return mixed
+ */
+ public function getEscaped($fieldName = null)
+ {
+ $this->_process();
+ $this->_getDefaultEscapeFilter();
+
+ if ($fieldName === null) {
+ return $this->_escapeRecursive($this->_validFields);
+ }
+ if (array_key_exists($fieldName, $this->_validFields)) {
+ return $this->_escapeRecursive($this->_validFields[$fieldName]);
+ }
+ return null;
+ }
+
+ /**
+ * @param mixed $value
+ * @return mixed
+ */
+ protected function _escapeRecursive($data)
+ {
+ if (!is_array($data)) {
+ return $this->_getDefaultEscapeFilter()->filter($data);
+ }
+ foreach ($data as &$element) {
+ $element = $this->_escapeRecursive($element);
+ }
+ return $data;
+ }
+
+ /**
+ * @param string $fieldName OPTIONAL
+ * @return mixed
+ */
+ public function getUnescaped($fieldName = null)
+ {
+ $this->_process();
+ if ($fieldName === null) {
+ return $this->_validFields;
+ }
+ if (array_key_exists($fieldName, $this->_validFields)) {
+ return $this->_validFields[$fieldName];
+ }
+ return null;
+ }
+
+ /**
+ * @param string $fieldName
+ * @return mixed
+ */
+ public function __get($fieldName)
+ {
+ return $this->getEscaped($fieldName);
+ }
+
+ /**
+ * @return boolean
+ */
+ public function hasInvalid()
+ {
+ $this->_process();
+ return !(empty($this->_invalidMessages));
+ }
+
+ /**
+ * @return boolean
+ */
+ public function hasMissing()
+ {
+ $this->_process();
+ return !(empty($this->_missingFields));
+ }
+
+ /**
+ * @return boolean
+ */
+ public function hasUnknown()
+ {
+ $this->_process();
+ return !(empty($this->_unknownFields));
+ }
+
+ /**
+ * @return boolean
+ */
+ public function hasValid()
+ {
+ $this->_process();
+ return !(empty($this->_validFields));
+ }
+
+ /**
+ * @param string $fieldName
+ * @return boolean
+ */
+ public function isValid($fieldName = null)
+ {
+ $this->_process();
+ if ($fieldName === null) {
+ return !($this->hasMissing() || $this->hasInvalid());
+ }
+ return array_key_exists($fieldName, $this->_validFields);
+ }
+
+ /**
+ * @param string $fieldName
+ * @return boolean
+ */
+ public function __isset($fieldName)
+ {
+ $this->_process();
+ return isset($this->_validFields[$fieldName]);
+ }
+
+ /**
+ * @return Zend_Filter_Input
+ * @throws Zend_Filter_Exception
+ */
+ public function process()
+ {
+ $this->_process();
+ if ($this->hasInvalid()) {
+ require_once 'Zend/Filter/Exception.php';
+ throw new Zend_Filter_Exception("Input has invalid fields");
+ }
+ if ($this->hasMissing()) {
+ require_once 'Zend/Filter/Exception.php';
+ throw new Zend_Filter_Exception("Input has missing fields");
+ }
+
+ return $this;
+ }
+
+ /**
+ * @param array $data
+ * @return Zend_Filter_Input
+ */
+ public function setData(array $data)
+ {
+ $this->_data = $data;
+
+ /**
+ * Reset to initial state
+ */
+ $this->_validFields = array();
+ $this->_invalidMessages = array();
+ $this->_invalidErrors = array();
+ $this->_missingFields = array();
+ $this->_unknownFields = array();
+
+ $this->_processed = false;
+
+ return $this;
+ }
+
+ /**
+ * @param mixed $escapeFilter
+ * @return Zend_Filter_Interface
+ */
+ public function setDefaultEscapeFilter($escapeFilter)
+ {
+ if (is_string($escapeFilter) || is_array($escapeFilter)) {
+ $escapeFilter = $this->_getFilter($escapeFilter);
+ }
+ if (!$escapeFilter instanceof Zend_Filter_Interface) {
+ require_once 'Zend/Filter/Exception.php';
+ throw new Zend_Filter_Exception('Escape filter specified does not implement Zend_Filter_Interface');
+ }
+ $this->_defaultEscapeFilter = $escapeFilter;
+ return $escapeFilter;
+ }
+
+ /**
+ * @param array $options
+ * @return Zend_Filter_Input
+ * @throws Zend_Filter_Exception if an unknown option is given
+ */
+ public function setOptions(array $options)
+ {
+ foreach ($options as $option => $value) {
+ switch ($option) {
+ case self::ESCAPE_FILTER:
+ $this->setDefaultEscapeFilter($value);
+ break;
+ case self::INPUT_NAMESPACE:
+ $this->addNamespace($value);
+ break;
+ case self::ALLOW_EMPTY:
+ case self::BREAK_CHAIN:
+ case self::MISSING_MESSAGE:
+ case self::NOT_EMPTY_MESSAGE:
+ case self::PRESENCE:
+ $this->_defaults[$option] = $value;
+ break;
+ default:
+ require_once 'Zend/Filter/Exception.php';
+ throw new Zend_Filter_Exception("Unknown option '$option'");
+ break;
+ }
+ }
+
+ return $this;
+ }
+
+ /*
+ * Protected methods
+ */
+
+ /**
+ * @return void
+ */
+ protected function _filter()
+ {
+ foreach ($this->_filterRules as $ruleName => &$filterRule) {
+ /**
+ * Make sure we have an array representing this filter chain.
+ * Don't typecast to (array) because it might be a Zend_Filter object
+ */
+ if (!is_array($filterRule)) {
+ $filterRule = array($filterRule);
+ }
+
+ /**
+ * Filters are indexed by integer, metacommands are indexed by string.
+ * Pick out the filters.
+ */
+ $filterList = array();
+ foreach ($filterRule as $key => $value) {
+ if (is_int($key)) {
+ $filterList[] = $value;
+ }
+ }
+
+ /**
+ * Use defaults for filter metacommands.
+ */
+ $filterRule[self::RULE] = $ruleName;
+ if (!isset($filterRule[self::FIELDS])) {
+ $filterRule[self::FIELDS] = $ruleName;
+ }
+
+ /**
+ * Load all the filter classes and add them to the chain.
+ */
+ if (!isset($filterRule[self::FILTER_CHAIN])) {
+ $filterRule[self::FILTER_CHAIN] = new Zend_Filter();
+ foreach ($filterList as $filter) {
+ if (is_string($filter) || is_array($filter)) {
+ $filter = $this->_getFilter($filter);
+ }
+ $filterRule[self::FILTER_CHAIN]->addFilter($filter);
+ }
+ }
+
+ /**
+ * If the ruleName is the special wildcard rule,
+ * then apply the filter chain to all input data.
+ * Else just process the field named by the rule.
+ */
+ if ($ruleName == self::RULE_WILDCARD) {
+ foreach (array_keys($this->_data) as $field) {
+ $this->_filterRule(array_merge($filterRule, array(self::FIELDS => $field)));
+ }
+ } else {
+ $this->_filterRule($filterRule);
+ }
+ }
+ }
+
+ /**
+ * @param array $filterRule
+ * @return void
+ */
+ protected function _filterRule(array $filterRule)
+ {
+ $field = $filterRule[self::FIELDS];
+ if (!array_key_exists($field, $this->_data)) {
+ return;
+ }
+ if (is_array($this->_data[$field])) {
+ foreach ($this->_data[$field] as $key => $value) {
+ $this->_data[$field][$key] = $filterRule[self::FILTER_CHAIN]->filter($value);
+ }
+ } else {
+ $this->_data[$field] =
+ $filterRule[self::FILTER_CHAIN]->filter($this->_data[$field]);
+ }
+ }
+
+ /**
+ * @return Zend_Filter_Interface
+ */
+ protected function _getDefaultEscapeFilter()
+ {
+ if ($this->_defaultEscapeFilter !== null) {
+ return $this->_defaultEscapeFilter;
+ }
+ return $this->setDefaultEscapeFilter($this->_defaults[self::ESCAPE_FILTER]);
+ }
+
+ /**
+ * @param string $rule
+ * @param string $field
+ * @return string
+ */
+ protected function _getMissingMessage($rule, $field)
+ {
+ $message = $this->_defaults[self::MISSING_MESSAGE];
+ $message = str_replace('%rule%', $rule, $message);
+ $message = str_replace('%field%', $field, $message);
+ return $message;
+ }
+
+ /**
+ * @return string
+ */
+ protected function _getNotEmptyMessage($rule, $field)
+ {
+ $message = $this->_defaults[self::NOT_EMPTY_MESSAGE];
+ $message = str_replace('%rule%', $rule, $message);
+ $message = str_replace('%field%', $field, $message);
+ return $message;
+ }
+
+ /**
+ * @return void
+ */
+ protected function _process()
+ {
+ if ($this->_processed === false) {
+ $this->_filter();
+ $this->_validate();
+ $this->_processed = true;
+ }
+ }
+
+ /**
+ * @return void
+ */
+ protected function _validate()
+ {
+ /**
+ * Special case: if there are no validators, treat all fields as valid.
+ */
+ if (!$this->_validatorRules) {
+ $this->_validFields = $this->_data;
+ $this->_data = array();
+ return;
+ }
+
+ foreach ($this->_validatorRules as $ruleName => &$validatorRule) {
+ /**
+ * Make sure we have an array representing this validator chain.
+ * Don't typecast to (array) because it might be a Zend_Validate object
+ */
+ if (!is_array($validatorRule)) {
+ $validatorRule = array($validatorRule);
+ }
+
+ /**
+ * Validators are indexed by integer, metacommands are indexed by string.
+ * Pick out the validators.
+ */
+ $validatorList = array();
+ foreach ($validatorRule as $key => $value) {
+ if (is_int($key)) {
+ $validatorList[] = $value;
+ }
+ }
+
+ /**
+ * Use defaults for validation metacommands.
+ */
+ $validatorRule[self::RULE] = $ruleName;
+ if (!isset($validatorRule[self::FIELDS])) {
+ $validatorRule[self::FIELDS] = $ruleName;
+ }
+ if (!isset($validatorRule[self::BREAK_CHAIN])) {
+ $validatorRule[self::BREAK_CHAIN] = $this->_defaults[self::BREAK_CHAIN];
+ }
+ if (!isset($validatorRule[self::PRESENCE])) {
+ $validatorRule[self::PRESENCE] = $this->_defaults[self::PRESENCE];
+ }
+ if (!isset($validatorRule[self::ALLOW_EMPTY])) {
+ $validatorRule[self::ALLOW_EMPTY] = $this->_defaults[self::ALLOW_EMPTY];
+ }
+ if (!isset($validatorRule[self::MESSAGES])) {
+ $validatorRule[self::MESSAGES] = array();
+ } else if (!is_array($validatorRule[self::MESSAGES])) {
+ $validatorRule[self::MESSAGES] = array($validatorRule[self::MESSAGES]);
+ } else if (!array_intersect_key($validatorList, $validatorRule[self::MESSAGES])) {
+ $validatorRule[self::MESSAGES] = array($validatorRule[self::MESSAGES]);
+ }
+
+ /**
+ * Load all the validator classes and add them to the chain.
+ */
+ if (!isset($validatorRule[self::VALIDATOR_CHAIN])) {
+ $validatorRule[self::VALIDATOR_CHAIN] = new Zend_Validate();
+ $i = 0;
+ foreach ($validatorList as $validator) {
+
+ if (is_string($validator) || is_array($validator)) {
+ $validator = $this->_getValidator($validator);
+ }
+ if (isset($validatorRule[self::MESSAGES][$i])) {
+ $value = $validatorRule[self::MESSAGES][$i];
+ if (is_array($value)) {
+ $validator->setMessages($value);
+ } else {
+ $validator->setMessage($value);
+ }
+ }
+
+ $validatorRule[self::VALIDATOR_CHAIN]->addValidator($validator, $validatorRule[self::BREAK_CHAIN]);
+ ++$i;
+ }
+ $validatorRule[self::VALIDATOR_CHAIN_COUNT] = $i;
+ }
+
+ /**
+ * If the ruleName is the special wildcard rule,
+ * then apply the validator chain to all input data.
+ * Else just process the field named by the rule.
+ */
+ if ($ruleName == self::RULE_WILDCARD) {
+ foreach (array_keys($this->_data) as $field) {
+ $this->_validateRule(array_merge($validatorRule, array(self::FIELDS => $field)));
+ }
+ } else {
+ $this->_validateRule($validatorRule);
+ }
+ }
+
+ /**
+ * Unset fields in $_data that have been added to other arrays.
+ * We have to wait until all rules have been processed because
+ * a given field may be referenced by multiple rules.
+ */
+ foreach (array_merge(array_keys($this->_missingFields), array_keys($this->_invalidMessages)) as $rule) {
+ foreach ((array) $this->_validatorRules[$rule][self::FIELDS] as $field) {
+ unset($this->_data[$field]);
+ }
+ }
+ foreach ($this->_validFields as $field => $value) {
+ unset($this->_data[$field]);
+ }
+
+ /**
+ * Anything left over in $_data is an unknown field.
+ */
+ $this->_unknownFields = $this->_data;
+ }
+
+ /**
+ * @param array $validatorRule
+ * @return void
+ */
+ protected function _validateRule(array $validatorRule)
+ {
+ /**
+ * Get one or more data values from input, and check for missing fields.
+ * Apply defaults if fields are missing.
+ */
+ $data = array();
+ foreach ((array) $validatorRule[self::FIELDS] as $field) {
+ if (array_key_exists($field, $this->_data)) {
+ $data[$field] = $this->_data[$field];
+ } else
+ if (array_key_exists(self::DEFAULT_VALUE, $validatorRule)) {
+ if (is_array($validatorRule[self::DEFAULT_VALUE])) {
+ $key = array_search($field, (array) $validatorRule[self::FIELDS]);
+ if (array_key_exists($key, $validatorRule[self::DEFAULT_VALUE])) {
+ $data[$field] = $validatorRule[self::DEFAULT_VALUE][$key];
+ }
+ } else {
+ $data[$field] = $validatorRule[self::DEFAULT_VALUE];
+ }
+ } else
+ if ($validatorRule[self::PRESENCE] == self::PRESENCE_REQUIRED) {
+ $this->_missingFields[$validatorRule[self::RULE]][] =
+ $this->_getMissingMessage($validatorRule[self::RULE], $field);
+ }
+ }
+
+ /**
+ * If any required fields are missing, break the loop.
+ */
+ if (isset($this->_missingFields[$validatorRule[self::RULE]]) && count($this->_missingFields[$validatorRule[self::RULE]]) > 0) {
+ return;
+ }
+
+ /**
+ * Evaluate the inputs against the validator chain.
+ */
+ if (count((array) $validatorRule[self::FIELDS]) > 1) {
+ if (!$validatorRule[self::VALIDATOR_CHAIN]->isValid($data)) {
+ $this->_invalidMessages[$validatorRule[self::RULE]] = $validatorRule[self::VALIDATOR_CHAIN]->getMessages();
+ $this->_invalidErrors[$validatorRule[self::RULE]] = $validatorRule[self::VALIDATOR_CHAIN]->getErrors();
+ return;
+ }
+ } else {
+ $failed = false;
+ foreach ($data as $fieldKey => $field) {
+ if (!is_array($field)) {
+ $field = array($field);
+ }
+ foreach ($field as $value) {
+ if (empty($value)) {
+ if ($validatorRule[self::ALLOW_EMPTY] == true) {
+ continue;
+ }
+ if ($validatorRule[self::VALIDATOR_CHAIN_COUNT] == 0) {
+ $notEmptyValidator = $this->_getValidator('NotEmpty');
+ $notEmptyValidator->setMessage($this->_getNotEmptyMessage($validatorRule[self::RULE], $fieldKey));
+ $validatorRule[self::VALIDATOR_CHAIN]->addValidator($notEmptyValidator);
+ }
+ }
+ if (!$validatorRule[self::VALIDATOR_CHAIN]->isValid($value)) {
+ $this->_invalidMessages[$validatorRule[self::RULE]] =
+ $validatorRule[self::VALIDATOR_CHAIN]->getMessages();
+ $this->_invalidErrors[$validatorRule[self::RULE]] =
+ $validatorRule[self::VALIDATOR_CHAIN]->getErrors();
+ unset($this->_validFields[$fieldKey]);
+ $failed = true;
+ if ($validatorRule[self::BREAK_CHAIN]) {
+ return;
+ }
+ }
+ }
+ }
+ if ($failed) {
+ return;
+ }
+ }
+
+ /**
+ * If we got this far, the inputs for this rule pass validation.
+ */
+ foreach ((array) $validatorRule[self::FIELDS] as $field) {
+ if (array_key_exists($field, $data)) {
+ $this->_validFields[$field] = $data[$field];
+ }
+ }
+ }
+
+ /**
+ * @param mixed $classBaseName
+ * @return Zend_Filter_Interface
+ */
+ protected function _getFilter($classBaseName)
+ {
+ return $this->_getFilterOrValidator(self::FILTER, $classBaseName);
+ }
+
+ /**
+ * @param mixed $classBaseName
+ * @return Zend_Validate_Interface
+ */
+ protected function _getValidator($classBaseName)
+ {
+ return $this->_getFilterOrValidator(self::VALIDATE, $classBaseName);
+ }
+
+ /**
+ * @param string $type
+ * @param mixed $classBaseName
+ * @return Zend_Filter_Interface|Zend_Validate_Interface
+ * @throws Zend_Filter_Exception
+ */
+ protected function _getFilterOrValidator($type, $classBaseName)
+ {
+ $args = array();
+
+ if (is_array($classBaseName)) {
+ $args = $classBaseName;
+ $classBaseName = array_shift($args);
+ }
+
+ $interfaceName = 'Zend_' . ucfirst($type) . '_Interface';
+ $className = $this->getPluginLoader($type)->load(ucfirst($classBaseName));
+
+ $class = new ReflectionClass($className);
+
+ if (!$class->implementsInterface($interfaceName)) {
+ require_once 'Zend/Filter/Exception.php';
+ throw new Zend_Filter_Exception("Class based on basename '$classBaseName' must implement the '$interfaceName' interface");
+ }
+
+ if ($class->hasMethod('__construct')) {
+ $object = $class->newInstanceArgs($args);
+ } else {
+ $object = $class->newInstance();
+ }
+
+ return $object;
+ }
+
+}
diff --git a/lib/Zend/Filter/Int.php b/lib/Zend/Filter/Int.php
new file mode 100644
index 0000000..39c3d6d
--- /dev/null
+++ b/lib/Zend/Filter/Int.php
@@ -0,0 +1,50 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Int.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Filter_Interface
+ */
+require_once 'Zend/Filter/Interface.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Filter_Int implements Zend_Filter_Interface
+{
+ /**
+ * Defined by Zend_Filter_Interface
+ *
+ * Returns (int) $value
+ *
+ * @param string $value
+ * @return integer
+ */
+ public function filter($value)
+ {
+ return (int) ((string) $value);
+ }
+}
diff --git a/lib/Zend/Filter/Interface.php b/lib/Zend/Filter/Interface.php
new file mode 100644
index 0000000..61a3531
--- /dev/null
+++ b/lib/Zend/Filter/Interface.php
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Interface.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+interface Zend_Filter_Interface
+{
+ /**
+ * Returns the result of filtering $value
+ *
+ * @param mixed $value
+ * @throws Zend_Filter_Exception If filtering $value is impossible
+ * @return mixed
+ */
+ public function filter($value);
+}
diff --git a/lib/Zend/Filter/PregReplace.php b/lib/Zend/Filter/PregReplace.php
new file mode 100644
index 0000000..7e6ab27
--- /dev/null
+++ b/lib/Zend/Filter/PregReplace.php
@@ -0,0 +1,156 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: PregReplace.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+/**
+ * @see Zend_Filter_Interface
+ */
+require_once 'Zend/Filter/Interface.php';
+
+/**
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Filter_PregReplace implements Zend_Filter_Interface
+{
+ /**
+ * Pattern to match
+ * @var mixed
+ */
+ protected $_matchPattern = null;
+
+ /**
+ * Replacement pattern
+ * @var mixed
+ */
+ protected $_replacement = '';
+
+ /**
+ * Is unicode enabled?
+ *
+ * @var bool
+ */
+ static protected $_unicodeSupportEnabled = null;
+
+ /**
+ * Is Unicode Support Enabled Utility function
+ *
+ * @return bool
+ */
+ static public function isUnicodeSupportEnabled()
+ {
+ if (self::$_unicodeSupportEnabled === null) {
+ self::_determineUnicodeSupport();
+ }
+
+ return self::$_unicodeSupportEnabled;
+ }
+
+ /**
+ * Method to cache the regex needed to determine if unicode support is available
+ *
+ * @return bool
+ */
+ static protected function _determineUnicodeSupport()
+ {
+ self::$_unicodeSupportEnabled = (@preg_match('/\pL/u', 'a')) ? true : false;
+ }
+
+ /**
+ * Constructor
+ *
+ * @param string $match
+ * @param string $replace
+ * @return void
+ */
+ public function __construct($matchPattern = null, $replacement = null)
+ {
+ if ($matchPattern) {
+ $this->setMatchPattern($matchPattern);
+ }
+
+ if ($replacement) {
+ $this->setReplacement($replacement);
+ }
+ }
+
+ /**
+ * Set the match pattern for the regex being called within filter()
+ *
+ * @param mixed $match - same as the first argument of preg_replace
+ * @return Zend_Filter_PregReplace
+ */
+ public function setMatchPattern($match)
+ {
+ $this->_matchPattern = $match;
+ return $this;
+ }
+
+ /**
+ * Get currently set match pattern
+ *
+ * @return string
+ */
+ public function getMatchPattern()
+ {
+ return $this->_matchPattern;
+ }
+
+ /**
+ * Set the Replacement pattern/string for the preg_replace called in filter
+ *
+ * @param mixed $replacement - same as the second argument of preg_replace
+ * @return Zend_Filter_PregReplace
+ */
+ public function setReplacement($replacement)
+ {
+ $this->_replacement = $replacement;
+ return $this;
+ }
+
+ /**
+ * Get currently set replacement value
+ *
+ * @return string
+ */
+ public function getReplacement()
+ {
+ return $this->_replacement;
+ }
+
+ /**
+ * Perform regexp replacement as filter
+ *
+ * @param string $value
+ * @return string
+ */
+ public function filter($value)
+ {
+ if ($this->_matchPattern == null) {
+ require_once 'Zend/Filter/Exception.php';
+ throw new Zend_Filter_Exception(get_class($this) . ' does not have a valid MatchPattern set.');
+ }
+
+ return preg_replace($this->_matchPattern, $this->_replacement, $value);
+ }
+
+}
diff --git a/lib/Zend/Filter/RealPath.php b/lib/Zend/Filter/RealPath.php
new file mode 100644
index 0000000..f94de32
--- /dev/null
+++ b/lib/Zend/Filter/RealPath.php
@@ -0,0 +1,50 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: RealPath.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Filter_Interface
+ */
+require_once 'Zend/Filter/Interface.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Filter_RealPath implements Zend_Filter_Interface
+{
+ /**
+ * Defined by Zend_Filter_Interface
+ *
+ * Returns realpath($value)
+ *
+ * @param string $value
+ * @return string
+ */
+ public function filter($value)
+ {
+ return realpath((string) $value);
+ }
+}
diff --git a/lib/Zend/Filter/StringToLower.php b/lib/Zend/Filter/StringToLower.php
new file mode 100644
index 0000000..acd91f7
--- /dev/null
+++ b/lib/Zend/Filter/StringToLower.php
@@ -0,0 +1,76 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: StringToLower.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Filter_Interface
+ */
+require_once 'Zend/Filter/Interface.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Filter_StringToLower implements Zend_Filter_Interface
+{
+ /**
+ * Encoding for the input string
+ *
+ * @var string
+ */
+ protected $_encoding = null;
+
+ /**
+ * Set the input encoding for the given string
+ *
+ * @param string $encoding
+ * @throws Zend_Filter_Exception
+ */
+ public function setEncoding($encoding = null)
+ {
+ if (!function_exists('mb_strtolower')) {
+ require_once 'Zend/Filter/Exception.php';
+ throw new Zend_Filter_Exception('mbstring is required for this feature');
+ }
+ $this->_encoding = $encoding;
+ }
+
+ /**
+ * Defined by Zend_Filter_Interface
+ *
+ * Returns the string $value, converting characters to lowercase as necessary
+ *
+ * @param string $value
+ * @return string
+ */
+ public function filter($value)
+ {
+ if ($this->_encoding) {
+ return mb_strtolower((string) $value, $this->_encoding);
+ }
+
+ return strtolower((string) $value);
+ }
+}
diff --git a/lib/Zend/Filter/StringToUpper.php b/lib/Zend/Filter/StringToUpper.php
new file mode 100644
index 0000000..bd68aae
--- /dev/null
+++ b/lib/Zend/Filter/StringToUpper.php
@@ -0,0 +1,76 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: StringToUpper.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Filter_Interface
+ */
+require_once 'Zend/Filter/Interface.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Filter_StringToUpper implements Zend_Filter_Interface
+{
+ /**
+ * Encoding for the input string
+ *
+ * @var string
+ */
+ protected $_encoding = null;
+
+ /**
+ * Set the input encoding for the given string
+ *
+ * @param string $encoding
+ * @throws Zend_Filter_Exception
+ */
+ public function setEncoding($encoding = null)
+ {
+ if (!function_exists('mb_strtoupper')) {
+ require_once 'Zend/Filter/Exception.php';
+ throw new Zend_Filter_Exception('mbstring is required for this feature');
+ }
+ $this->_encoding = $encoding;
+ }
+
+ /**
+ * Defined by Zend_Filter_Interface
+ *
+ * Returns the string $value, converting characters to uppercase as necessary
+ *
+ * @param string $value
+ * @return string
+ */
+ public function filter($value)
+ {
+ if ($this->_encoding) {
+ return mb_strtoupper((string) $value, $this->_encoding);
+ }
+
+ return strtoupper((string) $value);
+ }
+}
diff --git a/lib/Zend/Filter/StringTrim.php b/lib/Zend/Filter/StringTrim.php
new file mode 100644
index 0000000..af22900
--- /dev/null
+++ b/lib/Zend/Filter/StringTrim.php
@@ -0,0 +1,97 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: StringTrim.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Filter_Interface
+ */
+require_once 'Zend/Filter/Interface.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Filter_StringTrim implements Zend_Filter_Interface
+{
+ /**
+ * List of characters provided to the trim() function
+ *
+ * If this is null, then trim() is called with no specific character list,
+ * and its default behavior will be invoked, trimming whitespace.
+ *
+ * @var string|null
+ */
+ protected $_charList;
+
+ /**
+ * Sets filter options
+ *
+ * @param string $charList
+ * @return void
+ */
+ public function __construct($charList = null)
+ {
+ $this->_charList = $charList;
+ }
+
+ /**
+ * Returns the charList option
+ *
+ * @return string|null
+ */
+ public function getCharList()
+ {
+ return $this->_charList;
+ }
+
+ /**
+ * Sets the charList option
+ *
+ * @param string|null $charList
+ * @return Zend_Filter_StringTrim Provides a fluent interface
+ */
+ public function setCharList($charList)
+ {
+ $this->_charList = $charList;
+ return $this;
+ }
+
+ /**
+ * Defined by Zend_Filter_Interface
+ *
+ * Returns the string $value with characters stripped from the beginning and end
+ *
+ * @param string $value
+ * @return string
+ */
+ public function filter($value)
+ {
+ if (null === $this->_charList) {
+ return trim((string) $value);
+ } else {
+ return trim((string) $value, $this->_charList);
+ }
+ }
+}
diff --git a/lib/Zend/Filter/StripNewlines.php b/lib/Zend/Filter/StripNewlines.php
new file mode 100644
index 0000000..b2dd042
--- /dev/null
+++ b/lib/Zend/Filter/StripNewlines.php
@@ -0,0 +1,48 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: $
+ */
+
+/**
+ * @see Zend_Filter_Interface
+ */
+require_once 'Zend/Filter/Interface.php';
+
+/**
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Filter_StripNewlines implements Zend_Filter_Interface
+{
+
+ /**
+ * Defined by Zend_Filter_Interface
+ *
+ * Returns $value without newline control characters
+ *
+ * @param string $value
+ * @return string
+ */
+ public function filter ($value)
+ {
+ return str_replace(array("\n", "\r"), '', $value);
+ }
+}
diff --git a/lib/Zend/Filter/StripTags.php b/lib/Zend/Filter/StripTags.php
new file mode 100644
index 0000000..4bd907c
--- /dev/null
+++ b/lib/Zend/Filter/StripTags.php
@@ -0,0 +1,294 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: StripTags.php 14273 2009-03-10 20:00:32Z matthew $
+ */
+
+
+/**
+ * @see Zend_Filter_Interface
+ */
+require_once 'Zend/Filter/Interface.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Filter_StripTags implements Zend_Filter_Interface
+{
+ /**
+ * Unique ID prefix used for allowing comments
+ */
+ const UNIQUE_ID_PREFIX = '__Zend_Filter_StripTags__';
+
+ /**
+ * Whether comments are allowed
+ *
+ * If false (the default), then comments are removed from the input string.
+ *
+ * @var boolean
+ */
+ public $commentsAllowed;
+
+ /**
+ * Array of allowed tags and allowed attributes for each allowed tag
+ *
+ * Tags are stored in the array keys, and the array values are themselves
+ * arrays of the attributes allowed for the corresponding tag.
+ *
+ * @var array
+ */
+ protected $_tagsAllowed = array();
+
+ /**
+ * Array of allowed attributes for all allowed tags
+ *
+ * Attributes stored here are allowed for all of the allowed tags.
+ *
+ * @var array
+ */
+ protected $_attributesAllowed = array();
+
+ /**
+ * Sets the filter options
+ *
+ * @param array|string $tagsAllowed
+ * @param array|string $attributesAllowed
+ * @param boolean $allowComments
+ * @return void
+ */
+ public function __construct($tagsAllowed = null, $attributesAllowed = null, $commentsAllowed = false)
+ {
+ $this->setTagsAllowed($tagsAllowed);
+ $this->setAttributesAllowed($attributesAllowed);
+ $this->commentsAllowed = (boolean) $commentsAllowed;
+ }
+
+ /**
+ * Returns the tagsAllowed option
+ *
+ * @return array
+ */
+ public function getTagsAllowed()
+ {
+ return $this->_tagsAllowed;
+ }
+
+ /**
+ * Sets the tagsAllowed option
+ *
+ * @param array|string $tagsAllowed
+ * @return Zend_Filter_StripTags Provides a fluent interface
+ */
+ public function setTagsAllowed($tagsAllowed)
+ {
+ if (!is_array($tagsAllowed)) {
+ $tagsAllowed = array($tagsAllowed);
+ }
+
+ foreach ($tagsAllowed as $index => $element) {
+ // If the tag was provided without attributes
+ if (is_int($index) && is_string($element)) {
+ // Canonicalize the tag name
+ $tagName = strtolower($element);
+ // Store the tag as allowed with no attributes
+ $this->_tagsAllowed[$tagName] = array();
+ }
+ // Otherwise, if a tag was provided with attributes
+ else if (is_string($index) && (is_array($element) || is_string($element))) {
+ // Canonicalize the tag name
+ $tagName = strtolower($index);
+ // Canonicalize the attributes
+ if (is_string($element)) {
+ $element = array($element);
+ }
+ // Store the tag as allowed with the provided attributes
+ $this->_tagsAllowed[$tagName] = array();
+ foreach ($element as $attribute) {
+ if (is_string($attribute)) {
+ // Canonicalize the attribute name
+ $attributeName = strtolower($attribute);
+ $this->_tagsAllowed[$tagName][$attributeName] = null;
+ }
+ }
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns the attributesAllowed option
+ *
+ * @return array
+ */
+ public function getAttributesAllowed()
+ {
+ return $this->_attributesAllowed;
+ }
+
+ /**
+ * Sets the attributesAllowed option
+ *
+ * @param array|string $attributesAllowed
+ * @return Zend_Filter_StripTags Provides a fluent interface
+ */
+ public function setAttributesAllowed($attributesAllowed)
+ {
+ if (!is_array($attributesAllowed)) {
+ $attributesAllowed = array($attributesAllowed);
+ }
+
+ // Store each attribute as allowed
+ foreach ($attributesAllowed as $attribute) {
+ if (is_string($attribute)) {
+ // Canonicalize the attribute name
+ $attributeName = strtolower($attribute);
+ $this->_attributesAllowed[$attributeName] = null;
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Defined by Zend_Filter_Interface
+ *
+ * @todo improve docblock descriptions
+ *
+ * @param string $value
+ * @return string
+ */
+ public function filter($value)
+ {
+ $valueCopy = (string) $value;
+
+ // If comments are allowed, then replace them with unique identifiers
+ if ($this->commentsAllowed) {
+ preg_match_all('/<\!--.*?--\s*>/s' , (string) $valueCopy, $matches);
+ $comments = array_unique($matches[0]);
+ foreach ($comments as $k => $v) {
+ $valueCopy = str_replace($v, self::UNIQUE_ID_PREFIX . $k, $valueCopy);
+ }
+ }
+
+ // Initialize accumulator for filtered data
+ $dataFiltered = '';
+ // Parse the input data iteratively as regular pre-tag text followed by a
+ // tag; either may be empty strings
+ preg_match_all('/([^<]*)(<?[^>]*>?)/', (string) $valueCopy, $matches);
+ // Iterate over each set of matches
+ foreach ($matches[1] as $index => $preTag) {
+ // If the pre-tag text is non-empty, strip any ">" characters from it
+ if (strlen($preTag)) {
+ $preTag = str_replace('>', '', $preTag);
+ }
+ // If a tag exists in this match, then filter the tag
+ $tag = $matches[2][$index];
+ if (strlen($tag)) {
+ $tagFiltered = $this->_filterTag($tag);
+ } else {
+ $tagFiltered = '';
+ }
+ // Add the filtered pre-tag text and filtered tag to the data buffer
+ $dataFiltered .= $preTag . $tagFiltered;
+ }
+
+ // If comments are allowed, then replace the unique identifiers with the corresponding comments
+ if ($this->commentsAllowed) {
+ foreach ($comments as $k => $v) {
+ $dataFiltered = str_replace(self::UNIQUE_ID_PREFIX . $k, $v, $dataFiltered);
+ }
+ }
+
+ // Return the filtered data
+ return $dataFiltered;
+ }
+
+ /**
+ * Filters a single tag against the current option settings
+ *
+ * @param string $tag
+ * @return string
+ */
+ protected function _filterTag($tag)
+ {
+ // Parse the tag into:
+ // 1. a starting delimiter (mandatory)
+ // 2. a tag name (if available)
+ // 3. a string of attributes (if available)
+ // 4. an ending delimiter (if available)
+ $isMatch = preg_match('~(</?)(\w*)((/(?!>)|[^/>])*)(/?>)~', $tag, $matches);
+
+ // If the tag does not match, then strip the tag entirely
+ if (!$isMatch) {
+ return '';
+ }
+
+ // Save the matches to more meaningfully named variables
+ $tagStart = $matches[1];
+ $tagName = strtolower($matches[2]);
+ $tagAttributes = $matches[3];
+ $tagEnd = $matches[5];
+
+ // If the tag is not an allowed tag, then remove the tag entirely
+ if (!isset($this->_tagsAllowed[$tagName])) {
+ return '';
+ }
+
+ // Trim the attribute string of whitespace at the ends
+ $tagAttributes = trim($tagAttributes);
+
+ // If there are non-whitespace characters in the attribute string
+ if (strlen($tagAttributes)) {
+ // Parse iteratively for well-formed attributes
+ preg_match_all('/(\w+)\s*=\s*(?:(")(.*?)"|(\')(.*?)\')/s', $tagAttributes, $matches);
+
+ // Initialize valid attribute accumulator
+ $tagAttributes = '';
+
+ // Iterate over each matched attribute
+ foreach ($matches[1] as $index => $attributeName) {
+ $attributeName = strtolower($attributeName);
+ $attributeDelimiter = $matches[2][$index];
+ $attributeValue = $matches[3][$index];
+
+ // If the attribute is not allowed, then remove it entirely
+ if (!array_key_exists($attributeName, $this->_tagsAllowed[$tagName])
+ && !array_key_exists($attributeName, $this->_attributesAllowed)) {
+ continue;
+ }
+ // Add the attribute to the accumulator
+ $tagAttributes .= " $attributeName=" . $attributeDelimiter
+ . $attributeValue . $attributeDelimiter;
+ }
+ }
+
+ // Reconstruct tags ending with "/>" as backwards-compatible XHTML tag
+ if (strpos($tagEnd, '/') !== false) {
+ $tagEnd = " $tagEnd";
+ }
+
+ // Return the filtered tag
+ return $tagStart . $tagName . $tagAttributes . $tagEnd;
+ }
+}
diff --git a/lib/Zend/Filter/Word/CamelCaseToDash.php b/lib/Zend/Filter/Word/CamelCaseToDash.php
new file mode 100644
index 0000000..c4717f2
--- /dev/null
+++ b/lib/Zend/Filter/Word/CamelCaseToDash.php
@@ -0,0 +1,44 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: CamelCaseToDash.php 6779 2007-11-08 15:10:41Z matthew $
+ */
+
+/**
+ * @see Zend_Filter_Interface
+ */
+require_once 'Zend/Filter/Word/CamelCaseToSeparator.php';
+
+/**
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Filter_Word_CamelCaseToDash extends Zend_Filter_Word_CamelCaseToSeparator
+{
+ /**
+ * Constructor
+ *
+ * @return void
+ */
+ public function __construct()
+ {
+ parent::__construct('-');
+ }
+}
diff --git a/lib/Zend/Filter/Word/CamelCaseToSeparator.php b/lib/Zend/Filter/Word/CamelCaseToSeparator.php
new file mode 100644
index 0000000..5dfa590
--- /dev/null
+++ b/lib/Zend/Filter/Word/CamelCaseToSeparator.php
@@ -0,0 +1,49 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: CamelCaseToSeparator.php 6779 2007-11-08 15:10:41Z matthew $
+ */
+
+/**
+ * @see Zend_Filter_PregReplace
+ */
+require_once 'Zend/Filter/Word/Separator/Abstract.php';
+
+/**
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Filter_Word_CamelCaseToSeparator extends Zend_Filter_Word_Separator_Abstract
+{
+
+ public function filter($value)
+ {
+ if (self::isUnicodeSupportEnabled()) {
+ parent::setMatchPattern(array('#(?<=(?:\p{Lu}))(\p{Lu}\p{Ll})#','#(?<=(?:\p{Ll}))(\p{Lu})#'));
+ parent::setReplacement(array($this->_separator . '\1', $this->_separator . '\1'));
+ } else {
+ parent::setMatchPattern(array('#(?<=(?:[A-Z]))([A-Z]+)([A-Z][A-z])#', '#(?<=(?:[a-z]))([A-Z])#'));
+ parent::setReplacement(array('\1' . $this->_separator . '\2', $this->_separator . '\1'));
+ }
+
+ return parent::filter($value);
+ }
+
+}
diff --git a/lib/Zend/Filter/Word/CamelCaseToUnderscore.php b/lib/Zend/Filter/Word/CamelCaseToUnderscore.php
new file mode 100644
index 0000000..659dfb4
--- /dev/null
+++ b/lib/Zend/Filter/Word/CamelCaseToUnderscore.php
@@ -0,0 +1,44 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: CamelCaseToUnderscore.php 6779 2007-11-08 15:10:41Z matthew $
+ */
+
+/**
+ * @see Zend_Filter_CamelCaseToSeparator
+ */
+require_once 'Zend/Filter/Word/CamelCaseToSeparator.php';
+
+/**
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Filter_Word_CamelCaseToUnderscore extends Zend_Filter_Word_CamelCaseToSeparator
+{
+ /**
+ * Constructor
+ *
+ * @return void
+ */
+ public function __construct()
+ {
+ parent::__construct('_');
+ }
+}
diff --git a/lib/Zend/Filter/Word/DashToCamelCase.php b/lib/Zend/Filter/Word/DashToCamelCase.php
new file mode 100644
index 0000000..a1ab8e7
--- /dev/null
+++ b/lib/Zend/Filter/Word/DashToCamelCase.php
@@ -0,0 +1,44 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: CamelCaseToDash.php 6779 2007-11-08 15:10:41Z matthew $
+ */
+
+/**
+ * @see Zend_Filter_Interface
+ */
+require_once 'Zend/Filter/Word/SeparatorToCamelCase.php';
+
+/**
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Filter_Word_DashToCamelCase extends Zend_Filter_Word_SeparatorToCamelCase
+{
+ /**
+ * Constructor
+ *
+ * @return void
+ */
+ public function __construct()
+ {
+ parent::__construct('-');
+ }
+}
diff --git a/lib/Zend/Filter/Word/DashToSeparator.php b/lib/Zend/Filter/Word/DashToSeparator.php
new file mode 100644
index 0000000..884ba1a
--- /dev/null
+++ b/lib/Zend/Filter/Word/DashToSeparator.php
@@ -0,0 +1,42 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: CamelCaseToSeparator.php 6779 2007-11-08 15:10:41Z matthew $
+ */
+
+/**
+ * @see Zend_Filter_PregReplace
+ */
+require_once 'Zend/Filter/Word/Separator/Abstract.php';
+
+/**
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Filter_Word_DashToSeparator extends Zend_Filter_Word_Separator_Abstract
+{
+
+ public function filter($value)
+ {
+ $this->setMatchPattern('#-#');
+ $this->setReplacement($this->_separator);
+ return parent::filter($value);
+ }
+}
diff --git a/lib/Zend/Filter/Word/DashToUnderscore.php b/lib/Zend/Filter/Word/DashToUnderscore.php
new file mode 100644
index 0000000..404598a
--- /dev/null
+++ b/lib/Zend/Filter/Word/DashToUnderscore.php
@@ -0,0 +1,45 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: CamelCaseToSeparator.php 6779 2007-11-08 15:10:41Z matthew $
+ */
+
+/**
+ * @see Zend_Filter_PregReplace
+ */
+require_once 'Zend/Filter/Word/SeparatorToSeparator.php';
+
+/**
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Filter_Word_DashToUnderscore extends Zend_Filter_Word_SeparatorToSeparator
+{
+ /**
+ * Constructor
+ *
+ * @param string $separator Space by default
+ * @return void
+ */
+ public function __construct()
+ {
+ parent::__construct('-', '_');
+ }
+} \ No newline at end of file
diff --git a/lib/Zend/Filter/Word/Separator/Abstract.php b/lib/Zend/Filter/Word/Separator/Abstract.php
new file mode 100644
index 0000000..b33b96b
--- /dev/null
+++ b/lib/Zend/Filter/Word/Separator/Abstract.php
@@ -0,0 +1,76 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: CamelCaseToUnderscore.php 6779 2007-11-08 15:10:41Z matthew $
+ */
+
+/**
+ * @see Zend_Filter_PregReplace
+ */
+require_once 'Zend/Filter/PregReplace.php';
+
+/**
+ * @category Zend
+ * @package Zend_Filter
+ * @uses Zend_Filter_PregReplace
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+abstract class Zend_Filter_Word_Separator_Abstract extends Zend_Filter_PregReplace
+{
+
+ protected $_separator = null;
+
+ /**
+ * Constructor
+ *
+ * @param string $separator Space by default
+ * @return void
+ */
+ public function __construct($separator = ' ')
+ {
+ $this->setSeparator($separator);
+ }
+
+ /**
+ * Sets a new seperator
+ *
+ * @param string $separator Seperator
+ * @return $this
+ */
+ public function setSeparator($separator)
+ {
+ if ($separator == null) {
+ require_once 'Zend/Filter/Exception.php';
+ throw new Zend_Filter_Exception('"' . $separator . '" is not a valid separator.');
+ }
+ $this->_separator = $separator;
+ return $this;
+ }
+
+ /**
+ * Returns the actual set seperator
+ *
+ * @return string
+ */
+ public function getSeparator()
+ {
+ return $this->_separator;
+ }
+
+} \ No newline at end of file
diff --git a/lib/Zend/Filter/Word/SeparatorToCamelCase.php b/lib/Zend/Filter/Word/SeparatorToCamelCase.php
new file mode 100644
index 0000000..853ec1b
--- /dev/null
+++ b/lib/Zend/Filter/Word/SeparatorToCamelCase.php
@@ -0,0 +1,52 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: CamelCaseToSeparator.php 6779 2007-11-08 15:10:41Z matthew $
+ */
+
+/**
+ * @see Zend_Filter_PregReplace
+ */
+require_once 'Zend/Filter/Word/Separator/Abstract.php';
+
+/**
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Filter_Word_SeparatorToCamelCase extends Zend_Filter_Word_Separator_Abstract
+{
+
+ public function filter($value)
+ {
+ // a unicode safe way of converting characters to \x00\x00 notation
+ $pregQuotedSeparator = preg_quote($this->_separator, '#');
+
+ if (self::isUnicodeSupportEnabled()) {
+ parent::setMatchPattern(array('#('.$pregQuotedSeparator.')(\p{L}{1})#e','#(^\p{Ll}{1})#e'));
+ parent::setReplacement(array("strtoupper('\\2')","strtoupper('\\1')"));
+ } else {
+ parent::setMatchPattern(array('#('.$pregQuotedSeparator.')([A-Z]{1})#e','#(^[a-z]{1})#e'));
+ parent::setReplacement(array("strtoupper('\\2')","strtoupper('\\1')"));
+ }
+
+ return parent::filter($value);
+ }
+
+}
diff --git a/lib/Zend/Filter/Word/SeparatorToDash.php b/lib/Zend/Filter/Word/SeparatorToDash.php
new file mode 100644
index 0000000..d5eab06
--- /dev/null
+++ b/lib/Zend/Filter/Word/SeparatorToDash.php
@@ -0,0 +1,46 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: CamelCaseToUnderscore.php 6779 2007-11-08 15:10:41Z matthew $
+ */
+
+/**
+ * @see Zend_Filter_SeperatorToSeparator
+ */
+require_once 'Zend/Filter/Word/SeparatorToSeparator.php';
+
+/**
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Filter_Word_SeparatorToDash extends Zend_Filter_Word_SeparatorToSeparator
+{
+ /**
+ * Constructor
+ *
+ * @param string $searchSeparator Seperator to search for change
+ * @return void
+ */
+ public function __construct($searchSeparator = ' ')
+ {
+ parent::__construct($searchSeparator, '-');
+ }
+
+} \ No newline at end of file
diff --git a/lib/Zend/Filter/Word/SeparatorToSeparator.php b/lib/Zend/Filter/Word/SeparatorToSeparator.php
new file mode 100644
index 0000000..e70aaf3
--- /dev/null
+++ b/lib/Zend/Filter/Word/SeparatorToSeparator.php
@@ -0,0 +1,129 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: CamelCaseToUnderscore.php 6779 2007-11-08 15:10:41Z matthew $
+ */
+
+/**
+ * @see Zend_Filter_PregReplace
+ */
+require_once 'Zend/Filter/PregReplace.php';
+
+/**
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Filter_Word_SeparatorToSeparator extends Zend_Filter_PregReplace
+{
+
+ protected $_searchSeparator = null;
+ protected $_replacementSeparator = null;
+
+ /**
+ * Constructor
+ *
+ * @param string $searchSeparator Seperator to search for
+ * @param string $replacementSeperator Seperator to replace with
+ * @return void
+ */
+ public function __construct($searchSeparator = ' ', $replacementSeparator = '-')
+ {
+ $this->setSearchSeparator($searchSeparator);
+ $this->setReplacementSeparator($replacementSeparator);
+ }
+
+ /**
+ * Sets a new seperator to search for
+ *
+ * @param string $separator Seperator to search for
+ * @return $this
+ */
+ public function setSearchSeparator($separator)
+ {
+ $this->_searchSeparator = $separator;
+ return $this;
+ }
+
+ /**
+ * Returns the actual set seperator to search for
+ *
+ * @return string
+ */
+ public function getSearchSeparator()
+ {
+ return $this->_searchSeparator;
+ }
+
+ /**
+ * Sets a new seperator which replaces the searched one
+ *
+ * @param string $separator Seperator which replaces the searched one
+ * @return $this
+ */
+ public function setReplacementSeparator($separator)
+ {
+ $this->_replacementSeparator = $separator;
+ return $this;
+ }
+
+ /**
+ * Returns the actual set seperator which replaces the searched one
+ *
+ * @return string
+ */
+ public function getReplacementSeparator()
+ {
+ return $this->_replacementSeparator;
+ }
+
+ /**
+ * Defined by Zend_Filter_Interface
+ *
+ * Returns the string $value, replacing the searched seperators with the defined ones
+ *
+ * @param string $value
+ * @return string
+ */
+ public function filter($value)
+ {
+ return $this->_separatorToSeparatorFilter($value);
+ }
+
+ /**
+ * Do the real work, replaces the seperator to search for with the replacement seperator
+ *
+ * Returns the replaced string
+ *
+ * @param string $value
+ * @return string
+ */
+ protected function _separatorToSeparatorFilter($value)
+ {
+ if ($this->_searchSeparator == null) {
+ require_once 'Zend/Filter/Exception.php';
+ throw new Zend_Filter_Exception('You must provide a search separator for this filter to work.');
+ }
+
+ $this->setMatchPattern('#' . preg_quote($this->_searchSeparator, '#') . '#');
+ $this->setReplacement($this->_replacementSeparator);
+ return parent::filter($value);
+ }
+
+} \ No newline at end of file
diff --git a/lib/Zend/Filter/Word/UnderscoreToCamelCase.php b/lib/Zend/Filter/Word/UnderscoreToCamelCase.php
new file mode 100644
index 0000000..aabbba9
--- /dev/null
+++ b/lib/Zend/Filter/Word/UnderscoreToCamelCase.php
@@ -0,0 +1,44 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: CamelCaseToDash.php 6779 2007-11-08 15:10:41Z matthew $
+ */
+
+/**
+ * @see Zend_Filter_Interface
+ */
+require_once 'Zend/Filter/Word/SeparatorToCamelCase.php';
+
+/**
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Filter_Word_UnderscoreToCamelCase extends Zend_Filter_Word_SeparatorToCamelCase
+{
+ /**
+ * Constructor
+ *
+ * @return void
+ */
+ public function __construct()
+ {
+ parent::__construct('_');
+ }
+}
diff --git a/lib/Zend/Filter/Word/UnderscoreToDash.php b/lib/Zend/Filter/Word/UnderscoreToDash.php
new file mode 100644
index 0000000..43326c4
--- /dev/null
+++ b/lib/Zend/Filter/Word/UnderscoreToDash.php
@@ -0,0 +1,45 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: CamelCaseToSeparator.php 6779 2007-11-08 15:10:41Z matthew $
+ */
+
+/**
+ * @see Zend_Filter_PregReplace
+ */
+require_once 'Zend/Filter/Word/SeparatorToSeparator.php';
+
+/**
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Filter_Word_UnderscoreToDash extends Zend_Filter_Word_SeparatorToSeparator
+{
+ /**
+ * Constructor
+ *
+ * @param string $separator Space by default
+ * @return void
+ */
+ public function __construct()
+ {
+ parent::__construct('_', '-');
+ }
+} \ No newline at end of file
diff --git a/lib/Zend/Filter/Word/UnderscoreToSeparator.php b/lib/Zend/Filter/Word/UnderscoreToSeparator.php
new file mode 100644
index 0000000..3fba924
--- /dev/null
+++ b/lib/Zend/Filter/Word/UnderscoreToSeparator.php
@@ -0,0 +1,45 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: UnderscoreToPathSeparator.php 6793 2007-11-09 18:10:06Z matthew $
+ */
+
+/**
+ * @see Zend_Filter_PregReplace
+ */
+require_once 'Zend/Filter/Word/SeparatorToSeparator.php';
+
+/**
+ * @category Zend
+ * @package Zend_Filter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Filter_Word_UnderscoreToSeparator extends Zend_Filter_Word_SeparatorToSeparator
+{
+ /**
+ * Constructor
+ *
+ * @param string $separator Space by default
+ * @return void
+ */
+ public function __construct($replacementSeparator = ' ')
+ {
+ parent::__construct('_', $replacementSeparator);
+ }
+}
diff --git a/lib/Zend/Loader.php b/lib/Zend/Loader.php
new file mode 100644
index 0000000..3d962a5
--- /dev/null
+++ b/lib/Zend/Loader.php
@@ -0,0 +1,263 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Loader
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Loader.php 12507 2008-11-10 16:29:09Z matthew $
+ */
+
+/**
+ * Static methods for loading classes and files.
+ *
+ * @category Zend
+ * @package Zend_Loader
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Loader
+{
+ /**
+ * Loads a class from a PHP file. The filename must be formatted
+ * as "$class.php".
+ *
+ * If $dirs is a string or an array, it will search the directories
+ * in the order supplied, and attempt to load the first matching file.
+ *
+ * If $dirs is null, it will split the class name at underscores to
+ * generate a path hierarchy (e.g., "Zend_Example_Class" will map
+ * to "Zend/Example/Class.php").
+ *
+ * If the file was not found in the $dirs, or if no $dirs were specified,
+ * it will attempt to load it from PHP's include_path.
+ *
+ * @param string $class - The full class name of a Zend component.
+ * @param string|array $dirs - OPTIONAL Either a path or an array of paths
+ * to search.
+ * @return void
+ * @throws Zend_Exception
+ */
+ public static function loadClass($class, $dirs = null)
+ {
+ if (class_exists($class, false) || interface_exists($class, false)) {
+ return;
+ }
+
+ if ((null !== $dirs) && !is_string($dirs) && !is_array($dirs)) {
+ require_once 'Zend/Exception.php';
+ throw new Zend_Exception('Directory argument must be a string or an array');
+ }
+
+ // autodiscover the path from the class name
+ $file = str_replace('_', DIRECTORY_SEPARATOR, $class) . '.php';
+ if (!empty($dirs)) {
+ // use the autodiscovered path
+ $dirPath = dirname($file);
+ if (is_string($dirs)) {
+ $dirs = explode(PATH_SEPARATOR, $dirs);
+ }
+ foreach ($dirs as $key => $dir) {
+ if ($dir == '.') {
+ $dirs[$key] = $dirPath;
+ } else {
+ $dir = rtrim($dir, '\\/');
+ $dirs[$key] = $dir . DIRECTORY_SEPARATOR . $dirPath;
+ }
+ }
+ $file = basename($file);
+ self::loadFile($file, $dirs, true);
+ } else {
+ self::_securityCheck($file);
+ include_once $file;
+ }
+
+ if (!class_exists($class, false) && !interface_exists($class, false)) {
+ require_once 'Zend/Exception.php';
+ throw new Zend_Exception("File \"$file\" does not exist or class \"$class\" was not found in the file");
+ }
+ }
+
+ /**
+ * Loads a PHP file. This is a wrapper for PHP's include() function.
+ *
+ * $filename must be the complete filename, including any
+ * extension such as ".php". Note that a security check is performed that
+ * does not permit extended characters in the filename. This method is
+ * intended for loading Zend Framework files.
+ *
+ * If $dirs is a string or an array, it will search the directories
+ * in the order supplied, and attempt to load the first matching file.
+ *
+ * If the file was not found in the $dirs, or if no $dirs were specified,
+ * it will attempt to load it from PHP's include_path.
+ *
+ * If $once is TRUE, it will use include_once() instead of include().
+ *
+ * @param string $filename
+ * @param string|array $dirs - OPTIONAL either a path or array of paths
+ * to search.
+ * @param boolean $once
+ * @return boolean
+ * @throws Zend_Exception
+ */
+ public static function loadFile($filename, $dirs = null, $once = false)
+ {
+ self::_securityCheck($filename);
+
+ /**
+ * Search in provided directories, as well as include_path
+ */
+ $incPath = false;
+ if (!empty($dirs) && (is_array($dirs) || is_string($dirs))) {
+ if (is_array($dirs)) {
+ $dirs = implode(PATH_SEPARATOR, $dirs);
+ }
+ $incPath = get_include_path();
+ set_include_path($dirs . PATH_SEPARATOR . $incPath);
+ }
+
+ /**
+ * Try finding for the plain filename in the include_path.
+ */
+ if ($once) {
+ include_once $filename;
+ } else {
+ include $filename;
+ }
+
+ /**
+ * If searching in directories, reset include_path
+ */
+ if ($incPath) {
+ set_include_path($incPath);
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns TRUE if the $filename is readable, or FALSE otherwise.
+ * This function uses the PHP include_path, where PHP's is_readable()
+ * does not.
+ *
+ * Note from ZF-2900:
+ * If you use custom error handler, please check whether return value
+ * from error_reporting() is zero or not.
+ * At mark of fopen() can not suppress warning if the handler is used.
+ *
+ * @param string $filename
+ * @return boolean
+ */
+ public static function isReadable($filename)
+ {
+ if (!$fh = @fopen($filename, 'r', true)) {
+ return false;
+ }
+ @fclose($fh);
+ return true;
+ }
+
+ /**
+ * spl_autoload() suitable implementation for supporting class autoloading.
+ *
+ * Attach to spl_autoload() using the following:
+ * <code>
+ * spl_autoload_register(array('Zend_Loader', 'autoload'));
+ * </code>
+ *
+ * @param string $class
+ * @return string|false Class name on success; false on failure
+ */
+ public static function autoload($class)
+ {
+ try {
+ self::loadClass($class);
+ return $class;
+ } catch (Exception $e) {
+ return false;
+ }
+ }
+
+ /**
+ * Register {@link autoload()} with spl_autoload()
+ *
+ * @param string $class (optional)
+ * @param boolean $enabled (optional)
+ * @return void
+ * @throws Zend_Exception if spl_autoload() is not found
+ * or if the specified class does not have an autoload() method.
+ */
+ public static function registerAutoload($class = 'Zend_Loader', $enabled = true)
+ {
+ if (!function_exists('spl_autoload_register')) {
+ require_once 'Zend/Exception.php';
+ throw new Zend_Exception('spl_autoload does not exist in this PHP installation');
+ }
+
+ self::loadClass($class);
+ $methods = get_class_methods($class);
+ if (!in_array('autoload', (array) $methods)) {
+ require_once 'Zend/Exception.php';
+ throw new Zend_Exception("The class \"$class\" does not have an autoload() method");
+ }
+
+ if ($enabled === true) {
+ spl_autoload_register(array($class, 'autoload'));
+ } else {
+ spl_autoload_unregister(array($class, 'autoload'));
+ }
+ }
+
+ /**
+ * Ensure that filename does not contain exploits
+ *
+ * @param string $filename
+ * @return void
+ * @throws Zend_Exception
+ */
+ protected static function _securityCheck($filename)
+ {
+ /**
+ * Security check
+ */
+ if (preg_match('/[^a-z0-9\\/\\\\_.-]/i', $filename)) {
+ require_once 'Zend/Exception.php';
+ throw new Zend_Exception('Security check: Illegal character in filename');
+ }
+ }
+
+ /**
+ * Attempt to include() the file.
+ *
+ * include() is not prefixed with the @ operator because if
+ * the file is loaded and contains a parse error, execution
+ * will halt silently and this is difficult to debug.
+ *
+ * Always set display_errors = Off on production servers!
+ *
+ * @param string $filespec
+ * @param boolean $once
+ * @return boolean
+ * @deprecated Since 1.5.0; use loadFile() instead
+ */
+ protected static function _includeFile($filespec, $once = false)
+ {
+ if ($once) {
+ return include_once $filespec;
+ } else {
+ return include $filespec ;
+ }
+ }
+}
diff --git a/lib/Zend/Loader/Exception.php b/lib/Zend/Loader/Exception.php
new file mode 100644
index 0000000..2c0e1e4
--- /dev/null
+++ b/lib/Zend/Loader/Exception.php
@@ -0,0 +1,35 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Loader
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: CamelCaseToUnderscore.php 6779 2007-11-08 15:10:41Z matthew $
+ */
+
+/**
+ * @see Zend_Exception
+ */
+require_once 'Zend/Exception.php';
+
+/**
+ * @category Zend
+ * @package Zend_Loader
+ * @uses Zend_Exception
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Loader_Exception extends Zend_Exception
+{} \ No newline at end of file
diff --git a/lib/Zend/Loader/PluginLoader.php b/lib/Zend/Loader/PluginLoader.php
new file mode 100644
index 0000000..c1bd989
--- /dev/null
+++ b/lib/Zend/Loader/PluginLoader.php
@@ -0,0 +1,464 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Loader
+ * @subpackage PluginLoader
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+/** Zend_Loader_PluginLoader_Interface */
+require_once 'Zend/Loader/PluginLoader/Interface.php';
+
+/** Zend_Loader */
+require_once 'Zend/Loader.php';
+
+/**
+ * Generic plugin class loader
+ *
+ * @category Zend
+ * @package Zend_Loader
+ * @subpackage PluginLoader
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Loader_PluginLoader implements Zend_Loader_PluginLoader_Interface
+{
+ /**
+ * Class map cache file
+ * @var string
+ */
+ protected static $_includeFileCache;
+
+ /**
+ * Instance loaded plugin paths
+ *
+ * @var array
+ */
+ protected $_loadedPluginPaths = array();
+
+ /**
+ * Instance loaded plugins
+ *
+ * @var array
+ */
+ protected $_loadedPlugins = array();
+
+ /**
+ * Instance registry property
+ *
+ * @var array
+ */
+ protected $_prefixToPaths = array();
+
+ /**
+ * Statically loaded plugin path mappings
+ *
+ * @var array
+ */
+ protected static $_staticLoadedPluginPaths = array();
+
+ /**
+ * Statically loaded plugins
+ *
+ * @var array
+ */
+ protected static $_staticLoadedPlugins = array();
+
+ /**
+ * Static registry property
+ *
+ * @var array
+ */
+ protected static $_staticPrefixToPaths = array();
+
+ /**
+ * Whether to use a statically named registry for loading plugins
+ *
+ * @var string|null
+ */
+ protected $_useStaticRegistry = null;
+
+ /**
+ * Constructor
+ *
+ * @param array $prefixToPaths
+ * @param string $staticRegistryName OPTIONAL
+ */
+ public function __construct(Array $prefixToPaths = array(), $staticRegistryName = null)
+ {
+ if (is_string($staticRegistryName) && !empty($staticRegistryName)) {
+ $this->_useStaticRegistry = $staticRegistryName;
+ if(!isset(self::$_staticPrefixToPaths[$staticRegistryName])) {
+ self::$_staticPrefixToPaths[$staticRegistryName] = array();
+ }
+ if(!isset(self::$_staticLoadedPlugins[$staticRegistryName])) {
+ self::$_staticLoadedPlugins[$staticRegistryName] = array();
+ }
+ }
+
+ foreach ($prefixToPaths as $prefix => $path) {
+ $this->addPrefixPath($prefix, $path);
+ }
+ }
+
+ /**
+ * Format prefix for internal use
+ *
+ * @param string $prefix
+ * @return string
+ */
+ protected function _formatPrefix($prefix)
+ {
+ return rtrim($prefix, '_') . '_';
+ }
+
+ /**
+ * Add prefixed paths to the registry of paths
+ *
+ * @param string $prefix
+ * @param string $path
+ * @return Zend_Loader_PluginLoader
+ */
+ public function addPrefixPath($prefix, $path)
+ {
+ if (!is_string($prefix) || !is_string($path)) {
+ require_once 'Zend/Loader/PluginLoader/Exception.php';
+ throw new Zend_Loader_PluginLoader_Exception('Zend_Loader_PluginLoader::addPrefixPath() method only takes strings for prefix and path.');
+ }
+
+ $prefix = $this->_formatPrefix($prefix);
+ $path = rtrim($path, '/\\') . '/';
+
+ if ($this->_useStaticRegistry) {
+ self::$_staticPrefixToPaths[$this->_useStaticRegistry][$prefix][] = $path;
+ } else {
+ $this->_prefixToPaths[$prefix][] = $path;
+ }
+ return $this;
+ }
+
+ /**
+ * Get path stack
+ *
+ * @param string $prefix
+ * @return false|array False if prefix does not exist, array otherwise
+ */
+ public function getPaths($prefix = null)
+ {
+ if ((null !== $prefix) && is_string($prefix)) {
+ $prefix = $this->_formatPrefix($prefix);
+ if ($this->_useStaticRegistry) {
+ if (isset(self::$_staticPrefixToPaths[$this->_useStaticRegistry][$prefix])) {
+ return self::$_staticPrefixToPaths[$this->_useStaticRegistry][$prefix];
+ }
+
+ return false;
+ }
+
+ if (isset($this->_prefixToPaths[$prefix])) {
+ return $this->_prefixToPaths[$prefix];
+ }
+
+ return false;
+ }
+
+ if ($this->_useStaticRegistry) {
+ return self::$_staticPrefixToPaths[$this->_useStaticRegistry];
+ }
+
+ return $this->_prefixToPaths;
+ }
+
+ /**
+ * Clear path stack
+ *
+ * @param string $prefix
+ * @return bool False only if $prefix does not exist
+ */
+ public function clearPaths($prefix = null)
+ {
+ if ((null !== $prefix) && is_string($prefix)) {
+ $prefix = $this->_formatPrefix($prefix);
+ if ($this->_useStaticRegistry) {
+ if (isset(self::$_staticPrefixToPaths[$this->_useStaticRegistry][$prefix])) {
+ unset(self::$_staticPrefixToPaths[$this->_useStaticRegistry][$prefix]);
+ return true;
+ }
+
+ return false;
+ }
+
+ if (isset($this->_prefixToPaths[$prefix])) {
+ unset($this->_prefixToPaths[$prefix]);
+ return true;
+ }
+
+ return false;
+ }
+
+ if ($this->_useStaticRegistry) {
+ self::$_staticPrefixToPaths[$this->_useStaticRegistry] = array();
+ } else {
+ $this->_prefixToPaths = array();
+ }
+
+ return true;
+ }
+
+ /**
+ * Remove a prefix (or prefixed-path) from the registry
+ *
+ * @param string $prefix
+ * @param string $path OPTIONAL
+ * @return Zend_Loader_PluginLoader
+ */
+ public function removePrefixPath($prefix, $path = null)
+ {
+ $prefix = $this->_formatPrefix($prefix);
+ if ($this->_useStaticRegistry) {
+ $registry =& self::$_staticPrefixToPaths[$this->_useStaticRegistry];
+ } else {
+ $registry =& $this->_prefixToPaths;
+ }
+
+ if (!isset($registry[$prefix])) {
+ require_once 'Zend/Loader/PluginLoader/Exception.php';
+ throw new Zend_Loader_PluginLoader_Exception('Prefix ' . $prefix . ' was not found in the PluginLoader.');
+ }
+
+ if ($path != null) {
+ $pos = array_search($path, $registry[$prefix]);
+ if ($pos === null) {
+ require_once 'Zend/Loader/PluginLoader/Exception.php';
+ throw new Zend_Loader_PluginLoader_Exception('Prefix ' . $prefix . ' / Path ' . $path . ' was not found in the PluginLoader.');
+ }
+ unset($registry[$prefix][$pos]);
+ } else {
+ unset($registry[$prefix]);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Normalize plugin name
+ *
+ * @param string $name
+ * @return string
+ */
+ protected function _formatName($name)
+ {
+ return ucfirst((string) $name);
+ }
+
+ /**
+ * Whether or not a Plugin by a specific name is loaded
+ *
+ * @param string $name
+ * @return Zend_Loader_PluginLoader
+ */
+ public function isLoaded($name)
+ {
+ $name = $this->_formatName($name);
+ if ($this->_useStaticRegistry) {
+ return isset(self::$_staticLoadedPlugins[$this->_useStaticRegistry][$name]);
+ }
+
+ return isset($this->_loadedPlugins[$name]);
+ }
+
+ /**
+ * Return full class name for a named plugin
+ *
+ * @param string $name
+ * @return string|false False if class not found, class name otherwise
+ */
+ public function getClassName($name)
+ {
+ $name = $this->_formatName($name);
+ if ($this->_useStaticRegistry
+ && isset(self::$_staticLoadedPlugins[$this->_useStaticRegistry][$name])
+ ) {
+ return self::$_staticLoadedPlugins[$this->_useStaticRegistry][$name];
+ } elseif (isset($this->_loadedPlugins[$name])) {
+ return $this->_loadedPlugins[$name];
+ }
+
+ return false;
+ }
+
+ /**
+ * Get path to plugin class
+ *
+ * @param mixed $name
+ * @return string|false False if not found
+ */
+ public function getClassPath($name)
+ {
+ $name = $this->_formatName($name);
+ if ($this->_useStaticRegistry
+ && !empty(self::$_staticLoadedPluginPaths[$this->_useStaticRegistry][$name])
+ ) {
+ return self::$_staticLoadedPluginPaths[$this->_useStaticRegistry][$name];
+ } elseif (!empty($this->_loadedPluginPaths[$name])) {
+ return $this->_loadedPluginPaths[$name];
+ }
+
+ if ($this->isLoaded($name)) {
+ $class = $this->getClassName($name);
+ $r = new ReflectionClass($class);
+ $path = $r->getFileName();
+ if ($this->_useStaticRegistry) {
+ self::$_staticLoadedPluginPaths[$this->_useStaticRegistry][$name] = $path;
+ } else {
+ $this->_loadedPluginPaths[$name] = $path;
+ }
+ return $path;
+ }
+
+ return false;
+ }
+
+ /**
+ * Load a plugin via the name provided
+ *
+ * @param string $name
+ * @return string Class name of loaded class
+ * @throws Zend_Loader_Exception if class not found
+ */
+ public function load($name)
+ {
+ $name = $this->_formatName($name);
+ if ($this->isLoaded($name)) {
+ return $this->getClassName($name);
+ }
+
+ if ($this->_useStaticRegistry) {
+ $registry = self::$_staticPrefixToPaths[$this->_useStaticRegistry];
+ } else {
+ $registry = $this->_prefixToPaths;
+ }
+
+ $registry = array_reverse($registry, true);
+ $found = false;
+ $classFile = str_replace('_', DIRECTORY_SEPARATOR, $name) . '.php';
+ $incFile = self::getIncludeFileCache();
+ foreach ($registry as $prefix => $paths) {
+ $className = $prefix . $name;
+
+ if (class_exists($className, false)) {
+ $found = true;
+ break;
+ }
+
+ $paths = array_reverse($paths, true);
+
+ foreach ($paths as $path) {
+ $loadFile = $path . $classFile;
+ if (Zend_Loader::isReadable($loadFile)) {
+ include_once $loadFile;
+ if (class_exists($className, false)) {
+ if (null !== $incFile) {
+ self::_appendIncFile($loadFile);
+ }
+ $found = true;
+ break 2;
+ }
+ }
+ }
+ }
+
+ if (!$found) {
+ $message = "Plugin by name '$name' was not found in the registry; used paths:";
+ foreach ($registry as $prefix => $paths) {
+ $message .= "\n$prefix: " . implode(PATH_SEPARATOR, $paths);
+ }
+ require_once 'Zend/Loader/PluginLoader/Exception.php';
+ throw new Zend_Loader_PluginLoader_Exception($message);
+ }
+
+ if ($this->_useStaticRegistry) {
+ self::$_staticLoadedPlugins[$this->_useStaticRegistry][$name] = $className;
+ self::$_staticLoadedPluginPaths[$this->_useStaticRegistry][$name] = (isset($loadFile) ? $loadFile : '');
+ } else {
+ $this->_loadedPlugins[$name] = $className;
+ $this->_loadedPluginPaths[$name] = (isset($loadFile) ? $loadFile : '');
+ }
+ return $className;
+ }
+
+ /**
+ * Set path to class file cache
+ *
+ * Specify a path to a file that will add include_once statements for each
+ * plugin class loaded. This is an opt-in feature for performance purposes.
+ *
+ * @param string $file
+ * @return void
+ * @throws Zend_Loader_PluginLoader_Exception if file is not writeable or path does not exist
+ */
+ public static function setIncludeFileCache($file)
+ {
+ if (null === $file) {
+ self::$_includeFileCache = null;
+ return;
+ }
+
+ if (!file_exists($file) && !file_exists(dirname($file))) {
+ require_once 'Zend/Loader/PluginLoader/Exception.php';
+ throw new Zend_Loader_PluginLoader_Exception('Specified file does not exist and/or directory does not exist (' . $file . ')');
+ }
+ if (file_exists($file) && !is_writable($file)) {
+ require_once 'Zend/Loader/PluginLoader/Exception.php';
+ throw new Zend_Loader_PluginLoader_Exception('Specified file is not writeable (' . $file . ')');
+ }
+ if (!file_exists($file) && file_exists(dirname($file)) && !is_writable(dirname($file))) {
+ require_once 'Zend/Loader/PluginLoader/Exception.php';
+ throw new Zend_Loader_PluginLoader_Exception('Specified file is not writeable (' . $file . ')');
+ }
+
+ self::$_includeFileCache = $file;
+ }
+
+ /**
+ * Retrieve class file cache path
+ *
+ * @return string|null
+ */
+ public static function getIncludeFileCache()
+ {
+ return self::$_includeFileCache;
+ }
+
+ /**
+ * Append an include_once statement to the class file cache
+ *
+ * @param string $incFile
+ * @return void
+ */
+ protected static function _appendIncFile($incFile)
+ {
+ if (!file_exists(self::$_includeFileCache)) {
+ $file = '<?php';
+ } else {
+ $file = file_get_contents(self::$_includeFileCache);
+ }
+ if (!strstr($file, $incFile)) {
+ $file .= "\ninclude_once '$incFile';";
+ file_put_contents(self::$_includeFileCache, $file);
+ }
+ }
+}
diff --git a/lib/Zend/Loader/PluginLoader/Exception.php b/lib/Zend/Loader/PluginLoader/Exception.php
new file mode 100644
index 0000000..60b53a4
--- /dev/null
+++ b/lib/Zend/Loader/PluginLoader/Exception.php
@@ -0,0 +1,38 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Loader
+ * @subpackage PluginLoader
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+/**
+ * @see Zend_Loader_Exception
+ */
+require_once 'Zend/Loader/Exception.php';
+
+/**
+ * Plugin class loader exceptions
+ *
+ * @category Zend
+ * @package Zend_Loader
+ * @subpackage PluginLoader
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Loader_PluginLoader_Exception extends Zend_Loader_Exception
+{
+}
diff --git a/lib/Zend/Loader/PluginLoader/Interface.php b/lib/Zend/Loader/PluginLoader/Interface.php
new file mode 100644
index 0000000..50f34aa
--- /dev/null
+++ b/lib/Zend/Loader/PluginLoader/Interface.php
@@ -0,0 +1,74 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Loader
+ * @subpackage PluginLoader
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+/**
+ * Plugin class loader interface
+ *
+ * @category Zend
+ * @package Zend_Loader
+ * @subpackage PluginLoader
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+interface Zend_Loader_PluginLoader_Interface
+{
+ /**
+ * Add prefixed paths to the registry of paths
+ *
+ * @param string $prefix
+ * @param string $path
+ * @return Zend_Loader_PluginLoader
+ */
+ public function addPrefixPath($prefix, $path);
+
+ /**
+ * Remove a prefix (or prefixed-path) from the registry
+ *
+ * @param string $prefix
+ * @param string $path OPTIONAL
+ * @return Zend_Loader_PluginLoader
+ */
+ public function removePrefixPath($prefix, $path = null);
+
+ /**
+ * Whether or not a Helper by a specific name
+ *
+ * @param string $name
+ * @return Zend_Loader_PluginLoader
+ */
+ public function isLoaded($name);
+
+ /**
+ * Return full class name for a named helper
+ *
+ * @param string $name
+ * @return string
+ */
+ public function getClassName($name);
+
+ /**
+ * Load a helper via the name provided
+ *
+ * @param string $name
+ * @return string
+ */
+ public function load($name);
+}
diff --git a/lib/Zend/Mail.php b/lib/Zend/Mail.php
new file mode 100644
index 0000000..5364f22
--- /dev/null
+++ b/lib/Zend/Mail.php
@@ -0,0 +1,1042 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Mail.php 14071 2009-02-12 22:22:43Z beberlei $
+ */
+
+
+/**
+ * @see Zend_Mail_Transport_Abstract
+ */
+require_once 'Zend/Mail/Transport/Abstract.php';
+
+/**
+ * @see Zend_Mime
+ */
+require_once 'Zend/Mime.php';
+
+/**
+ * @see Zend_Mime_Message
+ */
+require_once 'Zend/Mime/Message.php';
+
+/**
+ * @see Zend_Mime_Part
+ */
+require_once 'Zend/Mime/Part.php';
+
+
+/**
+ * Class for sending an email.
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Mail extends Zend_Mime_Message
+{
+ /**#@+
+ * @access protected
+ */
+
+ /**
+ * @var Zend_Mail_Transport_Abstract
+ * @static
+ */
+ protected static $_defaultTransport = null;
+
+ /**
+ * Mail character set
+ * @var string
+ */
+ protected $_charset = null;
+
+ /**
+ * Mail headers
+ * @var array
+ */
+ protected $_headers = array();
+
+ /**
+ * Encoding of Mail headers
+ * @var string
+ */
+ protected $_headerEncoding = Zend_Mime::ENCODING_QUOTEDPRINTABLE;
+
+ /**
+ * From: address
+ * @var string
+ */
+ protected $_from = null;
+
+ /**
+ * To: addresses
+ * @var array
+ */
+ protected $_to = array();
+
+ /**
+ * Array of all recipients
+ * @var array
+ */
+ protected $_recipients = array();
+
+ /**
+ * Return-Path header
+ * @var string
+ */
+ protected $_returnPath = null;
+
+ /**
+ * Subject: header
+ * @var string
+ */
+ protected $_subject = null;
+
+ /**
+ * Date: header
+ * @var string
+ */
+ protected $_date = null;
+
+ /**
+ * Message-ID: header
+ * @var string
+ */
+ protected $_messageId = null;
+
+ /**
+ * text/plain MIME part
+ * @var false|Zend_Mime_Part
+ */
+ protected $_bodyText = false;
+
+ /**
+ * text/html MIME part
+ * @var false|Zend_Mime_Part
+ */
+ protected $_bodyHtml = false;
+
+ /**
+ * MIME boundary string
+ * @var string
+ */
+ protected $_mimeBoundary = null;
+
+ /**
+ * Content type of the message
+ * @var string
+ */
+ protected $_type = null;
+
+ /**#@-*/
+
+ /**
+ * Flag: whether or not email has attachments
+ * @var boolean
+ */
+ public $hasAttachments = false;
+
+
+ /**
+ * Sets the default mail transport for all following uses of
+ * Zend_Mail::send();
+ *
+ * @todo Allow passing a string to indicate the transport to load
+ * @todo Allow passing in optional options for the transport to load
+ * @param Zend_Mail_Transport_Abstract $transport
+ */
+ public static function setDefaultTransport(Zend_Mail_Transport_Abstract $transport)
+ {
+ self::$_defaultTransport = $transport;
+ }
+
+ /**
+ * Public constructor
+ *
+ * @param string $charset
+ */
+ public function __construct($charset = 'iso-8859-1')
+ {
+ $this->_charset = $charset;
+ }
+
+ /**
+ * Return charset string
+ *
+ * @return string
+ */
+ public function getCharset()
+ {
+ return $this->_charset;
+ }
+
+ /**
+ * Set content type
+ *
+ * Should only be used for manually setting multipart content types.
+ *
+ * @param string $type Content type
+ * @return Zend_Mail Implements fluent interface
+ * @throws Zend_Mail_Exception for types not supported by Zend_Mime
+ */
+ public function setType($type)
+ {
+ $allowed = array(
+ Zend_Mime::MULTIPART_ALTERNATIVE,
+ Zend_Mime::MULTIPART_MIXED,
+ Zend_Mime::MULTIPART_RELATED,
+ );
+ if (!in_array($type, $allowed)) {
+ /**
+ * @see Zend_Mail_Exception
+ */
+ require_once 'Zend/Mail/Exception.php';
+ throw new Zend_Mail_Exception('Invalid content type "' . $type . '"');
+ }
+
+ $this->_type = $type;
+ return $this;
+ }
+
+ /**
+ * Get content type of the message
+ *
+ * @return string
+ */
+ public function getType()
+ {
+ return $this->_type;
+ }
+
+ /**
+ * Set an arbitrary mime boundary for the message
+ *
+ * If not set, Zend_Mime will generate one.
+ *
+ * @param string $boundary
+ * @return Zend_Mail Provides fluent interface
+ */
+ public function setMimeBoundary($boundary)
+ {
+ $this->_mimeBoundary = $boundary;
+
+ return $this;
+ }
+
+ /**
+ * Return the boundary string used for the message
+ *
+ * @return string
+ */
+ public function getMimeBoundary()
+ {
+ return $this->_mimeBoundary;
+ }
+
+ /**
+ * Return encoding of mail headers
+ *
+ * @deprecated use {@link getHeaderEncoding()} instead
+ * @return <type>
+ */
+ public function getEncodingOfHeaders()
+ {
+ return $this->getHeaderEncoding();
+ }
+
+ /**
+ * Return the encoding of mail headers
+ *
+ * Either Zend_Mime::ENCODING_QUOTEDPRINTABLE or Zend_Mime::ENCODING_BASE64
+ *
+ * @return string
+ */
+ public function getHeaderEncoding()
+ {
+ return $this->_headerEncoding;
+ }
+
+ /**
+ * Set the encoding of mail headers
+ *
+ * @deprecated Use {@link setHeaderEncoding()} instead.
+ * @param string $encoding
+ * @return Zend_Mail
+ */
+ public function setEncodingOfHeaders($encoding)
+ {
+ return $this->setHeaderEncoding($encoding);
+ }
+
+ /**
+ * Set the encoding of mail headers
+ *
+ * @param string $encoding Zend_Mime::ENCODING_QUOTEDPRINTABLE or Zend_Mime::ENCODING_BASE64
+ * @return Zend_Mail Provides fluent interface
+ */
+ public function setHeaderEncoding($encoding)
+ {
+ $allowed = array(
+ Zend_Mime::ENCODING_BASE64,
+ Zend_Mime::ENCODING_QUOTEDPRINTABLE
+ );
+ if (!in_array($encoding, $allowed)) {
+ /**
+ * @see Zend_Mail_Exception
+ */
+ require_once 'Zend/Mail/Exception.php';
+ throw new Zend_Mail_Exception('Invalid encoding "' . $encoding . '"');
+ }
+ $this->_headerEncoding = $encoding;
+
+ return $this;
+ }
+
+ /**
+ * Sets the text body for the message.
+ *
+ * @param string $txt
+ * @param string $charset
+ * @param string $encoding
+ * @return Zend_Mail Provides fluent interface
+ */
+ public function setBodyText($txt, $charset = null, $encoding = Zend_Mime::ENCODING_QUOTEDPRINTABLE)
+ {
+ if ($charset === null) {
+ $charset = $this->_charset;
+ }
+
+ $mp = new Zend_Mime_Part($txt);
+ $mp->encoding = $encoding;
+ $mp->type = Zend_Mime::TYPE_TEXT;
+ $mp->disposition = Zend_Mime::DISPOSITION_INLINE;
+ $mp->charset = $charset;
+
+ $this->_bodyText = $mp;
+
+ return $this;
+ }
+
+ /**
+ * Return text body Zend_Mime_Part or string
+ *
+ * @param bool textOnly Whether to return just the body text content or the MIME part; defaults to false, the MIME part
+ * @return false|Zend_Mime_Part|string
+ */
+ public function getBodyText($textOnly = false)
+ {
+ if ($textOnly && $this->_bodyText) {
+ $body = $this->_bodyText;
+ return $body->getContent();
+ }
+
+ return $this->_bodyText;
+ }
+
+ /**
+ * Sets the HTML body for the message
+ *
+ * @param string $html
+ * @param string $charset
+ * @param string $encoding
+ * @return Zend_Mail Provides fluent interface
+ */
+ public function setBodyHtml($html, $charset = null, $encoding = Zend_Mime::ENCODING_QUOTEDPRINTABLE)
+ {
+ if ($charset === null) {
+ $charset = $this->_charset;
+ }
+
+ $mp = new Zend_Mime_Part($html);
+ $mp->encoding = $encoding;
+ $mp->type = Zend_Mime::TYPE_HTML;
+ $mp->disposition = Zend_Mime::DISPOSITION_INLINE;
+ $mp->charset = $charset;
+
+ $this->_bodyHtml = $mp;
+
+ return $this;
+ }
+
+ /**
+ * Return Zend_Mime_Part representing body HTML
+ *
+ * @param bool $htmlOnly Whether to return the body HTML only, or the MIME part; defaults to false, the MIME part
+ * @return false|Zend_Mime_Part|string
+ */
+ public function getBodyHtml($htmlOnly = false)
+ {
+ if ($htmlOnly && $this->_bodyHtml) {
+ $body = $this->_bodyHtml;
+ return $body->getContent();
+ }
+
+ return $this->_bodyHtml;
+ }
+
+ /**
+ * Adds an existing attachment to the mail message
+ *
+ * @param Zend_Mime_Part $attachment
+ * @return Zend_Mail Provides fluent interface
+ */
+ public function addAttachment(Zend_Mime_Part $attachment)
+ {
+ $this->addPart($attachment);
+ $this->hasAttachments = true;
+
+ return $this;
+ }
+
+ /**
+ * Creates a Zend_Mime_Part attachment
+ *
+ * Attachment is automatically added to the mail object after creation. The
+ * attachment object is returned to allow for further manipulation.
+ *
+ * @param string $body
+ * @param string $mimeType
+ * @param string $disposition
+ * @param string $encoding
+ * @param string $filename OPTIONAL A filename for the attachment
+ * @return Zend_Mime_Part Newly created Zend_Mime_Part object (to allow
+ * advanced settings)
+ */
+ public function createAttachment($body,
+ $mimeType = Zend_Mime::TYPE_OCTETSTREAM,
+ $disposition = Zend_Mime::DISPOSITION_ATTACHMENT,
+ $encoding = Zend_Mime::ENCODING_BASE64,
+ $filename = null)
+ {
+
+ $mp = new Zend_Mime_Part($body);
+ $mp->encoding = $encoding;
+ $mp->type = $mimeType;
+ $mp->disposition = $disposition;
+ $mp->filename = $filename;
+
+ $this->addAttachment($mp);
+
+ return $mp;
+ }
+
+ /**
+ * Return a count of message parts
+ *
+ * @return integer
+ */
+ public function getPartCount()
+ {
+ return count($this->_parts);
+ }
+
+ /**
+ * Encode header fields
+ *
+ * Encodes header content according to RFC1522 if it contains non-printable
+ * characters.
+ *
+ * @param string $value
+ * @return string
+ */
+ protected function _encodeHeader($value)
+ {
+ if (Zend_Mime::isPrintable($value) === false) {
+ if ($this->getHeaderEncoding() === Zend_Mime::ENCODING_QUOTEDPRINTABLE) {
+ $value = Zend_Mime::encodeQuotedPrintableHeader($value, $this->getCharset(), Zend_Mime::LINELENGTH, Zend_Mime::LINEEND);
+ } else {
+ $value = Zend_Mime::encodeBase64Header($value, $this->getCharset(), Zend_Mime::LINELENGTH, Zend_Mime::LINEEND);
+ }
+ }
+
+ return $value;
+ }
+
+ /**
+ * Add a header to the message
+ *
+ * Adds a header to this message. If append is true and the header already
+ * exists, raises a flag indicating that the header should be appended.
+ *
+ * @param string $headerName
+ * @param string $value
+ * @param bool $append
+ */
+ protected function _storeHeader($headerName, $value, $append = false)
+ {
+ if (isset($this->_headers[$headerName])) {
+ $this->_headers[$headerName][] = $value;
+ } else {
+ $this->_headers[$headerName] = array($value);
+ }
+
+ if ($append) {
+ $this->_headers[$headerName]['append'] = true;
+ }
+
+ }
+
+ /**
+ * Clear header from the message
+ *
+ * @param string $headerName
+ */
+ protected function _clearHeader($headerName)
+ {
+ if (isset($this->_headers[$headerName])){
+ unset($this->_headers[$headerName]);
+ }
+ }
+
+ /**
+ * Helper function for adding a recipient and the corresponding header
+ *
+ * @param string $headerName
+ * @param string $email
+ * @param string $name
+ */
+ protected function _addRecipientAndHeader($headerName, $email, $name)
+ {
+ $email = $this->_filterEmail($email);
+ $name = $this->_filterName($name);
+ // prevent duplicates
+ $this->_recipients[$email] = 1;
+ $this->_storeHeader($headerName, $this->_formatAddress($email, $name), true);
+ }
+
+ /**
+ * Adds To-header and recipient
+ *
+ * @param string $email
+ * @param string $name
+ * @return Zend_Mail Provides fluent interface
+ */
+ public function addTo($email, $name='')
+ {
+ $this->_addRecipientAndHeader('To', $email, $name);
+ $this->_to[] = $email;
+ return $this;
+ }
+
+ /**
+ * Adds Cc-header and recipient
+ *
+ * @param string $email
+ * @param string $name
+ * @return Zend_Mail Provides fluent interface
+ */
+ public function addCc($email, $name='')
+ {
+ $this->_addRecipientAndHeader('Cc', $email, $name);
+ return $this;
+ }
+
+ /**
+ * Adds Bcc recipient
+ *
+ * @param string $email
+ * @return Zend_Mail Provides fluent interface
+ */
+ public function addBcc($email)
+ {
+ $this->_addRecipientAndHeader('Bcc', $email, '');
+ return $this;
+ }
+
+ /**
+ * Return list of recipient email addresses
+ *
+ * @return array (of strings)
+ */
+ public function getRecipients()
+ {
+ return array_keys($this->_recipients);
+ }
+
+ /**
+ * Clears list of recipient email addresses
+ *
+ * @return Zend_Mail Provides fluent interface
+ */
+ public function clearRecipients()
+ {
+ $this->_recipients = array();
+ $this->_to = array();
+
+ $this->_clearHeader('To');
+ $this->_clearHeader('Cc');
+ $this->_clearHeader('Bcc');
+
+ return $this;
+ }
+
+ /**
+ * Sets From-header and sender of the message
+ *
+ * @param string $email
+ * @param string $name
+ * @return Zend_Mail Provides fluent interface
+ * @throws Zend_Mail_Exception if called subsequent times
+ */
+ public function setFrom($email, $name = null)
+ {
+ if ($this->_from === null) {
+ $email = $this->_filterEmail($email);
+ $name = $this->_filterName($name);
+ $this->_from = $email;
+ $this->_storeHeader('From', $this->_formatAddress($email, $name), true);
+ } else {
+ /**
+ * @see Zend_Mail_Exception
+ */
+ require_once 'Zend/Mail/Exception.php';
+ throw new Zend_Mail_Exception('From Header set twice');
+ }
+ return $this;
+ }
+
+ /**
+ * Returns the sender of the mail
+ *
+ * @return string
+ */
+ public function getFrom()
+ {
+ return $this->_from;
+ }
+
+ /**
+ * Clears the sender from the mail
+ *
+ * @return Zend_Mail Provides fluent interface
+ */
+ public function clearFrom()
+ {
+ $this->_from = null;
+ $this->_clearHeader('From');
+
+ return $this;
+ }
+
+ /**
+ * Sets the Return-Path header of the message
+ *
+ * @param string $email
+ * @return Zend_Mail Provides fluent interface
+ * @throws Zend_Mail_Exception if set multiple times
+ */
+ public function setReturnPath($email)
+ {
+ if ($this->_returnPath === null) {
+ $email = $this->_filterEmail($email);
+ $this->_returnPath = $email;
+ $this->_storeHeader('Return-Path', $email, false);
+ } else {
+ /**
+ * @see Zend_Mail_Exception
+ */
+ require_once 'Zend/Mail/Exception.php';
+ throw new Zend_Mail_Exception('Return-Path Header set twice');
+ }
+ return $this;
+ }
+
+ /**
+ * Returns the current Return-Path address of the message
+ *
+ * If no Return-Path header is set, returns the value of {@link $_from}.
+ *
+ * @return string
+ */
+ public function getReturnPath()
+ {
+ if (null !== $this->_returnPath) {
+ return $this->_returnPath;
+ }
+
+ return $this->_from;
+ }
+
+ /**
+ * Clears the current Return-Path address from the message
+ *
+ * @return Zend_Mail Provides fluent interface
+ */
+ public function clearReturnPath()
+ {
+ $this->_returnPath = null;
+ $this->_clearHeader('Return-Path');
+
+ return $this;
+ }
+
+ /**
+ * Sets the subject of the message
+ *
+ * @param string $subject
+ * @return Zend_Mail Provides fluent interface
+ * @throws Zend_Mail_Exception
+ */
+ public function setSubject($subject)
+ {
+ if ($this->_subject === null) {
+ $subject = $this->_filterOther($subject);
+ $this->_subject = $this->_encodeHeader($subject);
+ $this->_storeHeader('Subject', $this->_subject);
+ } else {
+ /**
+ * @see Zend_Mail_Exception
+ */
+ require_once 'Zend/Mail/Exception.php';
+ throw new Zend_Mail_Exception('Subject set twice');
+ }
+ return $this;
+ }
+
+ /**
+ * Returns the encoded subject of the message
+ *
+ * @return string
+ */
+ public function getSubject()
+ {
+ return $this->_subject;
+ }
+
+ /**
+ * Clears the encoded subject from the message
+ *
+ * @return Zend_Mail Provides fluent interface
+ */
+ public function clearSubject()
+ {
+ $this->_subject = null;
+ $this->_clearHeader('Subject');
+
+ return $this;
+ }
+
+ /**
+ * Sets Date-header
+ *
+ * @param string $date
+ * @return Zend_Mail Provides fluent interface
+ * @throws Zend_Mail_Exception if called subsequent times
+ */
+ public function setDate($date = null)
+ {
+ if ($this->_date === null) {
+ if ($date === null) {
+ $date = date('r');
+ } else if (is_int($date)) {
+ $date = date('r', $date);
+ } else if (is_string($date)) {
+ $date = strtotime($date);
+ if ($date === false || $date < 0) {
+ /**
+ * @see Zend_Mail_Exception
+ */
+ require_once 'Zend/Mail/Exception.php';
+ throw new Zend_Mail_Exception('String representations of Date Header must be ' .
+ 'strtotime()-compatible');
+ }
+ $date = date('r', $date);
+ } else if ($date instanceof Zend_Date) {
+ $date = $date->get(Zend_Date::RFC_2822);
+ } else {
+ /**
+ * @see Zend_Mail_Exception
+ */
+ require_once 'Zend/Mail/Exception.php';
+ throw new Zend_Mail_Exception(__METHOD__ . ' only accepts UNIX timestamps, Zend_Date objects, ' .
+ ' and strtotime()-compatible strings');
+ }
+ $this->_date = $date;
+ $this->_storeHeader('Date', $date);
+ } else {
+ /**
+ * @see Zend_Mail_Exception
+ */
+ require_once 'Zend/Mail/Exception.php';
+ throw new Zend_Mail_Exception('Date Header set twice');
+ }
+ return $this;
+ }
+
+ /**
+ * Returns the formatted date of the message
+ *
+ * @return string
+ */
+ public function getDate()
+ {
+ return $this->_date;
+ }
+
+ /**
+ * Clears the formatted date from the message
+ *
+ * @return Zend_Mail Provides fluent interface
+ */
+ public function clearDate()
+ {
+ $this->_date = null;
+ $this->_clearHeader('Date');
+
+ return $this;
+ }
+
+ /**
+ * Sets the Message-ID of the message
+ *
+ * @param boolean|string $id
+ * true :Auto
+ * false :No set
+ * null :No set
+ * string:Sets string
+ * @return Zend_Mail Provides fluent interface
+ * @throws Zend_Mail_Exception
+ */
+ public function setMessageId($id = true)
+ {
+ if ($id === null || $id === false) {
+ return $this;
+ } elseif ($id === true) {
+ $id = $this->createMessageId();
+ }
+
+ if ($this->_messageId === null) {
+ $id = $this->_filterOther($id);
+ $this->_messageId = $id;
+ $this->_storeHeader('Message-Id', $this->_messageId);
+ } else {
+ /**
+ * @see Zend_Mail_Exception
+ */
+ require_once 'Zend/Mail/Exception.php';
+ throw new Zend_Mail_Exception('Message-ID set twice');
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns the Message-ID of the message
+ *
+ * @return string
+ */
+ public function getMessageId()
+ {
+ return $this->_messageId;
+ }
+
+
+ /**
+ * Clears the Message-ID from the message
+ *
+ * @return Zend_Mail Provides fluent interface
+ */
+ public function clearMessageId()
+ {
+ $this->_messageId = null;
+ $this->_clearHeader('Message-Id');
+
+ return $this;
+ }
+
+ /**
+ * Creates the Message-ID
+ *
+ * @return string
+ */
+ public function createMessageId() {
+
+ $time = time();
+
+ if ($this->_from !== null) {
+ $user = $this->_from;
+ } elseif (isset($_SERVER['REMOTE_ADDR'])) {
+ $user = $_SERVER['REMOTE_ADDR'];
+ } else {
+ $user = getmypid();
+ }
+
+ $rand = mt_rand();
+
+ if ($this->_recipients !== array()) {
+ $recipient = array_rand($this->_recipients);
+ } else {
+ $recipient = 'unknown';
+ }
+
+ if (isset($_SERVER["SERVER_NAME"])) {
+ $hostName = $_SERVER["SERVER_NAME"];
+ } else {
+ $hostName = php_uname('n');
+ }
+
+ return sha1($time . $user . $rand . $recipient) . '@' . $hostName;
+ }
+
+ /**
+ * Add a custom header to the message
+ *
+ * @param string $name
+ * @param string $value
+ * @param boolean $append
+ * @return Zend_Mail Provides fluent interface
+ * @throws Zend_Mail_Exception on attempts to create standard headers
+ */
+ public function addHeader($name, $value, $append = false)
+ {
+ $prohibit = array('to', 'cc', 'bcc', 'from', 'subject',
+ 'return-path', 'date', 'message-id',
+ );
+ if (in_array(strtolower($name), $prohibit)) {
+ /**
+ * @see Zend_Mail_Exception
+ */
+ require_once 'Zend/Mail/Exception.php';
+ throw new Zend_Mail_Exception('Cannot set standard header from addHeader()');
+ }
+
+ $value = $this->_filterOther($value);
+ $value = $this->_encodeHeader($value);
+ $this->_storeHeader($name, $value, $append);
+
+ return $this;
+ }
+
+ /**
+ * Return mail headers
+ *
+ * @return void
+ */
+ public function getHeaders()
+ {
+ return $this->_headers;
+ }
+
+ /**
+ * Sends this email using the given transport or a previously
+ * set DefaultTransport or the internal mail function if no
+ * default transport had been set.
+ *
+ * @param Zend_Mail_Transport_Abstract $transport
+ * @return Zend_Mail Provides fluent interface
+ */
+ public function send($transport = null)
+ {
+ if ($transport === null) {
+ if (! self::$_defaultTransport instanceof Zend_Mail_Transport_Abstract) {
+ require_once 'Zend/Mail/Transport/Sendmail.php';
+ $transport = new Zend_Mail_Transport_Sendmail();
+ } else {
+ $transport = self::$_defaultTransport;
+ }
+ }
+
+ if ($this->_date === null) {
+ $this->setDate();
+ }
+
+ $transport->send($this);
+
+ return $this;
+ }
+
+ /**
+ * Filter of email data
+ *
+ * @param string $email
+ * @return string
+ */
+ protected function _filterEmail($email)
+ {
+ $rule = array("\r" => '',
+ "\n" => '',
+ "\t" => '',
+ '"' => '',
+ ',' => '',
+ '<' => '',
+ '>' => '',
+ );
+
+ return strtr($email, $rule);
+ }
+
+ /**
+ * Filter of name data
+ *
+ * @param string $name
+ * @return string
+ */
+ protected function _filterName($name)
+ {
+ $rule = array("\r" => '',
+ "\n" => '',
+ "\t" => '',
+ '"' => "'",
+ '<' => '[',
+ '>' => ']',
+ );
+
+ return trim(strtr($name, $rule));
+ }
+
+ /**
+ * Filter of other data
+ *
+ * @param string $data
+ * @return string
+ */
+ protected function _filterOther($data)
+ {
+ $rule = array("\r" => '',
+ "\n" => '',
+ "\t" => '',
+ );
+
+ return strtr($data, $rule);
+ }
+
+ /**
+ * Formats e-mail address
+ *
+ * @param string $email
+ * @param string $name
+ * @return string
+ */
+ protected function _formatAddress($email, $name)
+ {
+ if ($name === '' || $name === null || $name === $email) {
+ return $email;
+ } else {
+ $encodedName = $this->_encodeHeader($name);
+ if ($encodedName === $name && strpos($name, ',') !== false) {
+ $format = '"%s" <%s>';
+ } else {
+ $format = '%s <%s>';
+ }
+ return sprintf($format, $encodedName, $email);
+ }
+ }
+
+}
diff --git a/lib/Zend/Mail/Exception.php b/lib/Zend/Mail/Exception.php
new file mode 100644
index 0000000..bab758e
--- /dev/null
+++ b/lib/Zend/Mail/Exception.php
@@ -0,0 +1,37 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Exception.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Exception
+ */
+require_once 'Zend/Exception.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Mail
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Mail_Exception extends Zend_Exception
+{}
+
diff --git a/lib/Zend/Mail/Message.php b/lib/Zend/Mail/Message.php
new file mode 100644
index 0000000..787df98
--- /dev/null
+++ b/lib/Zend/Mail/Message.php
@@ -0,0 +1,112 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Message.php 9099 2008-03-30 19:35:47Z thomas $
+ */
+
+
+/**
+ * Zend_Mail_Part
+ */
+require_once 'Zend/Mail/Part.php';
+
+/**
+ * Zend_Mail_Message_Interface
+ */
+require_once 'Zend/Mail/Message/Interface.php';
+
+/**
+ * @category Zend
+ * @package Zend_Mail
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Mail_Message extends Zend_Mail_Part implements Zend_Mail_Message_Interface
+{
+ /**
+ * flags for this message
+ * @var array
+ */
+ protected $_flags = array();
+
+ /**
+ * Public constructor
+ *
+ * In addition to the parameters of Zend_Mail_Part::__construct() this constructor supports:
+ * - file filename or file handle of a file with raw message content
+ * - flags array with flags for message, keys are ignored, use constants defined in Zend_Mail_Storage
+ *
+ * @param string $rawMessage full message with or without headers
+ * @throws Zend_Mail_Exception
+ */
+ public function __construct(array $params)
+ {
+ if (isset($params['file'])) {
+ if (!is_resource($params['file'])) {
+ $params['raw'] = @file_get_contents($params['file']);
+ if ($params['raw'] === false) {
+ /**
+ * @see Zend_Mail_Exception
+ */
+ require_once 'Zend/Mail/Exception.php';
+ throw new Zend_Mail_Exception('could not open file');
+ }
+ } else {
+ $params['raw'] = stream_get_contents($params['file']);
+ }
+ }
+
+ if (!empty($params['flags'])) {
+ // set key and value to the same value for easy lookup
+ $this->_flags = array_combine($params['flags'], $params['flags']);
+ }
+
+ parent::__construct($params);
+ }
+
+ /**
+ * return toplines as found after headers
+ *
+ * @return string toplines
+ */
+ public function getTopLines()
+ {
+ return $this->_topLines;
+ }
+
+ /**
+ * check if flag is set
+ *
+ * @param mixed $flag a flag name, use constants defined in Zend_Mail_Storage
+ * @return bool true if set, otherwise false
+ */
+ public function hasFlag($flag)
+ {
+ return isset($this->_flags[$flag]);
+ }
+
+ /**
+ * get all set flags
+ *
+ * @return array array with flags, key and value are the same for easy lookup
+ */
+ public function getFlags()
+ {
+ return $this->_flags;
+ }
+}
diff --git a/lib/Zend/Mail/Message/File.php b/lib/Zend/Mail/Message/File.php
new file mode 100644
index 0000000..5a93449
--- /dev/null
+++ b/lib/Zend/Mail/Message/File.php
@@ -0,0 +1,96 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Message.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * Zend_Mail_Part
+ */
+require_once 'Zend/Mail/Part/File.php';
+
+/**
+ * Zend_Mail_Message_Interface
+ */
+require_once 'Zend/Mail/Message/Interface.php';
+
+/**
+ * @category Zend
+ * @package Zend_Mail
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Mail_Message_File extends Zend_Mail_Part_File implements Zend_Mail_Message_Interface
+{
+ /**
+ * flags for this message
+ * @var array
+ */
+ protected $_flags = array();
+
+ /**
+ * Public constructor
+ *
+ * In addition to the parameters of Zend_Mail_Part::__construct() this constructor supports:
+ * - flags array with flags for message, keys are ignored, use constants defined in Zend_Mail_Storage
+ *
+ * @param string $rawMessage full message with or without headers
+ * @throws Zend_Mail_Exception
+ */
+ public function __construct(array $params)
+ {
+ if (!empty($params['flags'])) {
+ // set key and value to the same value for easy lookup
+ $this->_flags = array_combine($params['flags'], $params['flags']);
+ }
+
+ parent::__construct($params);
+ }
+
+ /**
+ * return toplines as found after headers
+ *
+ * @return string toplines
+ */
+ public function getTopLines()
+ {
+ return $this->_topLines;
+ }
+
+ /**
+ * check if flag is set
+ *
+ * @param mixed $flag a flag name, use constants defined in Zend_Mail_Storage
+ * @return bool true if set, otherwise false
+ */
+ public function hasFlag($flag)
+ {
+ return isset($this->_flags[$flag]);
+ }
+
+ /**
+ * get all set flags
+ *
+ * @return array array with flags, key and value are the same for easy lookup
+ */
+ public function getFlags()
+ {
+ return $this->_flags;
+ }
+}
diff --git a/lib/Zend/Mail/Message/Interface.php b/lib/Zend/Mail/Message/Interface.php
new file mode 100644
index 0000000..92acd49
--- /dev/null
+++ b/lib/Zend/Mail/Message/Interface.php
@@ -0,0 +1,55 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Storage
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Interface.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Storage
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+interface Zend_Mail_Message_Interface
+{
+ /**
+ * return toplines as found after headers
+ *
+ * @return string toplines
+ */
+ public function getTopLines();
+
+ /**
+ * check if flag is set
+ *
+ * @param mixed $flag a flag name, use constants defined in Zend_Mail_Storage
+ * @return bool true if set, otherwise false
+ */
+ public function hasFlag($flag);
+
+ /**
+ * get all set flags
+ *
+ * @return array array with flags, key and value are the same for easy lookup
+ */
+ public function getFlags();
+} \ No newline at end of file
diff --git a/lib/Zend/Mail/Part.php b/lib/Zend/Mail/Part.php
new file mode 100644
index 0000000..c2f6bef
--- /dev/null
+++ b/lib/Zend/Mail/Part.php
@@ -0,0 +1,489 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Part.php 14073 2009-02-12 22:30:40Z beberlei $
+ */
+
+
+/**
+ * @see Zend_Mime_Decode
+ */
+require_once 'Zend/Mime/Decode.php';
+
+/**
+ * @see Zend_Mail_Part_Interface
+ */
+require_once 'Zend/Mail/Part/Interface.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Mail
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Mail_Part implements RecursiveIterator, Zend_Mail_Part_Interface
+{
+ /**
+ * headers of part as array
+ * @var null|array
+ */
+ protected $_headers;
+
+ /**
+ * raw part body
+ * @var null|string
+ */
+ protected $_content;
+
+ /**
+ * toplines as fetched with headers
+ * @var string
+ */
+ protected $_topLines = '';
+
+ /**
+ * parts of multipart message
+ * @var array
+ */
+ protected $_parts = array();
+
+ /**
+ * count of parts of a multipart message
+ * @var null|int
+ */
+ protected $_countParts;
+
+ /**
+ * current position of iterator
+ * @var int
+ */
+ protected $_iterationPos = 1;
+
+ /**
+ * mail handler, if late fetch is active
+ * @var null|Zend_Mail_Storage_Abstract
+ */
+ protected $_mail;
+
+ /**
+ * message number for mail handler
+ * @var int
+ */
+ protected $_messageNum = 0;
+
+ /**
+ * Public constructor
+ *
+ * Zend_Mail_Part supports different sources for content. The possible params are:
+ * - handler a instance of Zend_Mail_Storage_Abstract for late fetch
+ * - id number of message for handler
+ * - raw raw content with header and body as string
+ * - headers headers as array (name => value) or string, if a content part is found it's used as toplines
+ * - noToplines ignore content found after headers in param 'headers'
+ * - content content as string
+ *
+ * @param array $params full message with or without headers
+ * @throws Zend_Mail_Exception
+ */
+ public function __construct(array $params)
+ {
+ if (isset($params['handler'])) {
+ if (!$params['handler'] instanceof Zend_Mail_Storage_Abstract) {
+ /**
+ * @see Zend_Mail_Exception
+ */
+ require_once 'Zend/Mail/Exception.php';
+ throw new Zend_Mail_Exception('handler is not a valid mail handler');
+ }
+ if (!isset($params['id'])) {
+ /**
+ * @see Zend_Mail_Exception
+ */
+ require_once 'Zend/Mail/Exception.php';
+ throw new Zend_Mail_Exception('need a message id with a handler');
+ }
+
+ $this->_mail = $params['handler'];
+ $this->_messageNum = $params['id'];
+ }
+
+ if (isset($params['raw'])) {
+ Zend_Mime_Decode::splitMessage($params['raw'], $this->_headers, $this->_content);
+ } else if (isset($params['headers'])) {
+ if (is_array($params['headers'])) {
+ $this->_headers = $params['headers'];
+ } else {
+ if (!empty($params['noToplines'])) {
+ Zend_Mime_Decode::splitMessage($params['headers'], $this->_headers, $null);
+ } else {
+ Zend_Mime_Decode::splitMessage($params['headers'], $this->_headers, $this->_topLines);
+ }
+ }
+ if (isset($params['content'])) {
+ $this->_content = $params['content'];
+ }
+ }
+ }
+
+ /**
+ * Check if part is a multipart message
+ *
+ * @return bool if part is multipart
+ */
+ public function isMultipart()
+ {
+ try {
+ return stripos($this->contentType, 'multipart/') === 0;
+ } catch(Zend_Mail_Exception $e) {
+ return false;
+ }
+ }
+
+
+ /**
+ * Body of part
+ *
+ * If part is multipart the raw content of this part with all sub parts is returned
+ *
+ * @return string body
+ * @throws Zend_Mail_Exception
+ */
+ public function getContent()
+ {
+ if ($this->_content !== null) {
+ return $this->_content;
+ }
+
+ if ($this->_mail) {
+ return $this->_mail->getRawContent($this->_messageNum);
+ } else {
+ /**
+ * @see Zend_Mail_Exception
+ */
+ require_once 'Zend/Mail/Exception.php';
+ throw new Zend_Mail_Exception('no content');
+ }
+ }
+
+ /**
+ * Return size of part
+ *
+ * Quite simple implemented currently (not decoding). Handle with care.
+ *
+ * @return int size
+ */
+ public function getSize() {
+ return strlen($this->getContent());
+ }
+
+
+ /**
+ * Cache content and split in parts if multipart
+ *
+ * @return null
+ * @throws Zend_Mail_Exception
+ */
+ protected function _cacheContent()
+ {
+ // caching content if we can't fetch parts
+ if ($this->_content === null && $this->_mail) {
+ $this->_content = $this->_mail->getRawContent($this->_messageNum);
+ }
+
+ if (!$this->isMultipart()) {
+ return;
+ }
+
+ // split content in parts
+ $boundary = $this->getHeaderField('content-type', 'boundary');
+ if (!$boundary) {
+ /**
+ * @see Zend_Mail_Exception
+ */
+ require_once 'Zend/Mail/Exception.php';
+ throw new Zend_Mail_Exception('no boundary found in content type to split message');
+ }
+ $parts = Zend_Mime_Decode::splitMessageStruct($this->_content, $boundary);
+ if ($parts === null) {
+ return;
+ }
+ $counter = 1;
+ foreach ($parts as $part) {
+ $this->_parts[$counter++] = new self(array('headers' => $part['header'], 'content' => $part['body']));
+ }
+ }
+
+ /**
+ * Get part of multipart message
+ *
+ * @param int $num number of part starting with 1 for first part
+ * @return Zend_Mail_Part wanted part
+ * @throws Zend_Mail_Exception
+ */
+ public function getPart($num)
+ {
+ if (isset($this->_parts[$num])) {
+ return $this->_parts[$num];
+ }
+
+ if (!$this->_mail && $this->_content === null) {
+ /**
+ * @see Zend_Mail_Exception
+ */
+ require_once 'Zend/Mail/Exception.php';
+ throw new Zend_Mail_Exception('part not found');
+ }
+
+ if ($this->_mail && $this->_mail->hasFetchPart) {
+ // TODO: fetch part
+ // return
+ }
+
+ $this->_cacheContent();
+
+ if (!isset($this->_parts[$num])) {
+ /**
+ * @see Zend_Mail_Exception
+ */
+ require_once 'Zend/Mail/Exception.php';
+ throw new Zend_Mail_Exception('part not found');
+ }
+
+ return $this->_parts[$num];
+ }
+
+ /**
+ * Count parts of a multipart part
+ *
+ * @return int number of sub-parts
+ */
+ public function countParts()
+ {
+ if ($this->_countParts) {
+ return $this->_countParts;
+ }
+
+ $this->_countParts = count($this->_parts);
+ if ($this->_countParts) {
+ return $this->_countParts;
+ }
+
+ if ($this->_mail && $this->_mail->hasFetchPart) {
+ // TODO: fetch part
+ // return
+ }
+
+ $this->_cacheContent();
+
+ $this->_countParts = count($this->_parts);
+ return $this->_countParts;
+ }
+
+
+ /**
+ * Get all headers
+ *
+ * The returned headers are as saved internally. All names are lowercased. The value is a string or an array
+ * if a header with the same name occurs more than once.
+ *
+ * @return array headers as array(name => value)
+ */
+ public function getHeaders()
+ {
+ if ($this->_headers === null) {
+ if (!$this->_mail) {
+ $this->_headers = array();
+ } else {
+ $part = $this->_mail->getRawHeader($this->_messageNum);
+ Zend_Mime_Decode::splitMessage($part, $this->_headers, $null);
+ }
+ }
+
+ return $this->_headers;
+ }
+
+ /**
+ * Get a header in specificed format
+ *
+ * Internally headers that occur more than once are saved as array, all other as string. If $format
+ * is set to string implode is used to concat the values (with Zend_Mime::LINEEND as delim).
+ *
+ * @param string $name name of header, matches case-insensitive, but camel-case is replaced with dashes
+ * @param string $format change type of return value to 'string' or 'array'
+ * @return string|array value of header in wanted or internal format
+ * @throws Zend_Mail_Exception
+ */
+ public function getHeader($name, $format = null)
+ {
+ if ($this->_headers === null) {
+ $this->getHeaders();
+ }
+
+ $lowerName = strtolower($name);
+
+ if (!isset($this->_headers[$lowerName])) {
+ $lowerName = strtolower(preg_replace('%([a-z])([A-Z])%', '\1-\2', $name));
+ if (!isset($this->_headers[$lowerName])) {
+ /**
+ * @see Zend_Mail_Exception
+ */
+ require_once 'Zend/Mail/Exception.php';
+ throw new Zend_Mail_Exception("no Header with Name $name found");
+ }
+ }
+ $name = $lowerName;
+
+ $header = $this->_headers[$name];
+
+ switch ($format) {
+ case 'string':
+ if (is_array($header)) {
+ $header = implode(Zend_Mime::LINEEND, $header);
+ }
+ break;
+ case 'array':
+ $header = (array)$header;
+ default:
+ // do nothing
+ }
+
+ return $header;
+ }
+
+ /**
+ * Get a specific field from a header like content type or all fields as array
+ *
+ * If the header occurs more than once, only the value from the first header
+ * is returned.
+ *
+ * Throws a Zend_Mail_Exception if the requested header does not exist. If
+ * the specific header field does not exist, returns null.
+ *
+ * @param string $name name of header, like in getHeader()
+ * @param string $wantedPart the wanted part, default is first, if null an array with all parts is returned
+ * @param string $firstName key name for the first part
+ * @return string|array wanted part or all parts as array($firstName => firstPart, partname => value)
+ * @throws Zend_Exception, Zend_Mail_Exception
+ */
+ public function getHeaderField($name, $wantedPart = 0, $firstName = 0) {
+ return Zend_Mime_Decode::splitHeaderField(current($this->getHeader($name, 'array')), $wantedPart, $firstName);
+ }
+
+
+ /**
+ * Getter for mail headers - name is matched in lowercase
+ *
+ * This getter is short for Zend_Mail_Part::getHeader($name, 'string')
+ *
+ * @see Zend_Mail_Part::getHeader()
+ *
+ * @param string $name header name
+ * @return string value of header
+ * @throws Zend_Mail_Exception
+ */
+ public function __get($name)
+ {
+ return $this->getHeader($name, 'string');
+ }
+
+ /**
+ * magic method to get content of part
+ *
+ * @return string content
+ */
+ public function __toString()
+ {
+ return $this->getContent();
+ }
+
+ /**
+ * implements RecursiveIterator::hasChildren()
+ *
+ * @return bool current element has children/is multipart
+ */
+ public function hasChildren()
+ {
+ $current = $this->current();
+ return $current && $current instanceof Zend_Mail_Part && $current->isMultipart();
+ }
+
+ /**
+ * implements RecursiveIterator::getChildren()
+ *
+ * @return Zend_Mail_Part same as self::current()
+ */
+ public function getChildren()
+ {
+ return $this->current();
+ }
+
+ /**
+ * implements Iterator::valid()
+ *
+ * @return bool check if there's a current element
+ */
+ public function valid()
+ {
+ if ($this->_countParts === null) {
+ $this->countParts();
+ }
+ return $this->_iterationPos && $this->_iterationPos <= $this->_countParts;
+ }
+
+ /**
+ * implements Iterator::next()
+ *
+ * @return null
+ */
+ public function next()
+ {
+ ++$this->_iterationPos;
+ }
+
+ /**
+ * implements Iterator::key()
+ *
+ * @return string key/number of current part
+ */
+ public function key()
+ {
+ return $this->_iterationPos;
+ }
+
+ /**
+ * implements Iterator::current()
+ *
+ * @return Zend_Mail_Part current part
+ */
+ public function current()
+ {
+ return $this->getPart($this->_iterationPos);
+ }
+
+ /**
+ * implements Iterator::rewind()
+ *
+ * @return null
+ */
+ public function rewind()
+ {
+ $this->countParts();
+ $this->_iterationPos = 1;
+ }
+}
diff --git a/lib/Zend/Mail/Part/File.php b/lib/Zend/Mail/Part/File.php
new file mode 100644
index 0000000..6ff5147
--- /dev/null
+++ b/lib/Zend/Mail/Part/File.php
@@ -0,0 +1,198 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Part.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Mime_Decode
+ */
+require_once 'Zend/Mime/Decode.php';
+
+/**
+ * @see Zend_Mail_Part
+ */
+require_once 'Zend/Mail/Part.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Mail
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Mail_Part_File extends Zend_Mail_Part
+{
+ protected $_contentPos = array();
+ protected $_partPos = array();
+ protected $_fh;
+
+ /**
+ * Public constructor
+ *
+ * This handler supports the following params:
+ * - file filename or open file handler with message content (required)
+ * - startPos start position of message or part in file (default: current position)
+ * - endPos end position of message or part in file (default: end of file)
+ *
+ * @param array $params full message with or without headers
+ * @throws Zend_Mail_Exception
+ */
+ public function __construct(array $params)
+ {
+ if (empty($params['file'])) {
+ /**
+ * @see Zend_Mail_Exception
+ */
+ require_once 'Zend/Mail/Exception.php';
+ throw new Zend_Mail_Exception('no file given in params');
+ }
+
+ if (!is_resource($params['file'])) {
+ $this->_fh = fopen($params['file'], 'r');
+ } else {
+ $this->_fh = $params['file'];
+ }
+ if (!$this->_fh) {
+ /**
+ * @see Zend_Mail_Exception
+ */
+ require_once 'Zend/Mail/Exception.php';
+ throw new Zend_Mail_Exception('could not open file');
+ }
+ if (isset($params['startPos'])) {
+ fseek($this->_fh, $params['startPos']);
+ }
+ $header = '';
+ $endPos = isset($params['endPos']) ? $params['endPos'] : null;
+ while (($endPos === null || ftell($this->_fh) < $endPos) && trim($line = fgets($this->_fh))) {
+ $header .= $line;
+ }
+
+ Zend_Mime_Decode::splitMessage($header, $this->_headers, $null);
+
+ $this->_contentPos[0] = ftell($this->_fh);
+ if ($endPos !== null) {
+ $this->_contentPos[1] = $endPos;
+ } else {
+ fseek($this->_fh, 0, SEEK_END);
+ $this->_contentPos[1] = ftell($this->_fh);
+ }
+ if (!$this->isMultipart()) {
+ return;
+ }
+
+ $boundary = $this->getHeaderField('content-type', 'boundary');
+ if (!$boundary) {
+ /**
+ * @see Zend_Mail_Exception
+ */
+ require_once 'Zend/Mail/Exception.php';
+ throw new Zend_Mail_Exception('no boundary found in content type to split message');
+ }
+
+ $part = array();
+ $pos = $this->_contentPos[0];
+ fseek($this->_fh, $pos);
+ while (!feof($this->_fh) && ($endPos === null || $pos < $endPos)) {
+ $line = fgets($this->_fh);
+ if ($line === false) {
+ if (feof($this->_fh)) {
+ break;
+ }
+ /**
+ * @see Zend_Mail_Exception
+ */
+ require_once 'Zend/Mail/Exception.php';
+ throw new Zend_Mail_Exception('error reading file');
+ }
+
+ $lastPos = $pos;
+ $pos = ftell($this->_fh);
+ $line = trim($line);
+
+ if ($line == '--' . $boundary) {
+ if ($part) {
+ // not first part
+ $part[1] = $lastPos;
+ $this->_partPos[] = $part;
+ }
+ $part = array($pos);
+ } else if ($line == '--' . $boundary . '--') {
+ $part[1] = $lastPos;
+ $this->_partPos[] = $part;
+ break;
+ }
+ }
+ $this->_countParts = count($this->_partPos);
+
+ }
+
+
+ /**
+ * Body of part
+ *
+ * If part is multipart the raw content of this part with all sub parts is returned
+ *
+ * @return string body
+ * @throws Zend_Mail_Exception
+ */
+ public function getContent($stream = null)
+ {
+ fseek($this->_fh, $this->_contentPos[0]);
+ if ($stream !== null) {
+ return stream_copy_to_stream($this->_fh, $stream, $this->_contentPos[1] - $this->_contentPos[0]);
+ }
+ $length = $this->_contentPos[1] - $this->_contentPos[0];
+ return $length < 1 ? '' : fread($this->_fh, $length);
+ }
+
+ /**
+ * Return size of part
+ *
+ * Quite simple implemented currently (not decoding). Handle with care.
+ *
+ * @return int size
+ */
+ public function getSize() {
+ return $this->_contentPos[1] - $this->_contentPos[0];
+ }
+
+ /**
+ * Get part of multipart message
+ *
+ * @param int $num number of part starting with 1 for first part
+ * @return Zend_Mail_Part wanted part
+ * @throws Zend_Mail_Exception
+ */
+ public function getPart($num)
+ {
+ --$num;
+ if (!isset($this->_partPos[$num])) {
+ /**
+ * @see Zend_Mail_Exception
+ */
+ require_once 'Zend/Mail/Exception.php';
+ throw new Zend_Mail_Exception('part not found');
+ }
+
+ return new self(array('file' => $this->_fh, 'startPos' => $this->_partPos[$num][0],
+ 'endPos' => $this->_partPos[$num][1]));
+ }
+}
diff --git a/lib/Zend/Mail/Part/Interface.php b/lib/Zend/Mail/Part/Interface.php
new file mode 100644
index 0000000..d1a22f1
--- /dev/null
+++ b/lib/Zend/Mail/Part/Interface.php
@@ -0,0 +1,136 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Storage
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Interface.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Storage
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+interface Zend_Mail_Part_Interface extends RecursiveIterator
+{
+ /**
+ * Check if part is a multipart message
+ *
+ * @return bool if part is multipart
+ */
+ public function isMultipart();
+
+
+ /**
+ * Body of part
+ *
+ * If part is multipart the raw content of this part with all sub parts is returned
+ *
+ * @return string body
+ * @throws Zend_Mail_Exception
+ */
+ public function getContent();
+
+ /**
+ * Return size of part
+ *
+ * @return int size
+ */
+ public function getSize();
+
+ /**
+ * Get part of multipart message
+ *
+ * @param int $num number of part starting with 1 for first part
+ * @return Zend_Mail_Part wanted part
+ * @throws Zend_Mail_Exception
+ */
+ public function getPart($num);
+
+ /**
+ * Count parts of a multipart part
+ *
+ * @return int number of sub-parts
+ */
+ public function countParts();
+
+
+ /**
+ * Get all headers
+ *
+ * The returned headers are as saved internally. All names are lowercased. The value is a string or an array
+ * if a header with the same name occurs more than once.
+ *
+ * @return array headers as array(name => value)
+ */
+ public function getHeaders();
+
+ /**
+ * Get a header in specificed format
+ *
+ * Internally headers that occur more than once are saved as array, all other as string. If $format
+ * is set to string implode is used to concat the values (with Zend_Mime::LINEEND as delim).
+ *
+ * @param string $name name of header, matches case-insensitive, but camel-case is replaced with dashes
+ * @param string $format change type of return value to 'string' or 'array'
+ * @return string|array value of header in wanted or internal format
+ * @throws Zend_Mail_Exception
+ */
+ public function getHeader($name, $format = null);
+
+ /**
+ * Get a specific field from a header like content type or all fields as array
+ *
+ * If the header occurs more than once, only the value from the first header
+ * is returned.
+ *
+ * Throws a Zend_Mail_Exception if the requested header does not exist. If
+ * the specific header field does not exist, returns null.
+ *
+ * @param string $name name of header, like in getHeader()
+ * @param string $wantedPart the wanted part, default is first, if null an array with all parts is returned
+ * @param string $firstName key name for the first part
+ * @return string|array wanted part or all parts as array($firstName => firstPart, partname => value)
+ * @throws Zend_Exception, Zend_Mail_Exception
+ */
+ public function getHeaderField($name, $wantedPart = 0, $firstName = 0);
+
+
+ /**
+ * Getter for mail headers - name is matched in lowercase
+ *
+ * This getter is short for Zend_Mail_Part::getHeader($name, 'string')
+ *
+ * @see Zend_Mail_Part::getHeader()
+ *
+ * @param string $name header name
+ * @return string value of header
+ * @throws Zend_Mail_Exception
+ */
+ public function __get($name);
+
+ /**
+ * magic method to get content of part
+ *
+ * @return string content
+ */
+ public function __toString();
+} \ No newline at end of file
diff --git a/lib/Zend/Mail/Protocol/Abstract.php b/lib/Zend/Mail/Protocol/Abstract.php
new file mode 100644
index 0000000..cfb1f42
--- /dev/null
+++ b/lib/Zend/Mail/Protocol/Abstract.php
@@ -0,0 +1,385 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Protocol
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Abstract.php 12395 2008-11-07 23:58:38Z nico $
+ */
+
+
+/**
+ * @see Zend_Validate
+ */
+require_once 'Zend/Validate.php';
+
+
+/**
+ * @see Zend_Validate_Hostname
+ */
+require_once 'Zend/Validate/Hostname.php';
+
+
+/**
+ * Zend_Mail_Protocol_Abstract
+ *
+ * Provides low-level methods for concrete adapters to communicate with a remote mail server and track requests and responses.
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Protocol
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Abstract.php 12395 2008-11-07 23:58:38Z nico $
+ * @todo Implement proxy settings
+ */
+abstract class Zend_Mail_Protocol_Abstract
+{
+ /**
+ * Mail default EOL string
+ */
+ const EOL = "\r\n";
+
+
+ /**
+ * Default timeout in seconds for initiating session
+ */
+ const TIMEOUT_CONNECTION = 30;
+
+
+ /**
+ * Hostname or IP address of remote server
+ * @var string
+ */
+ protected $_host;
+
+
+ /**
+ * Port number of connection
+ * @var integer
+ */
+ protected $_port;
+
+
+ /**
+ * Instance of Zend_Validate to check hostnames
+ * @var Zend_Validate
+ */
+ protected $_validHost;
+
+
+ /**
+ * Socket connection resource
+ * @var resource
+ */
+ protected $_socket;
+
+
+ /**
+ * Last request sent to server
+ * @var string
+ */
+ protected $_request;
+
+
+ /**
+ * Array of server responses to last request
+ * @var array
+ */
+ protected $_response;
+
+
+ /**
+ * String template for parsing server responses using sscanf (default: 3 digit code and response string)
+ * @var resource
+ */
+ protected $_template = '%d%s';
+
+
+ /**
+ * Log of mail requests and server responses for a session
+ * @var string
+ */
+ private $_log;
+
+
+ /**
+ * Constructor.
+ *
+ * @param string $host OPTIONAL Hostname of remote connection (default: 127.0.0.1)
+ * @param integer $port OPTIONAL Port number (default: null)
+ * @throws Zend_Mail_Protocol_Exception
+ * @return void
+ */
+ public function __construct($host = '127.0.0.1', $port = null)
+ {
+ $this->_validHost = new Zend_Validate();
+ $this->_validHost->addValidator(new Zend_Validate_Hostname(Zend_Validate_Hostname::ALLOW_ALL));
+
+ if (!$this->_validHost->isValid($host)) {
+ /**
+ * @see Zend_Mail_Protocol_Exception
+ */
+ require_once 'Zend/Mail/Protocol/Exception.php';
+ throw new Zend_Mail_Protocol_Exception(join(', ', $this->_validHost->getMessages()));
+ }
+
+ $this->_host = $host;
+ $this->_port = $port;
+ }
+
+
+ /**
+ * Class destructor to cleanup open resources
+ *
+ * @return void
+ */
+ public function __destruct()
+ {
+ $this->_disconnect();
+ }
+
+
+ /**
+ * Create a connection to the remote host
+ *
+ * Concrete adapters for this class will implement their own unique connect scripts, using the _connect() method to create the socket resource.
+ */
+ abstract public function connect();
+
+
+ /**
+ * Retrieve the last client request
+ *
+ * @return string
+ */
+ public function getRequest()
+ {
+ return $this->_request;
+ }
+
+
+ /**
+ * Retrieve the last server response
+ *
+ * @return array
+ */
+ public function getResponse()
+ {
+ return $this->_response;
+ }
+
+
+ /**
+ * Retrieve the transaction log
+ *
+ * @return string
+ */
+ public function getLog()
+ {
+ return $this->_log;
+ }
+
+
+ /**
+ * Reset the transaction log
+ *
+ * @return void
+ */
+ public function resetLog()
+ {
+ $this->_log = '';
+ }
+
+
+ /**
+ * Connect to the server using the supplied transport and target
+ *
+ * An example $remote string may be 'tcp://mail.example.com:25' or 'ssh://hostname.com:2222'
+ *
+ * @param string $remote Remote
+ * @throws Zend_Mail_Protocol_Exception
+ * @return boolean
+ */
+ protected function _connect($remote)
+ {
+ $errorNum = 0;
+ $errorStr = '';
+
+ // open connection
+ $this->_socket = @stream_socket_client($remote, $errorNum, $errorStr, self::TIMEOUT_CONNECTION);
+
+ if ($this->_socket === false) {
+ if ($errorNum == 0) {
+ $errorStr = 'Could not open socket';
+ }
+ /**
+ * @see Zend_Mail_Protocol_Exception
+ */
+ require_once 'Zend/Mail/Protocol/Exception.php';
+ throw new Zend_Mail_Protocol_Exception($errorStr);
+ }
+
+ if (($result = stream_set_timeout($this->_socket, self::TIMEOUT_CONNECTION)) === false) {
+ /**
+ * @see Zend_Mail_Protocol_Exception
+ */
+ require_once 'Zend/Mail/Protocol/Exception.php';
+ throw new Zend_Mail_Protocol_Exception('Could not set stream timeout');
+ }
+
+ return $result;
+ }
+
+
+ /**
+ * Disconnect from remote host and free resource
+ *
+ * @return void
+ */
+ protected function _disconnect()
+ {
+ if (is_resource($this->_socket)) {
+ fclose($this->_socket);
+ }
+ }
+
+
+ /**
+ * Send the given request followed by a LINEEND to the server.
+ *
+ * @param string $request
+ * @throws Zend_Mail_Protocol_Exception
+ * @return integer|boolean Number of bytes written to remote host
+ */
+ protected function _send($request)
+ {
+ if (!is_resource($this->_socket)) {
+ /**
+ * @see Zend_Mail_Protocol_Exception
+ */
+ require_once 'Zend/Mail/Protocol/Exception.php';
+ throw new Zend_Mail_Protocol_Exception('No connection has been established to ' . $this->_host);
+ }
+
+ $this->_request = $request;
+
+ $result = fwrite($this->_socket, $request . self::EOL);
+
+ // Save request to internal log
+ $this->_log .= $request . self::EOL;
+
+ if ($result === false) {
+ /**
+ * @see Zend_Mail_Protocol_Exception
+ */
+ require_once 'Zend/Mail/Protocol/Exception.php';
+ throw new Zend_Mail_Protocol_Exception('Could not send request to ' . $this->_host);
+ }
+
+ return $result;
+ }
+
+
+ /**
+ * Get a line from the stream.
+ *
+ * @var integer $timeout Per-request timeout value if applicable
+ * @throws Zend_Mail_Protocol_Exception
+ * @return string
+ */
+ protected function _receive($timeout = null)
+ {
+ if (!is_resource($this->_socket)) {
+ /**
+ * @see Zend_Mail_Protocol_Exception
+ */
+ require_once 'Zend/Mail/Protocol/Exception.php';
+ throw new Zend_Mail_Protocol_Exception('No connection has been established to ' . $this->_host);
+ }
+
+ // Adapters may wish to supply per-commend timeouts according to appropriate RFC
+ if ($timeout !== null) {
+ stream_set_timeout($this->_socket, $timeout);
+ }
+
+ // Retrieve response
+ $reponse = fgets($this->_socket, 1024);
+
+ // Save request to internal log
+ $this->_log .= $reponse;
+
+ // Check meta data to ensure connection is still valid
+ $info = stream_get_meta_data($this->_socket);
+
+ if (!empty($info['timed_out'])) {
+ /**
+ * @see Zend_Mail_Protocol_Exception
+ */
+ require_once 'Zend/Mail/Protocol/Exception.php';
+ throw new Zend_Mail_Protocol_Exception($this->_host . ' has timed out');
+ }
+
+ if ($reponse === false) {
+ /**
+ * @see Zend_Mail_Protocol_Exception
+ */
+ require_once 'Zend/Mail/Protocol/Exception.php';
+ throw new Zend_Mail_Protocol_Exception('Could not read from ' . $this->_host);
+ }
+
+ return $reponse;
+ }
+
+
+ /**
+ * Parse server response for successful codes
+ *
+ * Read the response from the stream and check for expected return code.
+ * Throws a Zend_Mail_Protocol_Exception if an unexpected code is returned.
+ *
+ * @param string|array $code One or more codes that indicate a successful response
+ * @throws Zend_Mail_Protocol_Exception
+ * @return string Last line of response string
+ */
+ protected function _expect($code, $timeout = null)
+ {
+ $this->_response = array();
+ $cmd = '';
+ $msg = '';
+
+ if (!is_array($code)) {
+ $code = array($code);
+ }
+
+ do {
+ $this->_response[] = $result = $this->_receive($timeout);
+ sscanf($result, $this->_template, $cmd, $msg);
+
+ if ($cmd === null || !in_array($cmd, $code)) {
+ /**
+ * @see Zend_Mail_Protocol_Exception
+ */
+ require_once 'Zend/Mail/Protocol/Exception.php';
+ throw new Zend_Mail_Protocol_Exception($result);
+ }
+
+ } while (strpos($msg, '-') === 0); // The '-' message prefix indicates an information string instead of a response string.
+
+ return $msg;
+ }
+}
diff --git a/lib/Zend/Mail/Protocol/Exception.php b/lib/Zend/Mail/Protocol/Exception.php
new file mode 100644
index 0000000..c3add11
--- /dev/null
+++ b/lib/Zend/Mail/Protocol/Exception.php
@@ -0,0 +1,39 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Protocol
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Exception.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Exception
+ */
+require_once 'Zend/Mail/Exception.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Protocol
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Mail_Protocol_Exception extends Zend_Mail_Exception
+{}
+
diff --git a/lib/Zend/Mail/Protocol/Imap.php b/lib/Zend/Mail/Protocol/Imap.php
new file mode 100644
index 0000000..eec0ff3
--- /dev/null
+++ b/lib/Zend/Mail/Protocol/Imap.php
@@ -0,0 +1,837 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Protocol
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Imap.php 12539 2008-11-11 02:47:17Z yoshida@zend.co.jp $
+ */
+
+
+/**
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Protocol
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Mail_Protocol_Imap
+{
+ /**
+ * Default timeout in seconds for initiating session
+ */
+ const TIMEOUT_CONNECTION = 30;
+
+ /**
+ * socket to imap server
+ * @var resource|null
+ */
+ protected $_socket;
+
+ /**
+ * counter for request tag
+ * @var int
+ */
+ protected $_tagCount = 0;
+
+ /**
+ * Public constructor
+ *
+ * @param string $host hostname of IP address of IMAP server, if given connect() is called
+ * @param int|null $port port of IMAP server, null for default (143 or 993 for ssl)
+ * @param bool $ssl use ssl? 'SSL', 'TLS' or false
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ function __construct($host = '', $port = null, $ssl = false)
+ {
+ if ($host) {
+ $this->connect($host, $port, $ssl);
+ }
+ }
+
+ /**
+ * Public destructor
+ */
+ public function __destruct()
+ {
+ $this->logout();
+ }
+
+ /**
+ * Open connection to POP3 server
+ *
+ * @param string $host hostname of IP address of POP3 server
+ * @param int|null $port of IMAP server, default is 143 (993 for ssl)
+ * @param string|bool $ssl use 'SSL', 'TLS' or false
+ * @return string welcome message
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function connect($host, $port = null, $ssl = false)
+ {
+ if ($ssl == 'SSL') {
+ $host = 'ssl://' . $host;
+ }
+
+ if ($port === null) {
+ $port = $ssl === 'SSL' ? 993 : 143;
+ }
+
+ $errno = 0;
+ $errstr = '';
+ $this->_socket = @fsockopen($host, $port, $errno, $errstr, self::TIMEOUT_CONNECTION);
+ if (!$this->_socket) {
+ /**
+ * @see Zend_Mail_Protocol_Exception
+ */
+ require_once 'Zend/Mail/Protocol/Exception.php';
+ throw new Zend_Mail_Protocol_Exception('cannot connect to host : ' . $errno . ' : ' . $errstr);
+ }
+
+ if (!$this->_assumedNextLine('* OK')) {
+ /**
+ * @see Zend_Mail_Protocol_Exception
+ */
+ require_once 'Zend/Mail/Protocol/Exception.php';
+ throw new Zend_Mail_Protocol_Exception('host doesn\'t allow connection');
+ }
+
+ if ($ssl === 'TLS') {
+ $result = $this->requestAndResponse('STARTTLS');
+ $result = $result && stream_socket_enable_crypto($this->_socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
+ if (!$result) {
+ /**
+ * @see Zend_Mail_Protocol_Exception
+ */
+ require_once 'Zend/Mail/Protocol/Exception.php';
+ throw new Zend_Mail_Protocol_Exception('cannot enable TLS');
+ }
+ }
+ }
+
+ /**
+ * get the next line from socket with error checking, but nothing else
+ *
+ * @return string next line
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ protected function _nextLine()
+ {
+ $line = @fgets($this->_socket);
+ if ($line === false) {
+ /**
+ * @see Zend_Mail_Protocol_Exception
+ */
+ require_once 'Zend/Mail/Protocol/Exception.php';
+ throw new Zend_Mail_Protocol_Exception('cannot read - connection closed?');
+ }
+
+ return $line;
+ }
+
+ /**
+ * get next line and assume it starts with $start. some requests give a simple
+ * feedback so we can quickly check if we can go on.
+ *
+ * @param string $start the first bytes we assume to be in the next line
+ * @return bool line starts with $start
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ protected function _assumedNextLine($start)
+ {
+ $line = $this->_nextLine();
+ return strpos($line, $start) === 0;
+ }
+
+ /**
+ * get next line and split the tag. that's the normal case for a response line
+ *
+ * @param string $tag tag of line is returned by reference
+ * @return string next line
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ protected function _nextTaggedLine(&$tag)
+ {
+ $line = $this->_nextLine();
+
+ // seperate tag from line
+ list($tag, $line) = explode(' ', $line, 2);
+
+ return $line;
+ }
+
+ /**
+ * split a given line in tokens. a token is literal of any form or a list
+ *
+ * @param string $line line to decode
+ * @return array tokens, literals are returned as string, lists as array
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ protected function _decodeLine($line)
+ {
+ $tokens = array();
+ $stack = array();
+
+ /*
+ We start to decode the response here. The unterstood tokens are:
+ literal
+ "literal" or also "lit\\er\"al"
+ {bytes}<NL>literal
+ (literals*)
+ All tokens are returned in an array. Literals in braces (the last unterstood
+ token in the list) are returned as an array of tokens. I.e. the following response:
+ "foo" baz {3}<NL>bar ("f\\\"oo" bar)
+ would be returned as:
+ array('foo', 'baz', 'bar', array('f\\\"oo', 'bar'));
+
+ // TODO: add handling of '[' and ']' to parser for easier handling of response text
+ */
+ // replace any trailling <NL> including spaces with a single space
+ $line = rtrim($line) . ' ';
+ while (($pos = strpos($line, ' ')) !== false) {
+ $token = substr($line, 0, $pos);
+ while ($token[0] == '(') {
+ array_push($stack, $tokens);
+ $tokens = array();
+ $token = substr($token, 1);
+ }
+ if ($token[0] == '"') {
+ if (preg_match('%^"((.|\\\\|\\")*?)" *%', $line, $matches)) {
+ $tokens[] = $matches[1];
+ $line = substr($line, strlen($matches[0]));
+ continue;
+ }
+ }
+ if ($token[0] == '{') {
+ $endPos = strpos($token, '}');
+ $chars = substr($token, 1, $endPos - 1);
+ if (is_numeric($chars)) {
+ $token = '';
+ while (strlen($token) < $chars) {
+ $token .= $this->_nextLine();
+ }
+ $line = '';
+ if (strlen($token) > $chars) {
+ $line = substr($token, $chars);
+ $token = substr($token, 0, $chars);
+ } else {
+ $line .= $this->_nextLine();
+ }
+ $tokens[] = $token;
+ $line = trim($line) . ' ';
+ continue;
+ }
+ }
+ if ($stack && $token[strlen($token) - 1] == ')') {
+ // closing braces are not seperated by spaces, so we need to count them
+ $braces = strlen($token);
+ $token = rtrim($token, ')');
+ // only count braces if more than one
+ $braces -= strlen($token) + 1;
+ // only add if token had more than just closing braces
+ if ($token) {
+ $tokens[] = $token;
+ }
+ $token = $tokens;
+ $tokens = array_pop($stack);
+ // special handline if more than one closing brace
+ while ($braces-- > 0) {
+ $tokens[] = $token;
+ $token = $tokens;
+ $tokens = array_pop($stack);
+ }
+ }
+ $tokens[] = $token;
+ $line = substr($line, $pos + 1);
+ }
+
+ // maybe the server forgot to send some closing braces
+ while ($stack) {
+ $child = $tokens;
+ $tokens = array_pop($stack);
+ $tokens[] = $child;
+ }
+
+ return $tokens;
+ }
+
+ /**
+ * read a response "line" (could also be more than one real line if response has {..}<NL>)
+ * and do a simple decode
+ *
+ * @param array|string $tokens decoded tokens are returned by reference, if $dontParse
+ * is true the unparsed line is returned here
+ * @param string $wantedTag check for this tag for response code. Default '*' is
+ * continuation tag.
+ * @param bool $dontParse if true only the unparsed line is returned $tokens
+ * @return bool if returned tag matches wanted tag
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function readLine(&$tokens = array(), $wantedTag = '*', $dontParse = false)
+ {
+ $line = $this->_nextTaggedLine($tag);
+ if (!$dontParse) {
+ $tokens = $this->_decodeLine($line);
+ } else {
+ $tokens = $line;
+ }
+
+ // if tag is wanted tag we might be at the end of a multiline response
+ return $tag == $wantedTag;
+ }
+
+ /**
+ * read all lines of response until given tag is found (last line of response)
+ *
+ * @param string $tag the tag of your request
+ * @param string|array $filter you can filter the response so you get only the
+ * given response lines
+ * @param bool $dontParse if true every line is returned unparsed instead of
+ * the decoded tokens
+ * @return null|bool|array tokens if success, false if error, null if bad request
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function readResponse($tag, $dontParse = false)
+ {
+ $lines = array();
+ while (!$this->readLine($tokens, $tag, $dontParse)) {
+ $lines[] = $tokens;
+ }
+
+ if ($dontParse) {
+ // last to chars are still needed for response code
+ $tokens = array(substr($tokens, 0, 2));
+ }
+ // last line has response code
+ if ($tokens[0] == 'OK') {
+ return $lines ? $lines : true;
+ } else if ($tokens[0] == 'NO'){
+ return false;
+ }
+ return null;
+ }
+
+ /**
+ * send a request
+ *
+ * @param string $command your request command
+ * @param array $tokens additional parameters to command, use escapeString() to prepare
+ * @param string $tag provide a tag otherwise an autogenerated is returned
+ * @return null
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function sendRequest($command, $tokens = array(), &$tag = null)
+ {
+ if (!$tag) {
+ ++$this->_tagCount;
+ $tag = 'TAG' . $this->_tagCount;
+ }
+
+ $line = $tag . ' ' . $command;
+
+ foreach ($tokens as $token) {
+ if (is_array($token)) {
+ if (@fputs($this->_socket, $line . ' ' . $token[0] . "\r\n") === false) {
+ /**
+ * @see Zend_Mail_Protocol_Exception
+ */
+ require_once 'Zend/Mail/Protocol/Exception.php';
+ throw new Zend_Mail_Protocol_Exception('cannot write - connection closed?');
+ }
+ if (!$this->_assumedNextLine('+ ')) {
+ /**
+ * @see Zend_Mail_Protocol_Exception
+ */
+ require_once 'Zend/Mail/Protocol/Exception.php';
+ throw new Zend_Mail_Protocol_Exception('cannot send literal string');
+ }
+ $line = $token[1];
+ } else {
+ $line .= ' ' . $token;
+ }
+ }
+
+ if (@fputs($this->_socket, $line . "\r\n") === false) {
+ /**
+ * @see Zend_Mail_Protocol_Exception
+ */
+ require_once 'Zend/Mail/Protocol/Exception.php';
+ throw new Zend_Mail_Protocol_Exception('cannot write - connection closed?');
+ }
+ }
+
+ /**
+ * send a request and get response at once
+ *
+ * @param string $command command as in sendRequest()
+ * @param array $tokens parameters as in sendRequest()
+ * @param bool $dontParse if true unparsed lines are returned instead of tokens
+ * @return mixed response as in readResponse()
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function requestAndResponse($command, $tokens = array(), $dontParse = false)
+ {
+ $this->sendRequest($command, $tokens, $tag);
+ $response = $this->readResponse($tag, $dontParse);
+
+ return $response;
+ }
+
+ /**
+ * escape one or more literals i.e. for sendRequest
+ *
+ * @param string|array $string the literal/-s
+ * @return string|array escape literals, literals with newline ar returned
+ * as array('{size}', 'string');
+ */
+ public function escapeString($string)
+ {
+ if (func_num_args() < 2) {
+ if (strpos($string, "\n") !== false) {
+ return array('{' . strlen($string) . '}', $string);
+ } else {
+ return '"' . str_replace(array('\\', '"'), array('\\\\', '\\"'), $string) . '"';
+ }
+ }
+ $result = array();
+ foreach (func_get_args() as $string) {
+ $result[] = $this->escapeString($string);
+ }
+ return $result;
+ }
+
+ /**
+ * escape a list with literals or lists
+ *
+ * @param array $list list with literals or lists as PHP array
+ * @return string escaped list for imap
+ */
+ public function escapeList($list)
+ {
+ $result = array();
+ foreach ($list as $k => $v) {
+ if (!is_array($v)) {
+// $result[] = $this->escapeString($v);
+ $result[] = $v;
+ continue;
+ }
+ $result[] = $this->escapeList($v);
+ }
+ return '(' . implode(' ', $result) . ')';
+ }
+
+ /**
+ * Login to IMAP server.
+ *
+ * @param string $user username
+ * @param string $password password
+ * @return bool success
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function login($user, $password)
+ {
+ return $this->requestAndResponse('LOGIN', $this->escapeString($user, $password), true);
+ }
+
+ /**
+ * logout of imap server
+ *
+ * @return bool success
+ */
+ public function logout()
+ {
+ $result = false;
+ if ($this->_socket) {
+ try {
+ $result = $this->requestAndResponse('LOGOUT', array(), true);
+ } catch (Zend_Mail_Protocol_Exception $e) {
+ // ignoring exception
+ }
+ fclose($this->_socket);
+ $this->_socket = null;
+ }
+ return $result;
+ }
+
+
+ /**
+ * Get capabilities from IMAP server
+ *
+ * @return array list of capabilities
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function capability()
+ {
+ $response = $this->requestAndResponse('CAPABILITY');
+
+ if (!$response) {
+ return $response;
+ }
+
+ $capabilities = array();
+ foreach ($response as $line) {
+ $capabilities = array_merge($capabilities, $line);
+ }
+ return $capabilities;
+ }
+
+ /**
+ * Examine and select have the same response. The common code for both
+ * is in this method
+ *
+ * @param string $command can be 'EXAMINE' or 'SELECT' and this is used as command
+ * @param string $box which folder to change to or examine
+ * @return bool|array false if error, array with returned information
+ * otherwise (flags, exists, recent, uidvalidity)
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function examineOrSelect($command = 'EXAMINE', $box = 'INBOX')
+ {
+ $this->sendRequest($command, array($this->escapeString($box)), $tag);
+
+ $result = array();
+ while (!$this->readLine($tokens, $tag)) {
+ if ($tokens[0] == 'FLAGS') {
+ array_shift($tokens);
+ $result['flags'] = $tokens;
+ continue;
+ }
+ switch ($tokens[1]) {
+ case 'EXISTS':
+ case 'RECENT':
+ $result[strtolower($tokens[1])] = $tokens[0];
+ break;
+ case '[UIDVALIDITY':
+ $result['uidvalidity'] = (int)$tokens[2];
+ break;
+ default:
+ // ignore
+ }
+ }
+
+ if ($tokens[0] != 'OK') {
+ return false;
+ }
+ return $result;
+ }
+
+ /**
+ * change folder
+ *
+ * @param string $box change to this folder
+ * @return bool|array see examineOrselect()
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function select($box = 'INBOX')
+ {
+ return $this->examineOrSelect('SELECT', $box);
+ }
+
+ /**
+ * examine folder
+ *
+ * @param string $box examine this folder
+ * @return bool|array see examineOrselect()
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function examine($box = 'INBOX')
+ {
+ return $this->examineOrSelect('EXAMINE', $box);
+ }
+
+ /**
+ * fetch one or more items of one or more messages
+ *
+ * @param string|array $items items to fetch from message(s) as string (if only one item)
+ * or array of strings
+ * @param int $from message for items or start message if $to !== null
+ * @param int|null $to if null only one message ($from) is fetched, else it's the
+ * last message, INF means last message avaible
+ * @return string|array if only one item of one message is fetched it's returned as string
+ * if items of one message are fetched it's returned as (name => value)
+ * if one items of messages are fetched it's returned as (msgno => value)
+ * if items of messages are fetchted it's returned as (msgno => (name => value))
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function fetch($items, $from, $to = null)
+ {
+ if (is_array($from)) {
+ $set = implode(',', $from);
+ } else if ($to === null) {
+ $set = (int)$from;
+ } else if ($to === INF) {
+ $set = (int)$from . ':*';
+ } else {
+ $set = (int)$from . ':' . (int)$to;
+ }
+
+ $items = (array)$items;
+ $itemList = $this->escapeList($items);
+
+ $this->sendRequest('FETCH', array($set, $itemList), $tag);
+
+ $result = array();
+ while (!$this->readLine($tokens, $tag)) {
+ // ignore other responses
+ if ($tokens[1] != 'FETCH') {
+ continue;
+ }
+ // ignore other messages
+ if ($to === null && !is_array($from) && $tokens[0] != $from) {
+ continue;
+ }
+ // if we only want one item we return that one directly
+ if (count($items) == 1) {
+ if ($tokens[2][0] == $items[0]) {
+ $data = $tokens[2][1];
+ } else {
+ // maybe the server send an other field we didn't wanted
+ $count = count($tokens[2]);
+ // we start with 2, because 0 was already checked
+ for ($i = 2; $i < $count; $i += 2) {
+ if ($tokens[2][$i] != $items[0]) {
+ continue;
+ }
+ $data = $tokens[2][$i + 1];
+ break;
+ }
+ }
+ } else {
+ $data = array();
+ while (key($tokens[2]) !== null) {
+ $data[current($tokens[2])] = next($tokens[2]);
+ next($tokens[2]);
+ }
+ }
+ // if we want only one message we can ignore everything else and just return
+ if ($to === null && !is_array($from) && $tokens[0] == $from) {
+ // we still need to read all lines
+ while (!$this->readLine($tokens, $tag));
+ return $data;
+ }
+ $result[$tokens[0]] = $data;
+ }
+
+ if ($to === null && !is_array($from)) {
+ /**
+ * @see Zend_Mail_Protocol_Exception
+ */
+ require_once 'Zend/Mail/Protocol/Exception.php';
+ throw new Zend_Mail_Protocol_Exception('the single id was not found in response');
+ }
+
+ return $result;
+ }
+
+ /**
+ * get mailbox list
+ *
+ * this method can't be named after the IMAP command 'LIST', as list is a reserved keyword
+ *
+ * @param string $reference mailbox reference for list
+ * @param string $mailbox mailbox name match with wildcards
+ * @return array mailboxes that matched $mailbox as array(globalName => array('delim' => .., 'flags' => ..))
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function listMailbox($reference = '', $mailbox = '*')
+ {
+ $result = array();
+ $list = $this->requestAndResponse('LIST', $this->escapeString($reference, $mailbox));
+ if (!$list || $list === true) {
+ return $result;
+ }
+
+ foreach ($list as $item) {
+ if (count($item) != 4 || $item[0] != 'LIST') {
+ continue;
+ }
+ $result[$item[3]] = array('delim' => $item[2], 'flags' => $item[1]);
+ }
+
+ return $result;
+ }
+
+ /**
+ * set flags
+ *
+ * @param array $flags flags to set, add or remove - see $mode
+ * @param int $from message for items or start message if $to !== null
+ * @param int|null $to if null only one message ($from) is fetched, else it's the
+ * last message, INF means last message avaible
+ * @param string|null $mode '+' to add flags, '-' to remove flags, everything else sets the flags as given
+ * @param bool $silent if false the return values are the new flags for the wanted messages
+ * @return bool|array new flags if $silent is false, else true or false depending on success
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function store(array $flags, $from, $to = null, $mode = null, $silent = true)
+ {
+ $item = 'FLAGS';
+ if ($mode == '+' || $mode == '-') {
+ $item = $mode . $item;
+ }
+ if ($silent) {
+ $item .= '.SILENT';
+ }
+
+ $flags = $this->escapeList($flags);
+ $set = (int)$from;
+ if ($to != null) {
+ $set .= ':' . ($to == INF ? '*' : (int)$to);
+ }
+
+ $result = $this->requestAndResponse('STORE', array($set, $item, $flags), $silent);
+
+ if ($silent) {
+ return $result ? true : false;
+ }
+
+ $tokens = $result;
+ $result = array();
+ foreach ($tokens as $token) {
+ if ($token[1] != 'FETCH' || $token[2][0] != 'FLAGS') {
+ continue;
+ }
+ $result[$token[0]] = $token[2][1];
+ }
+
+ return $result;
+ }
+
+ /**
+ * append a new message to given folder
+ *
+ * @param string $folder name of target folder
+ * @param string $message full message content
+ * @param array $flags flags for new message
+ * @param string $date date for new message
+ * @return bool success
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function append($folder, $message, $flags = null, $date = null)
+ {
+ $tokens = array();
+ $tokens[] = $this->escapeString($folder);
+ if ($flags !== null) {
+ $tokens[] = $this->escapeList($flags);
+ }
+ if ($date !== null) {
+ $tokens[] = $this->escapeString($date);
+ }
+ $tokens[] = $this->escapeString($message);
+
+ return $this->requestAndResponse('APPEND', $tokens, true);
+ }
+
+ /**
+ * copy message set from current folder to other folder
+ *
+ * @param string $folder destination folder
+ * @param int|null $to if null only one message ($from) is fetched, else it's the
+ * last message, INF means last message avaible
+ * @return bool success
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function copy($folder, $from, $to = null)
+ {
+ $set = (int)$from;
+ if ($to != null) {
+ $set .= ':' . ($to == INF ? '*' : (int)$to);
+ }
+
+ return $this->requestAndResponse('COPY', array($set, $this->escapeString($folder)), true);
+ }
+
+ /**
+ * create a new folder (and parent folders if needed)
+ *
+ * @param string $folder folder name
+ * @return bool success
+ */
+ public function create($folder)
+ {
+ return $this->requestAndResponse('CREATE', array($this->escapeString($folder)), true);
+ }
+
+ /**
+ * rename an existing folder
+ *
+ * @param string $old old name
+ * @param string $new new name
+ * @return bool success
+ */
+ public function rename($old, $new)
+ {
+ return $this->requestAndResponse('RENAME', $this->escapeString($old, $new), true);
+ }
+
+ /**
+ * remove a folder
+ *
+ * @param string $folder folder name
+ * @return bool success
+ */
+ public function delete($folder)
+ {
+ return $this->requestAndResponse('DELETE', array($this->escapeString($folder)), true);
+ }
+
+ /**
+ * permanently remove messages
+ *
+ * @return bool success
+ */
+ public function expunge()
+ {
+ // TODO: parse response?
+ return $this->requestAndResponse('EXPUNGE');
+ }
+
+ /**
+ * send noop
+ *
+ * @return bool success
+ */
+ public function noop()
+ {
+ // TODO: parse response
+ return $this->requestAndResponse('NOOP');
+ }
+
+ /**
+ * do a search request
+ *
+ * This method is currently marked as internal as the API might change and is not
+ * safe if you don't take precautions.
+ *
+ * @internal
+ * @return array message ids
+ */
+ public function search(array $params)
+ {
+ $response = $this->requestAndResponse('SEARCH', $params);
+ if (!$response) {
+ return $response;
+ }
+
+ foreach ($response as $ids) {
+ if ($ids[0] == 'SEARCH') {
+ array_shift($ids);
+ return $ids;
+ }
+ }
+ return array();
+ }
+
+}
diff --git a/lib/Zend/Mail/Protocol/Pop3.php b/lib/Zend/Mail/Protocol/Pop3.php
new file mode 100644
index 0000000..c1f968a
--- /dev/null
+++ b/lib/Zend/Mail/Protocol/Pop3.php
@@ -0,0 +1,471 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Protocol
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Pop3.php 12539 2008-11-11 02:47:17Z yoshida@zend.co.jp $
+ */
+
+
+/**
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Protocol
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Mail_Protocol_Pop3
+{
+ /**
+ * Default timeout in seconds for initiating session
+ */
+ const TIMEOUT_CONNECTION = 30;
+
+ /**
+ * saves if server supports top
+ * @var null|bool
+ */
+ public $hasTop = null;
+
+ /**
+ * socket to pop3
+ * @var null|resource
+ */
+ protected $_socket;
+
+ /**
+ * greeting timestamp for apop
+ * @var null|string
+ */
+ protected $_timestamp;
+
+
+ /**
+ * Public constructor
+ *
+ * @param string $host hostname of IP address of POP3 server, if given connect() is called
+ * @param int|null $port port of POP3 server, null for default (110 or 995 for ssl)
+ * @param bool|string $ssl use ssl? 'SSL', 'TLS' or false
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function __construct($host = '', $port = null, $ssl = false)
+ {
+ if ($host) {
+ $this->connect($host, $port, $ssl);
+ }
+ }
+
+
+ /**
+ * Public destructor
+ */
+ public function __destruct()
+ {
+ $this->logout();
+ }
+
+
+ /**
+ * Open connection to POP3 server
+ *
+ * @param string $host hostname of IP address of POP3 server
+ * @param int|null $port of POP3 server, default is 110 (995 for ssl)
+ * @param string|bool $ssl use 'SSL', 'TLS' or false
+ * @return string welcome message
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function connect($host, $port = null, $ssl = false)
+ {
+ if ($ssl == 'SSL') {
+ $host = 'ssl://' . $host;
+ }
+
+ if ($port === null) {
+ $port = $ssl == 'SSL' ? 995 : 110;
+ }
+
+ $errno = 0;
+ $errstr = '';
+ $this->_socket = @fsockopen($host, $port, $errno, $errstr, self::TIMEOUT_CONNECTION);
+ if (!$this->_socket) {
+ /**
+ * @see Zend_Mail_Protocol_Exception
+ */
+ require_once 'Zend/Mail/Protocol/Exception.php';
+ throw new Zend_Mail_Protocol_Exception('cannot connect to host : ' . $errno . ' : ' . $errstr);
+ }
+
+ $welcome = $this->readResponse();
+
+ strtok($welcome, '<');
+ $this->_timestamp = strtok('>');
+ if (!strpos($this->_timestamp, '@')) {
+ $this->_timestamp = null;
+ } else {
+ $this->_timestamp = '<' . $this->_timestamp . '>';
+ }
+
+ if ($ssl === 'TLS') {
+ $this->request('STLS');
+ $result = stream_socket_enable_crypto($this->_socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
+ if (!$result) {
+ /**
+ * @see Zend_Mail_Protocol_Exception
+ */
+ require_once 'Zend/Mail/Protocol/Exception.php';
+ throw new Zend_Mail_Protocol_Exception('cannot enable TLS');
+ }
+ }
+
+ return $welcome;
+ }
+
+
+ /**
+ * Send a request
+ *
+ * @param string $request your request without newline
+ * @return null
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function sendRequest($request)
+ {
+ $result = @fputs($this->_socket, $request . "\r\n");
+ if (!$result) {
+ /**
+ * @see Zend_Mail_Protocol_Exception
+ */
+ require_once 'Zend/Mail/Protocol/Exception.php';
+ throw new Zend_Mail_Protocol_Exception('send failed - connection closed?');
+ }
+ }
+
+
+ /**
+ * read a response
+ *
+ * @param boolean $multiline response has multiple lines and should be read until "<nl>.<nl>"
+ * @return string response
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function readResponse($multiline = false)
+ {
+ $result = @fgets($this->_socket);
+ if (!is_string($result)) {
+ /**
+ * @see Zend_Mail_Protocol_Exception
+ */
+ require_once 'Zend/Mail/Protocol/Exception.php';
+ throw new Zend_Mail_Protocol_Exception('read failed - connection closed?');
+ }
+
+ $result = trim($result);
+ if (strpos($result, ' ')) {
+ list($status, $message) = explode(' ', $result, 2);
+ } else {
+ $status = $result;
+ $message = '';
+ }
+
+ if ($status != '+OK') {
+ /**
+ * @see Zend_Mail_Protocol_Exception
+ */
+ require_once 'Zend/Mail/Protocol/Exception.php';
+ throw new Zend_Mail_Protocol_Exception('last request failed');
+ }
+
+ if ($multiline) {
+ $message = '';
+ $line = fgets($this->_socket);
+ while ($line && rtrim($line, "\r\n") != '.') {
+ if ($line[0] == '.') {
+ $line = substr($line, 1);
+ }
+ $message .= $line;
+ $line = fgets($this->_socket);
+ };
+ }
+
+ return $message;
+ }
+
+
+ /**
+ * Send request and get resposne
+ *
+ * @see sendRequest(), readResponse()
+ *
+ * @param string $request request
+ * @param bool $multiline multiline response?
+ * @return string result from readResponse()
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function request($request, $multiline = false)
+ {
+ $this->sendRequest($request);
+ return $this->readResponse($multiline);
+ }
+
+
+ /**
+ * End communication with POP3 server (also closes socket)
+ *
+ * @return null
+ */
+ public function logout()
+ {
+ if (!$this->_socket) {
+ return;
+ }
+
+ try {
+ $this->request('QUIT');
+ } catch (Zend_Mail_Protocol_Exception $e) {
+ // ignore error - we're closing the socket anyway
+ }
+
+ fclose($this->_socket);
+ $this->_socket = null;
+ }
+
+
+ /**
+ * Get capabilities from POP3 server
+ *
+ * @return array list of capabilities
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function capa()
+ {
+ $result = $this->request('CAPA', true);
+ return explode("\n", $result);
+ }
+
+
+ /**
+ * Login to POP3 server. Can use APOP
+ *
+ * @param string $user username
+ * @param string $password password
+ * @param bool $try_apop should APOP be tried?
+ * @return void
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function login($user, $password, $tryApop = true)
+ {
+ if ($tryApop && $this->_timestamp) {
+ try {
+ $this->request("APOP $user " . md5($this->_timestamp . $password));
+ return;
+ } catch (Zend_Mail_Protocol_Exception $e) {
+ // ignore
+ }
+ }
+
+ $result = $this->request("USER $user");
+ $result = $this->request("PASS $password");
+ }
+
+
+ /**
+ * Make STAT call for message count and size sum
+ *
+ * @param int $messages out parameter with count of messages
+ * @param int $octets out parameter with size in octects of messages
+ * @return void
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function status(&$messages, &$octets)
+ {
+ $messages = 0;
+ $octets = 0;
+ $result = $this->request('STAT');
+
+ list($messages, $octets) = explode(' ', $result);
+ }
+
+
+ /**
+ * Make LIST call for size of message(s)
+ *
+ * @param int|null $msgno number of message, null for all
+ * @return int|array size of given message or list with array(num => size)
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function getList($msgno = null)
+ {
+ if ($msgno !== null) {
+ $result = $this->request("LIST $msgno");
+
+ list(, $result) = explode(' ', $result);
+ return (int)$result;
+ }
+
+ $result = $this->request('LIST', true);
+ $messages = array();
+ $line = strtok($result, "\n");
+ while ($line) {
+ list($no, $size) = explode(' ', trim($line));
+ $messages[(int)$no] = (int)$size;
+ $line = strtok("\n");
+ }
+
+ return $messages;
+ }
+
+
+ /**
+ * Make UIDL call for getting a uniqueid
+ *
+ * @param int|null $msgno number of message, null for all
+ * @return string|array uniqueid of message or list with array(num => uniqueid)
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function uniqueid($msgno = null)
+ {
+ if ($msgno !== null) {
+ $result = $this->request("UIDL $msgno");
+
+ list(, $result) = explode(' ', $result);
+ return $result;
+ }
+
+ $result = $this->request('UIDL', true);
+
+ $result = explode("\n", $result);
+ $messages = array();
+ foreach ($result as $line) {
+ if (!$line) {
+ continue;
+ }
+ list($no, $id) = explode(' ', trim($line), 2);
+ $messages[(int)$no] = $id;
+ }
+
+ return $messages;
+
+ }
+
+
+ /**
+ * Make TOP call for getting headers and maybe some body lines
+ * This method also sets hasTop - before it it's not known if top is supported
+ *
+ * The fallback makes normale RETR call, which retrieves the whole message. Additional
+ * lines are not removed.
+ *
+ * @param int $msgno number of message
+ * @param int $lines number of wanted body lines (empty line is inserted after header lines)
+ * @param bool $fallback fallback with full retrieve if top is not supported
+ * @return string message headers with wanted body lines
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function top($msgno, $lines = 0, $fallback = false)
+ {
+ if ($this->hasTop === false) {
+ if ($fallback) {
+ return $this->retrieve($msgno);
+ } else {
+ /**
+ * @see Zend_Mail_Protocol_Exception
+ */
+ require_once 'Zend/Mail/Protocol/Exception.php';
+ throw new Zend_Mail_Protocol_Exception('top not supported and no fallback wanted');
+ }
+ }
+ $this->hasTop = true;
+
+ $lines = (!$lines || $lines < 1) ? 0 : (int)$lines;
+
+ try {
+ $result = $this->request("TOP $msgno $lines", true);
+ } catch (Zend_Mail_Protocol_Exception $e) {
+ $this->hasTop = false;
+ if ($fallback) {
+ $result = $this->retrieve($msgno);
+ } else {
+ throw $e;
+ }
+ }
+
+ return $result;
+ }
+
+
+ /**
+ * Make a RETR call for retrieving a full message with headers and body
+ *
+ * @deprecated since 1.1.0; this method has a typo - please use retrieve()
+ * @param int $msgno message number
+ * @return string message
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function retrive($msgno)
+ {
+ return $this->retrieve($msgno);
+ }
+
+
+ /**
+ * Make a RETR call for retrieving a full message with headers and body
+ *
+ * @param int $msgno message number
+ * @return string message
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function retrieve($msgno)
+ {
+ $result = $this->request("RETR $msgno", true);
+ return $result;
+ }
+
+ /**
+ * Make a NOOP call, maybe needed for keeping the server happy
+ *
+ * @return null
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function noop()
+ {
+ $this->request('NOOP');
+ }
+
+
+ /**
+ * Make a DELE count to remove a message
+ *
+ * @return null
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function delete($msgno)
+ {
+ $this->request("DELE $msgno");
+ }
+
+
+ /**
+ * Make RSET call, which rollbacks delete requests
+ *
+ * @return null
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function undelete()
+ {
+ $this->request('RSET');
+ }
+}
diff --git a/lib/Zend/Mail/Protocol/Smtp.php b/lib/Zend/Mail/Protocol/Smtp.php
new file mode 100644
index 0000000..18ac77a
--- /dev/null
+++ b/lib/Zend/Mail/Protocol/Smtp.php
@@ -0,0 +1,443 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Protocol
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Smtp.php 11196 2008-09-02 00:56:25Z yoshida@zend.co.jp $
+ */
+
+
+/**
+ * @see Zend_Mime
+ */
+require_once 'Zend/Mime.php';
+
+
+/**
+ * @see Zend_Mail_Protocol_Abstract
+ */
+require_once 'Zend/Mail/Protocol/Abstract.php';
+
+
+/**
+ * Smtp implementation of Zend_Mail_Protocol_Abstract
+ *
+ * Minimum implementation according to RFC2821: EHLO, MAIL FROM, RCPT TO, DATA, RSET, NOOP, QUIT
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Protocol
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Mail_Protocol_Smtp extends Zend_Mail_Protocol_Abstract
+{
+ /**
+ * The transport method for the socket
+ *
+ * @var string
+ */
+ protected $_transport = 'tcp';
+
+
+ /**
+ * Indicates that a session is requested to be secure
+ *
+ * @var string
+ */
+ protected $_secure;
+
+
+ /**
+ * Indicates an smtp session has been started by the HELO command
+ *
+ * @var boolean
+ */
+ protected $_sess = false;
+
+
+ /**
+ * Indicates the HELO command has been issues
+ *
+ * @var unknown_type
+ */
+ protected $_helo = false;
+
+
+ /**
+ * Indicates an smtp AUTH has been issued and authenticated
+ *
+ * @var unknown_type
+ */
+ protected $_auth = false;
+
+
+ /**
+ * Indicates a MAIL command has been issued
+ *
+ * @var unknown_type
+ */
+ protected $_mail = false;
+
+
+ /**
+ * Indicates one or more RCTP commands have been issued
+ *
+ * @var unknown_type
+ */
+ protected $_rcpt = false;
+
+
+ /**
+ * Indicates that DATA has been issued and sent
+ *
+ * @var unknown_type
+ */
+ protected $_data = null;
+
+
+ /**
+ * Constructor.
+ *
+ * @param string $host
+ * @param integer $port
+ * @param array $config
+ * @return void
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function __construct($host = '127.0.0.1', $port = null, array $config = array())
+ {
+ if (isset($config['ssl'])) {
+ switch (strtolower($config['ssl'])) {
+ case 'tls':
+ $this->_secure = 'tls';
+ break;
+
+ case 'ssl':
+ $this->_transport = 'ssl';
+ $this->_secure = 'ssl';
+ if ($port == null) {
+ $port = 465;
+ }
+ break;
+
+ default:
+ /**
+ * @see Zend_Mail_Protocol_Exception
+ */
+ require_once 'Zend/Mail/Protocol/Exception.php';
+ throw new Zend_Mail_Protocol_Exception($config['ssl'] . ' is unsupported SSL type');
+ break;
+ }
+ }
+
+ // If no port has been specified then check the master PHP ini file. Defaults to 25 if the ini setting is null.
+ if ($port == null) {
+ if (($port = ini_get('smtp_port')) == '') {
+ $port = 25;
+ }
+ }
+
+ parent::__construct($host, $port);
+ }
+
+
+ /**
+ * Connect to the server with the parameters given in the constructor.
+ *
+ * @return boolean
+ */
+ public function connect()
+ {
+ return $this->_connect($this->_transport . '://' . $this->_host . ':'. $this->_port);
+ }
+
+
+ /**
+ * Initiate HELO/EHLO sequence and set flag to indicate valid smtp session
+ *
+ * @param string $host The client hostname or IP address (default: 127.0.0.1)
+ * @throws Zend_Mail_Protocol_Exception
+ * @return void
+ */
+ public function helo($host = '127.0.0.1')
+ {
+ // Respect RFC 2821 and disallow HELO attempts if session is already initiated.
+ if ($this->_sess === true) {
+ /**
+ * @see Zend_Mail_Protocol_Exception
+ */
+ require_once 'Zend/Mail/Protocol/Exception.php';
+ throw new Zend_Mail_Protocol_Exception('Cannot issue HELO to existing session');
+ }
+
+ // Validate client hostname
+ if (!$this->_validHost->isValid($host)) {
+ /**
+ * @see Zend_Mail_Protocol_Exception
+ */
+ require_once 'Zend/Mail/Protocol/Exception.php';
+ throw new Zend_Mail_Protocol_Exception(join(', ', $this->_validHost->getMessages()));
+ }
+
+ // Initiate helo sequence
+ $this->_expect(220, 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2
+ $this->_ehlo($host);
+
+ // If a TLS session is required, commence negotiation
+ if ($this->_secure == 'tls') {
+ $this->_send('STARTTLS');
+ $this->_expect(220, 180);
+ if (!stream_socket_enable_crypto($this->_socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
+ /**
+ * @see Zend_Mail_Protocol_Exception
+ */
+ require_once 'Zend/Mail/Protocol/Exception.php';
+ throw new Zend_Mail_Protocol_Exception('Unable to connect via TLS');
+ }
+ $this->_ehlo($host);
+ }
+
+ $this->_startSession();
+ $this->auth();
+ }
+
+
+ /**
+ * Send EHLO or HELO depending on capabilities of smtp host
+ *
+ * @param string $host The client hostname or IP address (default: 127.0.0.1)
+ * @throws Zend_Mail_Protocol_Exception
+ * @return void
+ */
+ protected function _ehlo($host)
+ {
+ // Support for older, less-compliant remote servers. Tries multiple attempts of EHLO or HELO.
+ try {
+ $this->_send('EHLO ' . $host);
+ $this->_expect(250, 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2
+ } catch (Zend_Mail_Protocol_Exception $e) {
+ $this->_send('HELO ' . $host);
+ $this->_expect(250, 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2
+ } catch (Zend_Mail_Protocol_Exception $e) {
+ throw $e;
+ }
+ }
+
+
+ /**
+ * Issues MAIL command
+ *
+ * @param string $from Sender mailbox
+ * @throws Zend_Mail_Protocol_Exception
+ * @return void
+ */
+ public function mail($from)
+ {
+ if ($this->_sess !== true) {
+ /**
+ * @see Zend_Mail_Protocol_Exception
+ */
+ require_once 'Zend/Mail/Protocol/Exception.php';
+ throw new Zend_Mail_Protocol_Exception('A valid session has not been started');
+ }
+
+ $this->_send('MAIL FROM:<' . $from . '>');
+ $this->_expect(250, 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2
+
+ // Set mail to true, clear recipients and any existing data flags as per 4.1.1.2 of RFC 2821
+ $this->_mail = true;
+ $this->_rcpt = false;
+ $this->_data = false;
+ }
+
+
+ /**
+ * Issues RCPT command
+ *
+ * @param string $to Receiver(s) mailbox
+ * @throws Zend_Mail_Protocol_Exception
+ * @return void
+ */
+ public function rcpt($to)
+ {
+ if ($this->_mail !== true) {
+ /**
+ * @see Zend_Mail_Protocol_Exception
+ */
+ require_once 'Zend/Mail/Protocol/Exception.php';
+ throw new Zend_Mail_Protocol_Exception('No sender reverse path has been supplied');
+ }
+
+ // Set rcpt to true, as per 4.1.1.3 of RFC 2821
+ $this->_send('RCPT TO:<' . $to . '>');
+ $this->_expect(array(250, 251), 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2
+ $this->_rcpt = true;
+ }
+
+
+ /**
+ * Issues DATA command
+ *
+ * @param string $data
+ * @throws Zend_Mail_Protocol_Exception
+ * @return void
+ */
+ public function data($data)
+ {
+ // Ensure recipients have been set
+ if ($this->_rcpt !== true) {
+ /**
+ * @see Zend_Mail_Protocol_Exception
+ */
+ require_once 'Zend/Mail/Protocol/Exception.php';
+ throw new Zend_Mail_Protocol_Exception('No recipient forward path has been supplied');
+ }
+
+ $this->_send('DATA');
+ $this->_expect(354, 120); // Timeout set for 2 minutes as per RFC 2821 4.5.3.2
+
+ foreach (explode(Zend_Mime::LINEEND, $data) as $line) {
+ if (strpos($line, '.') === 0) {
+ // Escape lines prefixed with a '.'
+ $line = '.' . $line;
+ }
+ $this->_send($line);
+ }
+
+ $this->_send('.');
+ $this->_expect(250, 600); // Timeout set for 10 minutes as per RFC 2821 4.5.3.2
+ $this->_data = true;
+ }
+
+
+ /**
+ * Issues the RSET command end validates answer
+ *
+ * Can be used to restore a clean smtp communication state when a transaction has been cancelled or commencing a new transaction.
+ *
+ * @return void
+ */
+ public function rset()
+ {
+ $this->_send('RSET');
+ // MS ESMTP doesn't follow RFC, see [ZF-1377]
+ $this->_expect(array(250, 220));
+
+ $this->_mail = false;
+ $this->_rcpt = false;
+ $this->_data = false;
+ }
+
+
+ /**
+ * Issues the NOOP command end validates answer
+ *
+ * Not used by Zend_Mail, could be used to keep a connection alive or check if it is still open.
+ *
+ * @return void
+ */
+ public function noop()
+ {
+ $this->_send('NOOP');
+ $this->_expect(250, 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2
+ }
+
+
+ /**
+ * Issues the VRFY command end validates answer
+ *
+ * Not used by Zend_Mail.
+ *
+ * @param string $user User Name or eMail to verify
+ * @return void
+ */
+ public function vrfy($user)
+ {
+ $this->_send('VRFY ' . $user);
+ $this->_expect(array(250, 251, 252), 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2
+ }
+
+
+ /**
+ * Issues the QUIT command and clears the current session
+ *
+ * @return void
+ */
+ public function quit()
+ {
+ if ($this->_sess) {
+ $this->_send('QUIT');
+ $this->_expect(221, 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2
+ $this->_stopSession();
+ }
+ }
+
+
+ /**
+ * Default authentication method
+ *
+ * This default method is implemented by AUTH adapters to properly authenticate to a remote host.
+ *
+ * @throws Zend_Mail_Protocol_Exception
+ * @return void
+ */
+ public function auth()
+ {
+ if ($this->_auth === true) {
+ /**
+ * @see Zend_Mail_Protocol_Exception
+ */
+ require_once 'Zend/Mail/Protocol/Exception.php';
+ throw new Zend_Mail_Protocol_Exception('Already authenticated for this session');
+ }
+ }
+
+
+ /**
+ * Closes connection
+ *
+ * @return void
+ */
+ public function disconnect()
+ {
+ $this->_disconnect();
+ }
+
+
+ /**
+ * Start mail session
+ *
+ * @return void
+ */
+ protected function _startSession()
+ {
+ $this->_sess = true;
+ }
+
+
+ /**
+ * Stop mail session
+ *
+ * @return void
+ */
+ protected function _stopSession()
+ {
+ $this->_sess = false;
+ }
+}
diff --git a/lib/Zend/Mail/Protocol/Smtp/Auth/Crammd5.php b/lib/Zend/Mail/Protocol/Smtp/Auth/Crammd5.php
new file mode 100644
index 0000000..337c7eb
--- /dev/null
+++ b/lib/Zend/Mail/Protocol/Smtp/Auth/Crammd5.php
@@ -0,0 +1,108 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Protocol
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Crammd5.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Mail_Protocol_Smtp
+ */
+require_once 'Zend/Mail/Protocol/Smtp.php';
+
+
+/**
+ * Performs CRAM-MD5 authentication
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Protocol
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Mail_Protocol_Smtp_Auth_Crammd5 extends Zend_Mail_Protocol_Smtp
+{
+ /**
+ * Constructor.
+ *
+ * @param string $host (Default: 127.0.0.1)
+ * @param int $port (Default: null)
+ * @param array $config Auth-specific parameters
+ * @return void
+ */
+ public function __construct($host = '127.0.0.1', $port = null, $config = null)
+ {
+ if (is_array($config)) {
+ if (isset($config['username'])) {
+ $this->_username = $config['username'];
+ }
+ if (isset($config['password'])) {
+ $this->_password = $config['password'];
+ }
+ }
+
+ parent::__construct($host, $port, $config);
+ }
+
+
+ /**
+ * @todo Perform CRAM-MD5 authentication with supplied credentials
+ *
+ * @return void
+ */
+ public function auth()
+ {
+ // Ensure AUTH has not already been initiated.
+ parent::auth();
+
+ $this->_send('AUTH CRAM-MD5');
+ $challenge = $this->_expect(334);
+ $challenge = base64_decode($challenge);
+ $digest = $this->_hmacMd5($this->_password, $challenge);
+ $this->_send(base64_encode($this->_username . ' ' . $digest));
+ $this->_expect(235);
+ $this->_auth = true;
+ }
+
+
+ /**
+ * Prepare CRAM-MD5 response to server's ticket
+ *
+ * @param string $key Challenge key (usually password)
+ * @param string $data Challenge data
+ * @param string $block Length of blocks
+ * @return string
+ */
+ protected function _hmacMd5($key, $data, $block = 64)
+ {
+ if (strlen($key) > 64) {
+ $key = pack('H32', md5($key));
+ } elseif (strlen($key) < 64) {
+ $key = str_pad($key, $block, chr(0));
+ }
+
+ $k_ipad = substr($key, 0, 64) ^ str_repeat(chr(0x36), 64);
+ $k_opad = substr($key, 0, 64) ^ str_repeat(chr(0x5C), 64);
+
+ $inner = pack('H32', md5($k_ipad . $data));
+ $digest = md5($k_opad . $inner);
+
+ return $digest;
+ }
+}
diff --git a/lib/Zend/Mail/Protocol/Smtp/Auth/Login.php b/lib/Zend/Mail/Protocol/Smtp/Auth/Login.php
new file mode 100644
index 0000000..6332207
--- /dev/null
+++ b/lib/Zend/Mail/Protocol/Smtp/Auth/Login.php
@@ -0,0 +1,98 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Protocol
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Login.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Mail_Protocol_Smtp
+ */
+require_once 'Zend/Mail/Protocol/Smtp.php';
+
+
+/**
+ * Performs LOGIN authentication
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Protocol
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Mail_Protocol_Smtp_Auth_Login extends Zend_Mail_Protocol_Smtp
+{
+ /**
+ * LOGIN username
+ *
+ * @var string
+ */
+ protected $_username;
+
+
+ /**
+ * LOGIN password
+ *
+ * @var string
+ */
+ protected $_password;
+
+
+ /**
+ * Constructor.
+ *
+ * @param string $host (Default: 127.0.0.1)
+ * @param int $port (Default: null)
+ * @param array $config Auth-specific parameters
+ * @return void
+ */
+ public function __construct($host = '127.0.0.1', $port = null, $config = null)
+ {
+ if (is_array($config)) {
+ if (isset($config['username'])) {
+ $this->_username = $config['username'];
+ }
+ if (isset($config['password'])) {
+ $this->_password = $config['password'];
+ }
+ }
+
+ parent::__construct($host, $port, $config);
+ }
+
+
+ /**
+ * Perform LOGIN authentication with supplied credentials
+ *
+ * @return void
+ */
+ public function auth()
+ {
+ // Ensure AUTH has not already been initiated.
+ parent::auth();
+
+ $this->_send('AUTH LOGIN');
+ $this->_expect(334);
+ $this->_send(base64_encode($this->_username));
+ $this->_expect(334);
+ $this->_send(base64_encode($this->_password));
+ $this->_expect(235);
+ $this->_auth = true;
+ }
+}
diff --git a/lib/Zend/Mail/Protocol/Smtp/Auth/Plain.php b/lib/Zend/Mail/Protocol/Smtp/Auth/Plain.php
new file mode 100644
index 0000000..cb8d019
--- /dev/null
+++ b/lib/Zend/Mail/Protocol/Smtp/Auth/Plain.php
@@ -0,0 +1,96 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Protocol
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Plain.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Mail_Protocol_Smtp
+ */
+require_once 'Zend/Mail/Protocol/Smtp.php';
+
+
+/**
+ * Performs PLAIN authentication
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Protocol
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Mail_Protocol_Smtp_Auth_Plain extends Zend_Mail_Protocol_Smtp
+{
+ /**
+ * PLAIN username
+ *
+ * @var string
+ */
+ protected $_username;
+
+
+ /**
+ * PLAIN password
+ *
+ * @var string
+ */
+ protected $_password;
+
+
+ /**
+ * Constructor.
+ *
+ * @param string $host (Default: 127.0.0.1)
+ * @param int $port (Default: null)
+ * @param array $config Auth-specific parameters
+ * @return void
+ */
+ public function __construct($host = '127.0.0.1', $port = null, $config = null)
+ {
+ if (is_array($config)) {
+ if (isset($config['username'])) {
+ $this->_username = $config['username'];
+ }
+ if (isset($config['password'])) {
+ $this->_password = $config['password'];
+ }
+ }
+
+ parent::__construct($host, $port, $config);
+ }
+
+
+ /**
+ * Perform PLAIN authentication with supplied credentials
+ *
+ * @return void
+ */
+ public function auth()
+ {
+ // Ensure AUTH has not already been initiated.
+ parent::auth();
+
+ $this->_send('AUTH PLAIN');
+ $this->_expect(334);
+ $this->_send(base64_encode(chr(0) . $this->_username . chr(0) . $this->_password));
+ $this->_expect(235);
+ $this->_auth = true;
+ }
+}
diff --git a/lib/Zend/Mail/Storage.php b/lib/Zend/Mail/Storage.php
new file mode 100644
index 0000000..227f496
--- /dev/null
+++ b/lib/Zend/Mail/Storage.php
@@ -0,0 +1,39 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Storage.php 9099 2008-03-30 19:35:47Z thomas $
+ */
+
+/**
+ * @category Zend
+ * @package Zend_Mail
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Mail_Storage
+{
+ // maildir and IMAP flags, using IMAP names, where possible to be able to distinguish between IMAP
+ // system flags and other flags
+ const FLAG_PASSED = 'Passed';
+ const FLAG_SEEN = '\Seen';
+ const FLAG_ANSWERED = '\Answered';
+ const FLAG_FLAGGED = '\Flagged';
+ const FLAG_DELETED = '\Deleted';
+ const FLAG_DRAFT = '\Draft';
+ const FLAG_RECENT = '\Recent';
+}
diff --git a/lib/Zend/Mail/Storage/Abstract.php b/lib/Zend/Mail/Storage/Abstract.php
new file mode 100644
index 0000000..9e84d93
--- /dev/null
+++ b/lib/Zend/Mail/Storage/Abstract.php
@@ -0,0 +1,366 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Storage
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Abstract.php 9099 2008-03-30 19:35:47Z thomas $
+ */
+
+
+/**
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Storage
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+abstract class Zend_Mail_Storage_Abstract implements Countable, ArrayAccess, SeekableIterator
+{
+ /**
+ * class capabilities with default values
+ * @var array
+ */
+ protected $_has = array('uniqueid' => true,
+ 'delete' => false,
+ 'create' => false,
+ 'top' => false,
+ 'fetchPart' => true,
+ 'flags' => false);
+
+ /**
+ * current iteration position
+ * @var int
+ */
+ protected $_iterationPos = 0;
+
+ /**
+ * maximum iteration position (= message count)
+ * @var null|int
+ */
+ protected $_iterationMax = null;
+
+ /**
+ * used message class, change it in an extened class to extend the returned message class
+ * @var string
+ */
+ protected $_messageClass = 'Zend_Mail_Message';
+
+ /**
+ * Getter for has-properties. The standard has properties
+ * are: hasFolder, hasUniqueid, hasDelete, hasCreate, hasTop
+ *
+ * The valid values for the has-properties are:
+ * - true if a feature is supported
+ * - false if a feature is not supported
+ * - null is it's not yet known or it can't be know if a feature is supported
+ *
+ * @param string $var property name
+ * @return bool supported or not
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function __get($var)
+ {
+ if (strpos($var, 'has') === 0) {
+ $var = strtolower(substr($var, 3));
+ return isset($this->_has[$var]) ? $this->_has[$var] : null;
+ }
+
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception($var . ' not found');
+ }
+
+
+ /**
+ * Get a full list of features supported by the specific mail lib and the server
+ *
+ * @return array list of features as array(featurename => true|false[|null])
+ */
+ public function getCapabilities()
+ {
+ return $this->_has;
+ }
+
+
+ /**
+ * Count messages messages in current box/folder
+ *
+ * @return int number of messages
+ * @throws Zend_Mail_Storage_Exception
+ */
+ abstract public function countMessages();
+
+
+ /**
+ * Get a list of messages with number and size
+ *
+ * @param int $id number of message
+ * @return int|array size of given message of list with all messages as array(num => size)
+ */
+ abstract public function getSize($id = 0);
+
+
+ /**
+ * Get a message with headers and body
+ *
+ * @param $id int number of message
+ * @return Zend_Mail_Message
+ */
+ abstract public function getMessage($id);
+
+
+ /**
+ * Get raw header of message or part
+ *
+ * @param int $id number of message
+ * @param null|array|string $part path to part or null for messsage header
+ * @param int $topLines include this many lines with header (after an empty line)
+ * @return string raw header
+ */
+ abstract public function getRawHeader($id, $part = null, $topLines = 0);
+
+ /**
+ * Get raw content of message or part
+ *
+ * @param int $id number of message
+ * @param null|array|string $part path to part or null for messsage content
+ * @return string raw content
+ */
+ abstract public function getRawContent($id, $part = null);
+
+ /**
+ * Create instance with parameters
+ *
+ * @param array $params mail reader specific parameters
+ * @throws Zend_Mail_Storage_Exception
+ */
+ abstract public function __construct($params);
+
+
+ /**
+ * Destructor calls close() and therefore closes the resource.
+ */
+ public function __destruct()
+ {
+ $this->close();
+ }
+
+
+ /**
+ * Close resource for mail lib. If you need to control, when the resource
+ * is closed. Otherwise the destructor would call this.
+ *
+ * @return null
+ */
+ abstract public function close();
+
+
+ /**
+ * Keep the resource alive.
+ *
+ * @return null
+ */
+ abstract public function noop();
+
+ /**
+ * delete a message from current box/folder
+ *
+ * @return null
+ */
+ abstract public function removeMessage($id);
+
+ /**
+ * get unique id for one or all messages
+ *
+ * if storage does not support unique ids it's the same as the message number
+ *
+ * @param int|null $id message number
+ * @return array|string message number for given message or all messages as array
+ * @throws Zend_Mail_Storage_Exception
+ */
+ abstract public function getUniqueId($id = null);
+
+ /**
+ * get a message number from a unique id
+ *
+ * I.e. if you have a webmailer that supports deleting messages you should use unique ids
+ * as parameter and use this method to translate it to message number right before calling removeMessage()
+ *
+ * @param string $id unique id
+ * @return int message number
+ * @throws Zend_Mail_Storage_Exception
+ */
+ abstract public function getNumberByUniqueId($id);
+
+ // interface implementations follows
+
+ /**
+ * Countable::count()
+ *
+ * @return int
+ */
+ public function count()
+ {
+ return $this->countMessages();
+ }
+
+
+ /**
+ * ArrayAccess::offsetExists()
+ *
+ * @param int $id
+ * @return boolean
+ */
+ public function offsetExists($id)
+ {
+ try {
+ if ($this->getMessage($id)) {
+ return true;
+ }
+ } catch(Zend_Mail_Storage_Exception $e) {}
+
+ return false;
+ }
+
+
+ /**
+ * ArrayAccess::offsetGet()
+ *
+ * @param int $id
+ * @return Zend_Mail_Message message object
+ */
+ public function offsetGet($id)
+ {
+ return $this->getMessage($id);
+ }
+
+
+ /**
+ * ArrayAccess::offsetSet()
+ *
+ * @param id $id
+ * @param mixed $value
+ * @throws Zend_Mail_Storage_Exception
+ * @return void
+ */
+ public function offsetSet($id, $value)
+ {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('cannot write mail messages via array access');
+ }
+
+
+ /**
+ * ArrayAccess::offsetUnset()
+ *
+ * @param int $id
+ * @return boolean success
+ */
+ public function offsetUnset($id)
+ {
+ return $this->removeMessage($id);
+ }
+
+
+ /**
+ * Iterator::rewind()
+ *
+ * Rewind always gets the new count from the storage. Thus if you use
+ * the interfaces and your scripts take long you should use reset()
+ * from time to time.
+ *
+ * @return void
+ */
+ public function rewind()
+ {
+ $this->_iterationMax = $this->countMessages();
+ $this->_iterationPos = 1;
+ }
+
+
+ /**
+ * Iterator::current()
+ *
+ * @return Zend_Mail_Message current message
+ */
+ public function current()
+ {
+ return $this->getMessage($this->_iterationPos);
+ }
+
+
+ /**
+ * Iterator::key()
+ *
+ * @return int id of current position
+ */
+ public function key()
+ {
+ return $this->_iterationPos;
+ }
+
+
+ /**
+ * Iterator::next()
+ *
+ * @return void
+ */
+ public function next()
+ {
+ ++$this->_iterationPos;
+ }
+
+
+ /**
+ * Iterator::valid()
+ *
+ * @return boolean
+ */
+ public function valid()
+ {
+ if ($this->_iterationMax === null) {
+ $this->_iterationMax = $this->countMessages();
+ }
+ return $this->_iterationPos && $this->_iterationPos <= $this->_iterationMax;
+ }
+
+
+ /**
+ * SeekableIterator::seek()
+ *
+ * @param int $pos
+ * @return void
+ * @throws OutOfBoundsException
+ */
+ public function seek($pos)
+ {
+ if ($this->_iterationMax === null) {
+ $this->_iterationMax = $this->countMessages();
+ }
+
+ if ($pos > $this->_iterationMax) {
+ throw new OutOfBoundsException('this position does not exist');
+ }
+ $this->_iterationPos = $pos;
+ }
+
+}
diff --git a/lib/Zend/Mail/Storage/Exception.php b/lib/Zend/Mail/Storage/Exception.php
new file mode 100644
index 0000000..1aea4f9
--- /dev/null
+++ b/lib/Zend/Mail/Storage/Exception.php
@@ -0,0 +1,39 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Storage
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Exception.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Mail_Exception
+ */
+require_once 'Zend/Mail/Exception.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Storage
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Mail_Storage_Exception extends Zend_Mail_Exception
+{}
+
diff --git a/lib/Zend/Mail/Storage/Folder.php b/lib/Zend/Mail/Storage/Folder.php
new file mode 100644
index 0000000..6ee9abe
--- /dev/null
+++ b/lib/Zend/Mail/Storage/Folder.php
@@ -0,0 +1,236 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Storage
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Folder.php 9099 2008-03-30 19:35:47Z thomas $
+ */
+
+
+/**
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Storage
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Mail_Storage_Folder implements RecursiveIterator
+{
+ /**
+ * subfolders of folder array(localName => Zend_Mail_Storage_Folder folder)
+ * @var array
+ */
+ protected $_folders;
+
+ /**
+ * local name (name of folder in parent folder)
+ * @var string
+ */
+ protected $_localName;
+
+ /**
+ * global name (absolute name of folder)
+ * @var string
+ */
+ protected $_globalName;
+
+ /**
+ * folder is selectable if folder is able to hold messages, else it's just a parent folder
+ * @var bool
+ */
+ protected $_selectable = true;
+
+ /**
+ * create a new mail folder instance
+ *
+ * @param string $localName name of folder in current subdirectory
+ * @param string $globalName absolute name of folder
+ * @param bool $selectable if true folder holds messages, if false it's just a parent for subfolders
+ * @param array $folders init with given instances of Zend_Mail_Storage_Folder as subfolders
+ */
+ public function __construct($localName, $globalName = '', $selectable = true, array $folders = array())
+ {
+ $this->_localName = $localName;
+ $this->_globalName = $globalName ? $globalName : $localName;
+ $this->_selectable = $selectable;
+ $this->_folders = $folders;
+ }
+
+ /**
+ * implements RecursiveIterator::hasChildren()
+ *
+ * @return bool current element has children
+ */
+ public function hasChildren()
+ {
+ $current = $this->current();
+ return $current && $current instanceof Zend_Mail_Storage_Folder && !$current->isLeaf();
+ }
+
+ /**
+ * implements RecursiveIterator::getChildren()
+ *
+ * @return Zend_Mail_Storage_Folder same as self::current()
+ */
+ public function getChildren()
+ {
+ return $this->current();
+ }
+
+ /**
+ * implements Iterator::valid()
+ *
+ * @return bool check if there's a current element
+ */
+ public function valid()
+ {
+ return key($this->_folders) !== null;
+ }
+
+ /**
+ * implements Iterator::next()
+ *
+ * @return null
+ */
+ public function next()
+ {
+ next($this->_folders);
+ }
+
+ /**
+ * implements Iterator::key()
+ *
+ * @return string key/local name of current element
+ */
+ public function key()
+ {
+ return key($this->_folders);
+ }
+
+ /**
+ * implements Iterator::current()
+ *
+ * @return Zend_Mail_Storage_Folder current folder
+ */
+ public function current()
+ {
+ return current($this->_folders);
+ }
+
+ /**
+ * implements Iterator::rewind()
+ *
+ * @return null
+ */
+ public function rewind()
+ {
+ reset($this->_folders);
+ }
+
+ /**
+ * get subfolder named $name
+ *
+ * @param string $name wanted subfolder
+ * @return Zend_Mail_Storage_Folder folder named $folder
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function __get($name)
+ {
+ if (!isset($this->_folders[$name])) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception("no subfolder named $name");
+ }
+
+ return $this->_folders[$name];
+ }
+
+ /**
+ * add or replace subfolder named $name
+ *
+ * @param string $name local name of subfolder
+ * @param Zend_Mail_Storage_Folder $folder instance for new subfolder
+ * @return null
+ */
+ public function __set($name, Zend_Mail_Storage_Folder $folder)
+ {
+ $this->_folders[$name] = $folder;
+ }
+
+ /**
+ * remove subfolder named $name
+ *
+ * @param string $name local name of subfolder
+ * @return null
+ */
+ public function __unset($name)
+ {
+ unset($this->_folders[$name]);
+ }
+
+ /**
+ * magic method for easy output of global name
+ *
+ * @return string global name of folder
+ */
+ public function __toString()
+ {
+ return (string)$this->getGlobalName();
+ }
+
+ /**
+ * get local name
+ *
+ * @return string local name
+ */
+ public function getLocalName()
+ {
+ return $this->_localName;
+ }
+
+ /**
+ * get global name
+ *
+ * @return string global name
+ */
+ public function getGlobalName()
+ {
+ return $this->_globalName;
+ }
+
+ /**
+ * is this folder selectable?
+ *
+ * @return bool selectable
+ */
+ public function isSelectable()
+ {
+ return $this->_selectable;
+ }
+
+ /**
+ * check if folder has no subfolder
+ *
+ * @return bool true if no subfolders
+ */
+ public function isLeaf()
+ {
+ return empty($this->_folders);
+ }
+}
diff --git a/lib/Zend/Mail/Storage/Folder/Interface.php b/lib/Zend/Mail/Storage/Folder/Interface.php
new file mode 100644
index 0000000..cb148b7
--- /dev/null
+++ b/lib/Zend/Mail/Storage/Folder/Interface.php
@@ -0,0 +1,60 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Storage
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Interface.php 9098 2008-03-30 19:29:10Z thomas $
+ */
+
+
+/**
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Storage
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+interface Zend_Mail_Storage_Folder_Interface
+{
+ /**
+ * get root folder or given folder
+ *
+ * @param string $rootFolder get folder structure for given folder, else root
+ * @return Zend_Mail_Storage_Folder root or wanted folder
+ */
+ public function getFolders($rootFolder = null);
+
+ /**
+ * select given folder
+ *
+ * folder must be selectable!
+ *
+ * @param Zend_Mail_Storage_Folder|string $globalName global name of folder or instance for subfolder
+ * @return null
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function selectFolder($globalName);
+
+
+ /**
+ * get Zend_Mail_Storage_Folder instance for current folder
+ *
+ * @return Zend_Mail_Storage_Folder instance of current folder
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function getCurrentFolder();
+}
diff --git a/lib/Zend/Mail/Storage/Folder/Maildir.php b/lib/Zend/Mail/Storage/Folder/Maildir.php
new file mode 100644
index 0000000..30b31aa
--- /dev/null
+++ b/lib/Zend/Mail/Storage/Folder/Maildir.php
@@ -0,0 +1,265 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Storage
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Maildir.php 9098 2008-03-30 19:29:10Z thomas $
+ */
+
+
+/**
+ * @see Zend_Mail_Storage_Folder
+ */
+require_once 'Zend/Mail/Storage/Folder.php';
+
+/**
+ * @see Zend_Mail_Storage_Folder_Interface
+ */
+require_once 'Zend/Mail/Storage/Folder/Interface.php';
+
+/**
+ * @see Zend_Mail_Storage_Maildir
+ */
+require_once 'Zend/Mail/Storage/Maildir.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Storage
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Mail_Storage_Folder_Maildir extends Zend_Mail_Storage_Maildir implements Zend_Mail_Storage_Folder_Interface
+{
+ /**
+ * Zend_Mail_Storage_Folder root folder for folder structure
+ * @var Zend_Mail_Storage_Folder
+ */
+ protected $_rootFolder;
+
+ /**
+ * rootdir of folder structure
+ * @var string
+ */
+ protected $_rootdir;
+
+ /**
+ * name of current folder
+ * @var string
+ */
+ protected $_currentFolder;
+
+ /**
+ * delim char for subfolders
+ * @var string
+ */
+ protected $_delim;
+
+ /**
+ * Create instance with parameters
+ * Supported parameters are:
+ * - dirname rootdir of maildir structure
+ * - delim delim char for folder structur, default is '.'
+ * - folder intial selected folder, default is 'INBOX'
+ *
+ * @param $params array mail reader specific parameters
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function __construct($params)
+ {
+ if (is_array($params)) {
+ $params = (object)$params;
+ }
+
+ if (!isset($params->dirname) || !is_dir($params->dirname)) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('no valid dirname given in params');
+ }
+
+ $this->_rootdir = rtrim($params->dirname, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
+
+ $this->_delim = isset($params->delim) ? $params->delim : '.';
+
+ $this->_buildFolderTree();
+ $this->selectFolder(!empty($params->folder) ? $params->folder : 'INBOX');
+ $this->_has['top'] = true;
+ $this->_has['flags'] = true;
+ }
+
+ /**
+ * find all subfolders and mbox files for folder structure
+ *
+ * Result is save in Zend_Mail_Storage_Folder instances with the root in $this->_rootFolder.
+ * $parentFolder and $parentGlobalName are only used internally for recursion.
+ *
+ * @return null
+ * @throws Zend_Mail_Storage_Exception
+ */
+ protected function _buildFolderTree()
+ {
+ $this->_rootFolder = new Zend_Mail_Storage_Folder('/', '/', false);
+ $this->_rootFolder->INBOX = new Zend_Mail_Storage_Folder('INBOX', 'INBOX', true);
+
+ $dh = @opendir($this->_rootdir);
+ if (!$dh) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception("can't read folders in maildir");
+ }
+ $dirs = array();
+ while (($entry = readdir($dh)) !== false) {
+ // maildir++ defines folders must start with .
+ if ($entry[0] != '.' || $entry == '.' || $entry == '..') {
+ continue;
+ }
+ if ($this->_isMaildir($this->_rootdir . $entry)) {
+ $dirs[] = $entry;
+ }
+ }
+ closedir($dh);
+
+ sort($dirs);
+ $stack = array(null);
+ $folderStack = array(null);
+ $parentFolder = $this->_rootFolder;
+ $parent = '.';
+
+ foreach ($dirs as $dir) {
+ do {
+ if (strpos($dir, $parent) === 0) {
+ $local = substr($dir, strlen($parent));
+ if (strpos($local, $this->_delim) !== false) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('error while reading maildir');
+ }
+ array_push($stack, $parent);
+ $parent = $dir . $this->_delim;
+ $folder = new Zend_Mail_Storage_Folder($local, substr($dir, 1), true);
+ $parentFolder->$local = $folder;
+ array_push($folderStack, $parentFolder);
+ $parentFolder = $folder;
+ break;
+ } else if ($stack) {
+ $parent = array_pop($stack);
+ $parentFolder = array_pop($folderStack);
+ }
+ } while ($stack);
+ if (!$stack) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('error while reading maildir');
+ }
+ }
+ }
+
+ /**
+ * get root folder or given folder
+ *
+ * @param string $rootFolder get folder structure for given folder, else root
+ * @return Zend_Mail_Storage_Folder root or wanted folder
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function getFolders($rootFolder = null)
+ {
+ if (!$rootFolder || $rootFolder == 'INBOX') {
+ return $this->_rootFolder;
+ }
+
+ // rootdir is same as INBOX in maildir
+ if (strpos($rootFolder, 'INBOX' . $this->_delim) === 0) {
+ $rootFolder = substr($rootFolder, 6);
+ }
+ $currentFolder = $this->_rootFolder;
+ $subname = trim($rootFolder, $this->_delim);
+ while ($currentFolder) {
+ @list($entry, $subname) = @explode($this->_delim, $subname, 2);
+ $currentFolder = $currentFolder->$entry;
+ if (!$subname) {
+ break;
+ }
+ }
+
+ if ($currentFolder->getGlobalName() != rtrim($rootFolder, $this->_delim)) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception("folder $rootFolder not found");
+ }
+ return $currentFolder;
+ }
+
+ /**
+ * select given folder
+ *
+ * folder must be selectable!
+ *
+ * @param Zend_Mail_Storage_Folder|string $globalName global name of folder or instance for subfolder
+ * @return null
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function selectFolder($globalName)
+ {
+ $this->_currentFolder = (string)$globalName;
+
+ // getting folder from folder tree for validation
+ $folder = $this->getFolders($this->_currentFolder);
+
+ try {
+ $this->_openMaildir($this->_rootdir . '.' . $folder->getGlobalName());
+ } catch(Zend_Mail_Storage_Exception $e) {
+ // check what went wrong
+ if (!$folder->isSelectable()) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception("{$this->_currentFolder} is not selectable");
+ }
+ // seems like file has vanished; rebuilding folder tree - but it's still an exception
+ $this->_buildFolderTree($this->_rootdir);
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('seems like the maildir has vanished, I\'ve rebuild the ' .
+ 'folder tree, search for an other folder and try again');
+ }
+ }
+
+ /**
+ * get Zend_Mail_Storage_Folder instance for current folder
+ *
+ * @return Zend_Mail_Storage_Folder instance of current folder
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function getCurrentFolder()
+ {
+ return $this->_currentFolder;
+ }
+}
diff --git a/lib/Zend/Mail/Storage/Folder/Mbox.php b/lib/Zend/Mail/Storage/Folder/Mbox.php
new file mode 100644
index 0000000..797148d
--- /dev/null
+++ b/lib/Zend/Mail/Storage/Folder/Mbox.php
@@ -0,0 +1,264 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Storage
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Mbox.php 9098 2008-03-30 19:29:10Z thomas $
+ */
+
+
+/**
+ * @see Zend_Mail_Storage_Folder
+ */
+require_once 'Zend/Mail/Storage/Folder.php';
+
+/**
+ * @see Zend_Mail_Storage_Folder_Interface
+ */
+require_once 'Zend/Mail/Storage/Folder/Interface.php';
+
+/**
+ * @see Zend_Mail_Storage_Mbox
+ */
+require_once 'Zend/Mail/Storage/Mbox.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Storage
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Mail_Storage_Folder_Mbox extends Zend_Mail_Storage_Mbox implements Zend_Mail_Storage_Folder_Interface
+{
+ /**
+ * Zend_Mail_Storage_Folder root folder for folder structure
+ * @var Zend_Mail_Storage_Folder
+ */
+ protected $_rootFolder;
+
+ /**
+ * rootdir of folder structure
+ * @var string
+ */
+ protected $_rootdir;
+
+ /**
+ * name of current folder
+ * @var string
+ */
+ protected $_currentFolder;
+
+ /**
+ * Create instance with parameters
+ *
+ * Disallowed parameters are:
+ * - filename use Zend_Mail_Storage_Mbox for a single file
+ * Supported parameters are:
+ * - dirname rootdir of mbox structure
+ * - folder intial selected folder, default is 'INBOX'
+ *
+ * @param $params array mail reader specific parameters
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function __construct($params)
+ {
+ if (is_array($params)) {
+ $params = (object)$params;
+ }
+
+ if (isset($params->filename)) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('use Zend_Mail_Storage_Mbox for a single file');
+ }
+
+ if (!isset($params->dirname) || !is_dir($params->dirname)) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('no valid dirname given in params');
+ }
+
+ $this->_rootdir = rtrim($params->dirname, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
+
+ $this->_buildFolderTree($this->_rootdir);
+ $this->selectFolder(!empty($params->folder) ? $params->folder : 'INBOX');
+ $this->_has['top'] = true;
+ $this->_has['uniqueid'] = false;
+ }
+
+ /**
+ * find all subfolders and mbox files for folder structure
+ *
+ * Result is save in Zend_Mail_Storage_Folder instances with the root in $this->_rootFolder.
+ * $parentFolder and $parentGlobalName are only used internally for recursion.
+ *
+ * @param string $currentDir call with root dir, also used for recursion.
+ * @param Zend_Mail_Storage_Folder|null $parentFolder used for recursion
+ * @param string $parentGlobalName used for rescursion
+ * @return null
+ * @throws Zend_Mail_Storage_Exception
+ */
+ protected function _buildFolderTree($currentDir, $parentFolder = null, $parentGlobalName = '')
+ {
+ if (!$parentFolder) {
+ $this->_rootFolder = new Zend_Mail_Storage_Folder('/', '/', false);
+ $parentFolder = $this->_rootFolder;
+ }
+
+ $dh = @opendir($currentDir);
+ if (!$dh) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception("can't read dir $currentDir");
+ }
+ while (($entry = readdir($dh)) !== false) {
+ // ignore hidden files for mbox
+ if ($entry[0] == '.') {
+ continue;
+ }
+ $absoluteEntry = $currentDir . $entry;
+ $globalName = $parentGlobalName . DIRECTORY_SEPARATOR . $entry;
+ if (is_file($absoluteEntry) && $this->_isMboxFile($absoluteEntry)) {
+ $parentFolder->$entry = new Zend_Mail_Storage_Folder($entry, $globalName);
+ continue;
+ }
+ if (!is_dir($absoluteEntry) /* || $entry == '.' || $entry == '..' */) {
+ continue;
+ }
+ $folder = new Zend_Mail_Storage_Folder($entry, $globalName, false);
+ $parentFolder->$entry = $folder;
+ $this->_buildFolderTree($absoluteEntry . DIRECTORY_SEPARATOR, $folder, $globalName);
+ }
+
+ closedir($dh);
+ }
+
+ /**
+ * get root folder or given folder
+ *
+ * @param string $rootFolder get folder structure for given folder, else root
+ * @return Zend_Mail_Storage_Folder root or wanted folder
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function getFolders($rootFolder = null)
+ {
+ if (!$rootFolder) {
+ return $this->_rootFolder;
+ }
+
+ $currentFolder = $this->_rootFolder;
+ $subname = trim($rootFolder, DIRECTORY_SEPARATOR);
+ while ($currentFolder) {
+ @list($entry, $subname) = @explode(DIRECTORY_SEPARATOR, $subname, 2);
+ $currentFolder = $currentFolder->$entry;
+ if (!$subname) {
+ break;
+ }
+ }
+
+ if ($currentFolder->getGlobalName() != DIRECTORY_SEPARATOR . trim($rootFolder, DIRECTORY_SEPARATOR)) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception("folder $rootFolder not found");
+ }
+ return $currentFolder;
+ }
+
+ /**
+ * select given folder
+ *
+ * folder must be selectable!
+ *
+ * @param Zend_Mail_Storage_Folder|string $globalName global name of folder or instance for subfolder
+ * @return null
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function selectFolder($globalName)
+ {
+ $this->_currentFolder = (string)$globalName;
+
+ // getting folder from folder tree for validation
+ $folder = $this->getFolders($this->_currentFolder);
+
+ try {
+ $this->_openMboxFile($this->_rootdir . $folder->getGlobalName());
+ } catch(Zend_Mail_Storage_Exception $e) {
+ // check what went wrong
+ if (!$folder->isSelectable()) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception("{$this->_currentFolder} is not selectable");
+ }
+ // seems like file has vanished; rebuilding folder tree - but it's still an exception
+ $this->_buildFolderTree($this->_rootdir);
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('seems like the mbox file has vanished, I\'ve rebuild the ' .
+ 'folder tree, search for an other folder and try again');
+ }
+ }
+
+ /**
+ * get Zend_Mail_Storage_Folder instance for current folder
+ *
+ * @return Zend_Mail_Storage_Folder instance of current folder
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function getCurrentFolder()
+ {
+ return $this->_currentFolder;
+ }
+
+ /**
+ * magic method for serialize()
+ *
+ * with this method you can cache the mbox class
+ *
+ * @return array name of variables
+ */
+ public function __sleep()
+ {
+ return array_merge(parent::__sleep(), array('_currentFolder', '_rootFolder', '_rootdir'));
+ }
+
+ /**
+ * magic method for unserialize()
+ *
+ * with this method you can cache the mbox class
+ *
+ * @return null
+ */
+ public function __wakeup()
+ {
+ // if cache is stall selectFolder() rebuilds the tree on error
+ parent::__wakeup();
+ }
+}
diff --git a/lib/Zend/Mail/Storage/Imap.php b/lib/Zend/Mail/Storage/Imap.php
new file mode 100644
index 0000000..f4051f9
--- /dev/null
+++ b/lib/Zend/Mail/Storage/Imap.php
@@ -0,0 +1,644 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Storage
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Imap.php 12519 2008-11-10 18:41:24Z alexander $
+ */
+
+
+/**
+ * @see Zend_Mail_Storage_Abstract
+ */
+require_once 'Zend/Mail/Storage/Abstract.php';
+
+/**
+ * @see Zend_Mail_Protocol_Imap
+ */
+require_once 'Zend/Mail/Protocol/Imap.php';
+
+/**
+ * @see Zend_Mail_Storage_Writable_Interface
+ */
+require_once 'Zend/Mail/Storage/Writable/Interface.php';
+
+/**
+ * @see Zend_Mail_Storage_Folder_Interface
+ */
+require_once 'Zend/Mail/Storage/Folder/Interface.php';
+
+/**
+ * @see Zend_Mail_Storage_Folder
+ */
+require_once 'Zend/Mail/Storage/Folder.php';
+
+/**
+ * @see Zend_Mail_Message
+ */
+require_once 'Zend/Mail/Message.php';
+
+/**
+ * @see Zend_Mail_Storage
+ */
+require_once 'Zend/Mail/Storage.php';
+
+/**
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Storage
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Mail_Storage_Imap extends Zend_Mail_Storage_Abstract
+ implements Zend_Mail_Storage_Folder_Interface, Zend_Mail_Storage_Writable_Interface
+{
+ // TODO: with an internal cache we could optimize this class, or create an extra class with
+ // such optimizations. Especially the various fetch calls could be combined to one cache call
+
+ /**
+ * protocol handler
+ * @var null|Zend_Mail_Protocol_Imap
+ */
+ protected $_protocol;
+
+ /**
+ * name of current folder
+ * @var string
+ */
+ protected $_currentFolder = '';
+
+ /**
+ * imap flags to constants translation
+ * @var array
+ */
+ protected static $_knownFlags = array('\Passed' => Zend_Mail_Storage::FLAG_PASSED,
+ '\Answered' => Zend_Mail_Storage::FLAG_ANSWERED,
+ '\Seen' => Zend_Mail_Storage::FLAG_SEEN,
+ '\Deleted' => Zend_Mail_Storage::FLAG_DELETED,
+ '\Draft' => Zend_Mail_Storage::FLAG_DRAFT,
+ '\Flagged' => Zend_Mail_Storage::FLAG_FLAGGED);
+
+ /**
+ * map flags to search criterias
+ * @var array
+ */
+ protected static $_searchFlags = array('\Recent' => 'RECENT',
+ '\Answered' => 'ANSWERED',
+ '\Seen' => 'SEEN',
+ '\Deleted' => 'DELETED',
+ '\Draft' => 'DRAFT',
+ '\Flagged' => 'FLAGGED');
+
+ /**
+ * Count messages all messages in current box
+ *
+ * @return int number of messages
+ * @throws Zend_Mail_Storage_Exception
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function countMessages($flags = null)
+ {
+ if (!$this->_currentFolder) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('No selected folder to count');
+ }
+
+ if ($flags === null) {
+ return count($this->_protocol->search(array('ALL')));
+ }
+
+ $params = array();
+ foreach ((array)$flags as $flag) {
+ if (isset(self::$_searchFlags[$flag])) {
+ $params[] = self::$_searchFlags[$flag];
+ } else {
+ $params[] = 'KEYWORD';
+ $params[] = $this->_protocol->escapeString($flag);
+ }
+ }
+ return count($this->_protocol->search($params));
+ }
+
+ /**
+ * get a list of messages with number and size
+ *
+ * @param int $id number of message
+ * @return int|array size of given message of list with all messages as array(num => size)
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function getSize($id = 0)
+ {
+ if ($id) {
+ return $this->_protocol->fetch('RFC822.SIZE', $id);
+ }
+ return $this->_protocol->fetch('RFC822.SIZE', 1, INF);
+ }
+
+ /**
+ * Fetch a message
+ *
+ * @param int $id number of message
+ * @return Zend_Mail_Message
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function getMessage($id)
+ {
+ $data = $this->_protocol->fetch(array('FLAGS', 'RFC822.HEADER'), $id);
+ $header = $data['RFC822.HEADER'];
+
+ $flags = array();
+ foreach ($data['FLAGS'] as $flag) {
+ $flags[] = isset(self::$_knownFlags[$flag]) ? self::$_knownFlags[$flag] : $flag;
+ }
+
+ return new $this->_messageClass(array('handler' => $this, 'id' => $id, 'headers' => $header, 'flags' => $flags));
+ }
+
+ /*
+ * Get raw header of message or part
+ *
+ * @param int $id number of message
+ * @param null|array|string $part path to part or null for messsage header
+ * @param int $topLines include this many lines with header (after an empty line)
+ * @param int $topLines include this many lines with header (after an empty line)
+ * @return string raw header
+ * @throws Zend_Mail_Protocol_Exception
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function getRawHeader($id, $part = null, $topLines = 0)
+ {
+ if ($part !== null) {
+ // TODO: implement
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('not implemented');
+ }
+
+ // TODO: toplines
+ return $this->_protocol->fetch('RFC822.HEADER', $id);
+ }
+
+ /*
+ * Get raw content of message or part
+ *
+ * @param int $id number of message
+ * @param null|array|string $part path to part or null for messsage content
+ * @return string raw content
+ * @throws Zend_Mail_Protocol_Exception
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function getRawContent($id, $part = null)
+ {
+ if ($part !== null) {
+ // TODO: implement
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('not implemented');
+ }
+
+ return $this->_protocol->fetch('RFC822.TEXT', $id);
+ }
+
+ /**
+ * create instance with parameters
+ * Supported paramters are
+ * - user username
+ * - host hostname or ip address of IMAP server [optional, default = 'localhost']
+ * - password password for user 'username' [optional, default = '']
+ * - port port for IMAP server [optional, default = 110]
+ * - ssl 'SSL' or 'TLS' for secure sockets
+ * - folder select this folder [optional, default = 'INBOX']
+ *
+ * @param array $params mail reader specific parameters
+ * @throws Zend_Mail_Storage_Exception
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function __construct($params)
+ {
+ if (is_array($params)) {
+ $params = (object)$params;
+ }
+
+ $this->_has['flags'] = true;
+
+ if ($params instanceof Zend_Mail_Protocol_Imap) {
+ $this->_protocol = $params;
+ try {
+ $this->selectFolder('INBOX');
+ } catch(Zend_Mail_Storage_Exception $e) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('cannot select INBOX, is this a valid transport?');
+ }
+ return;
+ }
+
+ if (!isset($params->user)) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('need at least user in params');
+ }
+
+ $host = isset($params->host) ? $params->host : 'localhost';
+ $password = isset($params->password) ? $params->password : '';
+ $port = isset($params->port) ? $params->port : null;
+ $ssl = isset($params->ssl) ? $params->ssl : false;
+
+ $this->_protocol = new Zend_Mail_Protocol_Imap();
+ $this->_protocol->connect($host, $port, $ssl);
+ if (!$this->_protocol->login($params->user, $password)) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('cannot login, user or password wrong');
+ }
+ $this->selectFolder(isset($params->folder) ? $params->folder : 'INBOX');
+ }
+
+ /**
+ * Close resource for mail lib. If you need to control, when the resource
+ * is closed. Otherwise the destructor would call this.
+ *
+ * @return null
+ */
+ public function close()
+ {
+ $this->_currentFolder = '';
+ $this->_protocol->logout();
+ }
+
+ /**
+ * Keep the server busy.
+ *
+ * @return null
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function noop()
+ {
+ if (!$this->_protocol->noop()) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('could not do nothing');
+ }
+ }
+
+ /**
+ * Remove a message from server. If you're doing that from a web enviroment
+ * you should be careful and use a uniqueid as parameter if possible to
+ * identify the message.
+ *
+ * @param int $id number of message
+ * @return null
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function removeMessage($id)
+ {
+ if (!$this->_protocol->store(array(Zend_Mail_Storage::FLAG_DELETED), $id, null, '+')) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('cannot set deleted flag');
+ }
+ // TODO: expunge here or at close? we can handle an error here better and are more fail safe
+ if (!$this->_protocol->expunge()) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('message marked as deleted, but could not expunge');
+ }
+ }
+
+ /**
+ * get unique id for one or all messages
+ *
+ * if storage does not support unique ids it's the same as the message number
+ *
+ * @param int|null $id message number
+ * @return array|string message number for given message or all messages as array
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function getUniqueId($id = null)
+ {
+ if ($id) {
+ return $this->_protocol->fetch('UID', $id);
+ }
+
+ return $this->_protocol->fetch('UID', 1, INF);
+ }
+
+ /**
+ * get a message number from a unique id
+ *
+ * I.e. if you have a webmailer that supports deleting messages you should use unique ids
+ * as parameter and use this method to translate it to message number right before calling removeMessage()
+ *
+ * @param string $id unique id
+ * @return int message number
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function getNumberByUniqueId($id)
+ {
+ // TODO: use search to find number directly
+ $ids = $this->getUniqueId();
+ foreach ($ids as $k => $v) {
+ if ($v == $id) {
+ return $k;
+ }
+ }
+
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('unique id not found');
+ }
+
+
+ /**
+ * get root folder or given folder
+ *
+ * @param string $rootFolder get folder structure for given folder, else root
+ * @return Zend_Mail_Storage_Folder root or wanted folder
+ * @throws Zend_Mail_Storage_Exception
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function getFolders($rootFolder = null)
+ {
+ $folders = $this->_protocol->listMailbox((string)$rootFolder);
+ if (!$folders) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('folder not found');
+ }
+
+ ksort($folders, SORT_STRING);
+ $root = new Zend_Mail_Storage_Folder('/', '/', false);
+ $stack = array(null);
+ $folderStack = array(null);
+ $parentFolder = $root;
+ $parent = '';
+
+ foreach ($folders as $globalName => $data) {
+ do {
+ if (!$parent || strpos($globalName, $parent) === 0) {
+ $pos = strrpos($globalName, $data['delim']);
+ if ($pos === false) {
+ $localName = $globalName;
+ } else {
+ $localName = substr($globalName, $pos + 1);
+ }
+ $selectable = !$data['flags'] || !in_array('\\Noselect', $data['flags']);
+
+ array_push($stack, $parent);
+ $parent = $globalName . $data['delim'];
+ $folder = new Zend_Mail_Storage_Folder($localName, $globalName, $selectable);
+ $parentFolder->$localName = $folder;
+ array_push($folderStack, $parentFolder);
+ $parentFolder = $folder;
+ break;
+ } else if ($stack) {
+ $parent = array_pop($stack);
+ $parentFolder = array_pop($folderStack);
+ }
+ } while ($stack);
+ if (!$stack) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('error while constructing folder tree');
+ }
+ }
+
+ return $root;
+ }
+
+ /**
+ * select given folder
+ *
+ * folder must be selectable!
+ *
+ * @param Zend_Mail_Storage_Folder|string $globalName global name of folder or instance for subfolder
+ * @return null
+ * @throws Zend_Mail_Storage_Exception
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function selectFolder($globalName)
+ {
+ $this->_currentFolder = $globalName;
+ if (!$this->_protocol->select($this->_currentFolder)) {
+ $this->_currentFolder = '';
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('cannot change folder, maybe it does not exist');
+ }
+ }
+
+
+ /**
+ * get Zend_Mail_Storage_Folder instance for current folder
+ *
+ * @return Zend_Mail_Storage_Folder instance of current folder
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function getCurrentFolder()
+ {
+ return $this->_currentFolder;
+ }
+
+ /**
+ * create a new folder
+ *
+ * This method also creates parent folders if necessary. Some mail storages may restrict, which folder
+ * may be used as parent or which chars may be used in the folder name
+ *
+ * @param string $name global name of folder, local name if $parentFolder is set
+ * @param string|Zend_Mail_Storage_Folder $parentFolder parent folder for new folder, else root folder is parent
+ * @return null
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function createFolder($name, $parentFolder = null)
+ {
+ // TODO: we assume / as the hierarchy delim - need to get that from the folder class!
+ if ($parentFolder instanceof Zend_Mail_Storage_Folder) {
+ $folder = $parentFolder->getGlobalName() . '/' . $name;
+ } else if ($parentFolder != null) {
+ $folder = $parentFolder . '/' . $name;
+ } else {
+ $folder = $name;
+ }
+
+ if (!$this->_protocol->create($folder)) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('cannot create folder');
+ }
+ }
+
+ /**
+ * remove a folder
+ *
+ * @param string|Zend_Mail_Storage_Folder $name name or instance of folder
+ * @return null
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function removeFolder($name)
+ {
+ if ($name instanceof Zend_Mail_Storage_Folder) {
+ $name = $name->getGlobalName();
+ }
+
+ if (!$this->_protocol->delete($name)) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('cannot delete folder');
+ }
+ }
+
+ /**
+ * rename and/or move folder
+ *
+ * The new name has the same restrictions as in createFolder()
+ *
+ * @param string|Zend_Mail_Storage_Folder $oldName name or instance of folder
+ * @param string $newName new global name of folder
+ * @return null
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function renameFolder($oldName, $newName)
+ {
+ if ($oldName instanceof Zend_Mail_Storage_Folder) {
+ $oldName = $oldName->getGlobalName();
+ }
+
+ if (!$this->_protocol->rename($oldName, $newName)) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('cannot rename folder');
+ }
+ }
+
+ /**
+ * append a new message to mail storage
+ *
+ * @param string $message message as string or instance of message class
+ * @param null|string|Zend_Mail_Storage_Folder $folder folder for new message, else current folder is taken
+ * @param null|array $flags set flags for new message, else a default set is used
+ * @throws Zend_Mail_Storage_Exception
+ */
+ // not yet * @param string|Zend_Mail_Message|Zend_Mime_Message $message message as string or instance of message class
+ public function appendMessage($message, $folder = null, $flags = null)
+ {
+ if ($folder === null) {
+ $folder = $this->_currentFolder;
+ }
+
+ if ($flags === null) {
+ $flags = array(Zend_Mail_Storage::FLAG_SEEN);
+ }
+
+ // TODO: handle class instances for $message
+ if (!$this->_protocol->append($folder, $message, $flags)) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('cannot create message, please check if the folder exists and your flags');
+ }
+ }
+
+ /**
+ * copy an existing message
+ *
+ * @param int $id number of message
+ * @param string|Zend_Mail_Storage_Folder $folder name or instance of targer folder
+ * @return null
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function copyMessage($id, $folder)
+ {
+ if (!$this->_protocol->copy($folder, $id)) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('cannot copy message, does the folder exist?');
+ }
+ }
+
+ /**
+ * move an existing message
+ *
+ * NOTE: imap has no native move command, thus it's emulated with copy and delete
+ *
+ * @param int $id number of message
+ * @param string|Zend_Mail_Storage_Folder $folder name or instance of targer folder
+ * @return null
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function moveMessage($id, $folder) {
+ $this->copyMessage($id, $folder);
+ $this->removeMessage($id);
+ }
+
+ /**
+ * set flags for message
+ *
+ * NOTE: this method can't set the recent flag.
+ *
+ * @param int $id number of message
+ * @param array $flags new flags for message
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function setFlags($id, $flags)
+ {
+ if (!$this->_protocol->store($flags, $id)) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('cannot set flags, have you tried to set the recent flag or special chars?');
+ }
+ }
+}
+
diff --git a/lib/Zend/Mail/Storage/Maildir.php b/lib/Zend/Mail/Storage/Maildir.php
new file mode 100644
index 0000000..429636a
--- /dev/null
+++ b/lib/Zend/Mail/Storage/Maildir.php
@@ -0,0 +1,475 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Storage
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Maildir.php 12519 2008-11-10 18:41:24Z alexander $
+ */
+
+
+/**
+ * @see Zend_Mail_Storage_Abstract
+ */
+require_once 'Zend/Mail/Storage/Abstract.php';
+
+/**
+ * @see Zend_Mail_Message_File
+ */
+require_once 'Zend/Mail/Message/File.php';
+
+/**
+ * @see Zend_Mail_Storage
+ */
+require_once 'Zend/Mail/Storage.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Storage
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Mail_Storage_Maildir extends Zend_Mail_Storage_Abstract
+{
+ /**
+ * used message class, change it in an extened class to extend the returned message class
+ * @var string
+ */
+ protected $_messageClass = 'Zend_Mail_Message_File';
+
+ /**
+ * data of found message files in maildir dir
+ * @var array
+ */
+ protected $_files = array();
+
+ /**
+ * known flag chars in filenames
+ *
+ * This list has to be in alphabetical order for setFlags()
+ *
+ * @var array
+ */
+ protected static $_knownFlags = array('D' => Zend_Mail_Storage::FLAG_DRAFT,
+ 'F' => Zend_Mail_Storage::FLAG_FLAGGED,
+ 'P' => Zend_Mail_Storage::FLAG_PASSED,
+ 'R' => Zend_Mail_Storage::FLAG_ANSWERED,
+ 'S' => Zend_Mail_Storage::FLAG_SEEN,
+ 'T' => Zend_Mail_Storage::FLAG_DELETED);
+
+ // TODO: getFlags($id) for fast access if headers are not needed (i.e. just setting flags)?
+
+ /**
+ * Count messages all messages in current box
+ *
+ * @return int number of messages
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function countMessages($flags = null)
+ {
+ if ($flags === null) {
+ return count($this->_files);
+ }
+
+ $count = 0;
+ if (!is_array($flags)) {
+ foreach ($this->_files as $file) {
+ if (isset($file['flaglookup'][$flags])) {
+ ++$count;
+ }
+ }
+ return $count;
+ }
+
+ $flags = array_flip($flags);
+ foreach ($this->_files as $file) {
+ foreach ($flags as $flag => $v) {
+ if (!isset($file['flaglookup'][$flag])) {
+ continue 2;
+ }
+ }
+ ++$count;
+ }
+ return $count;
+ }
+
+ /**
+ * Get one or all fields from file structure. Also checks if message is valid
+ *
+ * @param int $id message number
+ * @param string|null $field wanted field
+ * @return string|array wanted field or all fields as array
+ * @throws Zend_Mail_Storage_Exception
+ */
+ protected function _getFileData($id, $field = null)
+ {
+ if (!isset($this->_files[$id - 1])) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('id does not exist');
+ }
+
+ if (!$field) {
+ return $this->_files[$id - 1];
+ }
+
+ if (!isset($this->_files[$id - 1][$field])) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('field does not exist');
+ }
+
+ return $this->_files[$id - 1][$field];
+ }
+
+ /**
+ * Get a list of messages with number and size
+ *
+ * @param int|null $id number of message or null for all messages
+ * @return int|array size of given message of list with all messages as array(num => size)
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function getSize($id = null)
+ {
+ if ($id !== null) {
+ $filedata = $this->_getFileData($id);
+ return isset($filedata['size']) ? $filedata['size'] : filesize($filedata['filename']);
+ }
+
+ $result = array();
+ foreach ($this->_files as $num => $data) {
+ $result[$num + 1] = isset($data['size']) ? $data['size'] : filesize($data['filename']);
+ }
+
+ return $result;
+ }
+
+
+
+ /**
+ * Fetch a message
+ *
+ * @param int $id number of message
+ * @return Zend_Mail_Message_File
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function getMessage($id)
+ {
+ // TODO that's ugly, would be better to let the message class decide
+ if (strtolower($this->_messageClass) == 'zend_mail_message_file' || is_subclass_of($this->_messageClass, 'zend_mail_message_file')) {
+ return new $this->_messageClass(array('file' => $this->_getFileData($id, 'filename'),
+ 'flags' => $this->_getFileData($id, 'flags')));
+ }
+
+ return new $this->_messageClass(array('handler' => $this, 'id' => $id, 'headers' => $this->getRawHeader($id),
+ 'flags' => $this->_getFileData($id, 'flags')));
+ }
+
+ /*
+ * Get raw header of message or part
+ *
+ * @param int $id number of message
+ * @param null|array|string $part path to part or null for messsage header
+ * @param int $topLines include this many lines with header (after an empty line)
+ * @return string raw header
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function getRawHeader($id, $part = null, $topLines = 0)
+ {
+ if ($part !== null) {
+ // TODO: implement
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('not implemented');
+ }
+
+ $fh = fopen($this->_getFileData($id, 'filename'), 'r');
+
+ $content = '';
+ while (!feof($fh)) {
+ $line = fgets($fh);
+ if (!trim($line)) {
+ break;
+ }
+ $content .= $line;
+ }
+
+ fclose($fh);
+ return $content;
+ }
+
+ /*
+ * Get raw content of message or part
+ *
+ * @param int $id number of message
+ * @param null|array|string $part path to part or null for messsage content
+ * @return string raw content
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function getRawContent($id, $part = null)
+ {
+ if ($part !== null) {
+ // TODO: implement
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('not implemented');
+ }
+
+ $fh = fopen($this->_getFileData($id, 'filename'), 'r');
+
+ while (!feof($fh)) {
+ $line = fgets($fh);
+ if (!trim($line)) {
+ break;
+ }
+ }
+
+ $content = stream_get_contents($fh);
+ fclose($fh);
+ return $content;
+ }
+
+ /**
+ * Create instance with parameters
+ * Supported parameters are:
+ * - dirname dirname of mbox file
+ *
+ * @param $params array mail reader specific parameters
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function __construct($params)
+ {
+ if (is_array($params)) {
+ $params = (object)$params;
+ }
+
+ if (!isset($params->dirname) || !is_dir($params->dirname)) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('no valid dirname given in params');
+ }
+
+ if (!$this->_isMaildir($params->dirname)) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('invalid maildir given');
+ }
+
+ $this->_has['top'] = true;
+ $this->_has['flags'] = true;
+ $this->_openMaildir($params->dirname);
+ }
+
+ /**
+ * check if a given dir is a valid maildir
+ *
+ * @param string $dirname name of dir
+ * @return bool dir is valid maildir
+ */
+ protected function _isMaildir($dirname)
+ {
+ if (file_exists($dirname . '/new') && !is_dir($dirname . '/new')) {
+ return false;
+ }
+ if (file_exists($dirname . '/tmp') && !is_dir($dirname . '/tmp')) {
+ return false;
+ }
+ return is_dir($dirname . '/cur');
+ }
+
+ /**
+ * open given dir as current maildir
+ *
+ * @param string $dirname name of maildir
+ * @return null
+ * @throws Zend_Mail_Storage_Exception
+ */
+ protected function _openMaildir($dirname)
+ {
+ if ($this->_files) {
+ $this->close();
+ }
+
+ $dh = @opendir($dirname . '/cur/');
+ if (!$dh) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('cannot open maildir');
+ }
+ $this->_getMaildirFiles($dh, $dirname . '/cur/');
+ closedir($dh);
+
+ $dh = @opendir($dirname . '/new/');
+ if ($dh) {
+ $this->_getMaildirFiles($dh, $dirname . '/new/', array(Zend_Mail_Storage::FLAG_RECENT));
+ closedir($dh);
+ } else if (file_exists($dirname . '/new/')) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('cannot read recent mails in maildir');
+ }
+ }
+
+ /**
+ * find all files in opened dir handle and add to maildir files
+ *
+ * @param resource $dh dir handle used for search
+ * @param string $dirname dirname of dir in $dh
+ * @param array $default_flags default flags for given dir
+ * @return null
+ */
+ protected function _getMaildirFiles($dh, $dirname, $default_flags = array())
+ {
+ while (($entry = readdir($dh)) !== false) {
+ if ($entry[0] == '.' || !is_file($dirname . $entry)) {
+ continue;
+ }
+
+ @list($uniq, $info) = explode(':', $entry, 2);
+ @list(,$size) = explode(',', $uniq, 2);
+ if ($size && $size[0] == 'S' && $size[1] == '=') {
+ $size = substr($size, 2);
+ }
+ if (!ctype_digit($size)) {
+ $size = null;
+ }
+ @list($version, $flags) = explode(',', $info, 2);
+ if ($version != 2) {
+ $flags = '';
+ }
+
+ $named_flags = $default_flags;
+ $length = strlen($flags);
+ for ($i = 0; $i < $length; ++$i) {
+ $flag = $flags[$i];
+ $named_flags[$flag] = isset(self::$_knownFlags[$flag]) ? self::$_knownFlags[$flag] : $flag;
+ }
+
+ $data = array('uniq' => $uniq,
+ 'flags' => $named_flags,
+ 'flaglookup' => array_flip($named_flags),
+ 'filename' => $dirname . $entry);
+ if ($size !== null) {
+ $data['size'] = (int)$size;
+ }
+ $this->_files[] = $data;
+ }
+ }
+
+
+ /**
+ * Close resource for mail lib. If you need to control, when the resource
+ * is closed. Otherwise the destructor would call this.
+ *
+ * @return void
+ */
+ public function close()
+ {
+ $this->_files = array();
+ }
+
+
+ /**
+ * Waste some CPU cycles doing nothing.
+ *
+ * @return void
+ */
+ public function noop()
+ {
+ return true;
+ }
+
+
+ /**
+ * stub for not supported message deletion
+ *
+ * @return null
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function removeMessage($id)
+ {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('maildir is (currently) read-only');
+ }
+
+ /**
+ * get unique id for one or all messages
+ *
+ * if storage does not support unique ids it's the same as the message number
+ *
+ * @param int|null $id message number
+ * @return array|string message number for given message or all messages as array
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function getUniqueId($id = null)
+ {
+ if ($id) {
+ return $this->_getFileData($id, 'uniq');
+ }
+
+ $ids = array();
+ foreach ($this->_files as $num => $file) {
+ $ids[$num + 1] = $file['uniq'];
+ }
+ return $ids;
+ }
+
+ /**
+ * get a message number from a unique id
+ *
+ * I.e. if you have a webmailer that supports deleting messages you should use unique ids
+ * as parameter and use this method to translate it to message number right before calling removeMessage()
+ *
+ * @param string $id unique id
+ * @return int message number
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function getNumberByUniqueId($id)
+ {
+ foreach ($this->_files as $num => $file) {
+ if ($file['uniq'] == $id) {
+ return $num + 1;
+ }
+ }
+
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('unique id not found');
+ }
+}
diff --git a/lib/Zend/Mail/Storage/Mbox.php b/lib/Zend/Mail/Storage/Mbox.php
new file mode 100644
index 0000000..8be42d8
--- /dev/null
+++ b/lib/Zend/Mail/Storage/Mbox.php
@@ -0,0 +1,447 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Storage
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Mbox.php 12519 2008-11-10 18:41:24Z alexander $
+ */
+
+
+/**
+ * @see Zend_Loader
+ * May be used in constructor, but commented out for now
+ */
+// require_once 'Zend/Loader.php';
+
+/**
+ * @see Zend_Mail_Storage_Abstract
+ */
+require_once 'Zend/Mail/Storage/Abstract.php';
+
+/**
+ * @see Zend_Mail_Message_File
+ */
+require_once 'Zend/Mail/Message/File.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Storage
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Mail_Storage_Mbox extends Zend_Mail_Storage_Abstract
+{
+ /**
+ * file handle to mbox file
+ * @var null|resource
+ */
+ protected $_fh;
+
+ /**
+ * filename of mbox file for __wakeup
+ * @var string
+ */
+ protected $_filename;
+
+ /**
+ * modification date of mbox file for __wakeup
+ * @var int
+ */
+ protected $_filemtime;
+
+ /**
+ * start and end position of messages as array('start' => start, 'seperator' => headersep, 'end' => end)
+ * @var array
+ */
+ protected $_positions;
+
+ /**
+ * used message class, change it in an extened class to extend the returned message class
+ * @var string
+ */
+ protected $_messageClass = 'Zend_Mail_Message_File';
+
+ /**
+ * Count messages all messages in current box
+ *
+ * @return int number of messages
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function countMessages()
+ {
+ return count($this->_positions);
+ }
+
+
+ /**
+ * Get a list of messages with number and size
+ *
+ * @param int|null $id number of message or null for all messages
+ * @return int|array size of given message of list with all messages as array(num => size)
+ */
+ public function getSize($id = 0)
+ {
+ if ($id) {
+ $pos = $this->_positions[$id - 1];
+ return $pos['end'] - $pos['start'];
+ }
+
+ $result = array();
+ foreach ($this->_positions as $num => $pos) {
+ $result[$num + 1] = $pos['end'] - $pos['start'];
+ }
+
+ return $result;
+ }
+
+
+ /**
+ * Get positions for mail message or throw exeption if id is invalid
+ *
+ * @param int $id number of message
+ * @return array positions as in _positions
+ * @throws Zend_Mail_Storage_Exception
+ */
+ protected function _getPos($id)
+ {
+ if (!isset($this->_positions[$id - 1])) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('id does not exist');
+ }
+
+ return $this->_positions[$id - 1];
+ }
+
+
+ /**
+ * Fetch a message
+ *
+ * @param int $id number of message
+ * @return Zend_Mail_Message_File
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function getMessage($id)
+ {
+ // TODO that's ugly, would be better to let the message class decide
+ if (strtolower($this->_messageClass) == 'zend_mail_message_file' || is_subclass_of($this->_messageClass, 'zend_mail_message_file')) {
+ // TODO top/body lines
+ $messagePos = $this->_getPos($id);
+ return new $this->_messageClass(array('file' => $this->_fh, 'startPos' => $messagePos['start'],
+ 'endPos' => $messagePos['end']));
+ }
+
+ $bodyLines = 0; // TODO: need a way to change that
+
+ $message = $this->getRawHeader($id);
+ // file pointer is after headers now
+ if ($bodyLines) {
+ $message .= "\n";
+ while ($bodyLines-- && ftell($this->_fh) < $this->_positions[$id - 1]['end']) {
+ $message .= fgets($this->_fh);
+ }
+ }
+
+ return new $this->_messageClass(array('handler' => $this, 'id' => $id, 'headers' => $message));
+ }
+
+ /*
+ * Get raw header of message or part
+ *
+ * @param int $id number of message
+ * @param null|array|string $part path to part or null for messsage header
+ * @param int $topLines include this many lines with header (after an empty line)
+ * @return string raw header
+ * @throws Zend_Mail_Protocol_Exception
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function getRawHeader($id, $part = null, $topLines = 0)
+ {
+ if ($part !== null) {
+ // TODO: implement
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('not implemented');
+ }
+ $messagePos = $this->_getPos($id);
+ // TODO: toplines
+ return stream_get_contents($this->_fh, $messagePos['separator'] - $messagePos['start'], $messagePos['start']);
+ }
+
+ /*
+ * Get raw content of message or part
+ *
+ * @param int $id number of message
+ * @param null|array|string $part path to part or null for messsage content
+ * @return string raw content
+ * @throws Zend_Mail_Protocol_Exception
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function getRawContent($id, $part = null)
+ {
+ if ($part !== null) {
+ // TODO: implement
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('not implemented');
+ }
+ $messagePos = $this->_getPos($id);
+ return stream_get_contents($this->_fh, $messagePos['end'] - $messagePos['separator'], $messagePos['separator']);
+ }
+
+ /**
+ * Create instance with parameters
+ * Supported parameters are:
+ * - filename filename of mbox file
+ *
+ * @param $params array mail reader specific parameters
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function __construct($params)
+ {
+ if (is_array($params)) {
+ $params = (object)$params;
+ }
+
+ if (!isset($params->filename) /* || Zend_Loader::isReadable($params['filename']) */) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('no valid filename given in params');
+ }
+
+ $this->_openMboxFile($params->filename);
+ $this->_has['top'] = true;
+ $this->_has['uniqueid'] = false;
+ }
+
+ /**
+ * check if given file is a mbox file
+ *
+ * if $file is a resource its file pointer is moved after the first line
+ *
+ * @param resource|string $file stream resource of name of file
+ * @param bool $fileIsString file is string or resource
+ * @return bool file is mbox file
+ */
+ protected function _isMboxFile($file, $fileIsString = true)
+ {
+ if ($fileIsString) {
+ $file = @fopen($file, 'r');
+ if (!$file) {
+ return false;
+ }
+ } else {
+ fseek($file, 0);
+ }
+
+ $result = false;
+
+ $line = fgets($file);
+ if (strpos($line, 'From ') === 0) {
+ $result = true;
+ }
+
+ if ($fileIsString) {
+ @fclose($file);
+ }
+
+ return $result;
+ }
+
+ /**
+ * open given file as current mbox file
+ *
+ * @param string $filename filename of mbox file
+ * @return null
+ * @throws Zend_Mail_Storage_Exception
+ */
+ protected function _openMboxFile($filename)
+ {
+ if ($this->_fh) {
+ $this->close();
+ }
+
+ $this->_fh = @fopen($filename, 'r');
+ if (!$this->_fh) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('cannot open mbox file');
+ }
+ $this->_filename = $filename;
+ $this->_filemtime = filemtime($this->_filename);
+
+ if (!$this->_isMboxFile($this->_fh, false)) {
+ @fclose($this->_fh);
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('file is not a valid mbox format');
+ }
+
+ $messagePos = array('start' => ftell($this->_fh), 'separator' => 0, 'end' => 0);
+ while (($line = fgets($this->_fh)) !== false) {
+ if (strpos($line, 'From ') === 0) {
+ $messagePos['end'] = ftell($this->_fh) - strlen($line) - 2; // + newline
+ if (!$messagePos['separator']) {
+ $messagePos['separator'] = $messagePos['end'];
+ }
+ $this->_positions[] = $messagePos;
+ $messagePos = array('start' => ftell($this->_fh), 'separator' => 0, 'end' => 0);
+ }
+ if (!$messagePos['separator'] && !trim($line)) {
+ $messagePos['separator'] = ftell($this->_fh);
+ }
+ }
+
+ $messagePos['end'] = ftell($this->_fh);
+ if (!$messagePos['separator']) {
+ $messagePos['separator'] = $messagePos['end'];
+ }
+ $this->_positions[] = $messagePos;
+ }
+
+ /**
+ * Close resource for mail lib. If you need to control, when the resource
+ * is closed. Otherwise the destructor would call this.
+ *
+ * @return void
+ */
+ public function close()
+ {
+ @fclose($this->_fh);
+ $this->_positions = array();
+ }
+
+
+ /**
+ * Waste some CPU cycles doing nothing.
+ *
+ * @return void
+ */
+ public function noop()
+ {
+ return true;
+ }
+
+
+ /**
+ * stub for not supported message deletion
+ *
+ * @return null
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function removeMessage($id)
+ {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('mbox is read-only');
+ }
+
+ /**
+ * get unique id for one or all messages
+ *
+ * Mbox does not support unique ids (yet) - it's always the same as the message number.
+ * That shouldn't be a problem, because we can't change mbox files. Therefor the message
+ * number is save enough.
+ *
+ * @param int|null $id message number
+ * @return array|string message number for given message or all messages as array
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function getUniqueId($id = null)
+ {
+ if ($id) {
+ // check if id exists
+ $this->_getPos($id);
+ return $id;
+ }
+
+ $range = range(1, $this->countMessages());
+ return array_combine($range, $range);
+ }
+
+ /**
+ * get a message number from a unique id
+ *
+ * I.e. if you have a webmailer that supports deleting messages you should use unique ids
+ * as parameter and use this method to translate it to message number right before calling removeMessage()
+ *
+ * @param string $id unique id
+ * @return int message number
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function getNumberByUniqueId($id)
+ {
+ // check if id exists
+ $this->_getPos($id);
+ return $id;
+ }
+
+ /**
+ * magic method for serialize()
+ *
+ * with this method you can cache the mbox class
+ *
+ * @return array name of variables
+ */
+ public function __sleep()
+ {
+ return array('_filename', '_positions', '_filemtime');
+ }
+
+ /**
+ * magic method for unserialize()
+ *
+ * with this method you can cache the mbox class
+ * for cache validation the mtime of the mbox file is used
+ *
+ * @return null
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function __wakeup()
+ {
+ if ($this->_filemtime != @filemtime($this->_filename)) {
+ $this->close();
+ $this->_openMboxFile($this->_filename);
+ } else {
+ $this->_fh = @fopen($this->_filename, 'r');
+ if (!$this->_fh) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('cannot open mbox file');
+ }
+ }
+ }
+
+}
diff --git a/lib/Zend/Mail/Storage/Pop3.php b/lib/Zend/Mail/Storage/Pop3.php
new file mode 100644
index 0000000..693384d
--- /dev/null
+++ b/lib/Zend/Mail/Storage/Pop3.php
@@ -0,0 +1,328 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Storage
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Pop3.php 9099 2008-03-30 19:35:47Z thomas $
+ */
+
+
+/**
+ * @see Zend_Mail_Storage_Abstract
+ */
+require_once 'Zend/Mail/Storage/Abstract.php';
+
+/**
+ * @see Zend_Mail_Protocol_Pop3
+ */
+require_once 'Zend/Mail/Protocol/Pop3.php';
+
+/**
+ * @see Zend_Mail_Message
+ */
+require_once 'Zend/Mail/Message.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Storage
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Mail_Storage_Pop3 extends Zend_Mail_Storage_Abstract
+{
+ /**
+ * protocol handler
+ * @var null|Zend_Mail_Protocol_Pop3
+ */
+ protected $_protocol;
+
+
+ /**
+ * Count messages all messages in current box
+ *
+ * @return int number of messages
+ * @throws Zend_Mail_Storage_Exception
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function countMessages()
+ {
+ $this->_protocol->status($count, $null);
+ return (int)$count;
+ }
+
+ /**
+ * get a list of messages with number and size
+ *
+ * @param int $id number of message
+ * @return int|array size of given message of list with all messages as array(num => size)
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function getSize($id = 0)
+ {
+ $id = $id ? $id : null;
+ return $this->_protocol->getList($id);
+ }
+
+ /**
+ * Fetch a message
+ *
+ * @param int $id number of message
+ * @return Zend_Mail_Message
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function getMessage($id)
+ {
+ $bodyLines = 0;
+ $message = $this->_protocol->top($id, $bodyLines, true);
+
+ return new $this->_messageClass(array('handler' => $this, 'id' => $id, 'headers' => $message,
+ 'noToplines' => $bodyLines < 1));
+ }
+
+ /*
+ * Get raw header of message or part
+ *
+ * @param int $id number of message
+ * @param null|array|string $part path to part or null for messsage header
+ * @param int $topLines include this many lines with header (after an empty line)
+ * @return string raw header
+ * @throws Zend_Mail_Protocol_Exception
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function getRawHeader($id, $part = null, $topLines = 0)
+ {
+ if ($part !== null) {
+ // TODO: implement
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('not implemented');
+ }
+
+ return $this->_protocol->top($id, 0, true);
+ }
+
+ /*
+ * Get raw content of message or part
+ *
+ * @param int $id number of message
+ * @param null|array|string $part path to part or null for messsage content
+ * @return string raw content
+ * @throws Zend_Mail_Protocol_Exception
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function getRawContent($id, $part = null)
+ {
+ if ($part !== null) {
+ // TODO: implement
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('not implemented');
+ }
+
+ $content = $this->_protocol->retrieve($id);
+ // TODO: find a way to avoid decoding the headers
+ Zend_Mime_Decode::splitMessage($content, $null, $body);
+ return $body;
+ }
+
+ /**
+ * create instance with parameters
+ * Supported paramters are
+ * - host hostname or ip address of POP3 server
+ * - user username
+ * - password password for user 'username' [optional, default = '']
+ * - port port for POP3 server [optional, default = 110]
+ * - ssl 'SSL' or 'TLS' for secure sockets
+ *
+ * @param $params array mail reader specific parameters
+ * @throws Zend_Mail_Storage_Exception
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function __construct($params)
+ {
+ if (is_array($params)) {
+ $params = (object)$params;
+ }
+
+ $this->_has['fetchPart'] = false;
+ $this->_has['top'] = null;
+ $this->_has['uniqueid'] = null;
+
+ if ($params instanceof Zend_Mail_Protocol_Pop3) {
+ $this->_protocol = $params;
+ return;
+ }
+
+ if (!isset($params->user)) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('need at least user in params');
+ }
+
+ $host = isset($params->host) ? $params->host : 'localhost';
+ $password = isset($params->password) ? $params->password : '';
+ $port = isset($params->port) ? $params->port : null;
+ $ssl = isset($params->ssl) ? $params->ssl : false;
+
+ $this->_protocol = new Zend_Mail_Protocol_Pop3();
+ $this->_protocol->connect($host, $port, $ssl);
+ $this->_protocol->login($params->user, $password);
+ }
+
+ /**
+ * Close resource for mail lib. If you need to control, when the resource
+ * is closed. Otherwise the destructor would call this.
+ *
+ * @return null
+ */
+ public function close()
+ {
+ $this->_protocol->logout();
+ }
+
+ /**
+ * Keep the server busy.
+ *
+ * @return null
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function noop()
+ {
+ return $this->_protocol->noop();
+ }
+
+ /**
+ * Remove a message from server. If you're doing that from a web enviroment
+ * you should be careful and use a uniqueid as parameter if possible to
+ * identify the message.
+ *
+ * @param int $id number of message
+ * @return null
+ * @throws Zend_Mail_Protocol_Exception
+ */
+ public function removeMessage($id)
+ {
+ $this->_protocol->delete($id);
+ }
+
+ /**
+ * get unique id for one or all messages
+ *
+ * if storage does not support unique ids it's the same as the message number
+ *
+ * @param int|null $id message number
+ * @return array|string message number for given message or all messages as array
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function getUniqueId($id = null)
+ {
+ if (!$this->hasUniqueid) {
+ if ($id) {
+ return $id;
+ }
+ $count = $this->countMessages();
+ if ($count < 1) {
+ return array();
+ }
+ $range = range(1, $count);
+ return array_combine($range, $range);
+ }
+
+ return $this->_protocol->uniqueid($id);
+ }
+
+ /**
+ * get a message number from a unique id
+ *
+ * I.e. if you have a webmailer that supports deleting messages you should use unique ids
+ * as parameter and use this method to translate it to message number right before calling removeMessage()
+ *
+ * @param string $id unique id
+ * @return int message number
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function getNumberByUniqueId($id)
+ {
+ if (!$this->hasUniqueid) {
+ return $id;
+ }
+
+ $ids = $this->getUniqueId();
+ foreach ($ids as $k => $v) {
+ if ($v == $id) {
+ return $k;
+ }
+ }
+
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('unique id not found');
+ }
+
+ /**
+ * Special handling for hasTop and hasUniqueid. The headers of the first message is
+ * retrieved if Top wasn't needed/tried yet.
+ *
+ * @see Zend_Mail_Storage_Abstract:__get()
+ * @param string $var
+ * @return string
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function __get($var)
+ {
+ $result = parent::__get($var);
+ if ($result !== null) {
+ return $result;
+ }
+
+ if (strtolower($var) == 'hastop') {
+ if ($this->_protocol->hasTop === null) {
+ // need to make a real call, because not all server are honest in their capas
+ try {
+ $this->_protocol->top(1, 0, false);
+ } catch(Zend_Mail_Exception $e) {
+ // ignoring error
+ }
+ }
+ $this->_has['top'] = $this->_protocol->hasTop;
+ return $this->_protocol->hasTop;
+ }
+
+ if (strtolower($var) == 'hasuniqueid') {
+ $id = null;
+ try {
+ $id = $this->_protocol->uniqueid(1);
+ } catch(Zend_Mail_Exception $e) {
+ // ignoring error
+ }
+ $this->_has['uniqueid'] = $id ? true : false;
+ return $this->_has['uniqueid'];
+ }
+
+ return $result;
+ }
+}
diff --git a/lib/Zend/Mail/Storage/Writable/Interface.php b/lib/Zend/Mail/Storage/Writable/Interface.php
new file mode 100644
index 0000000..b49fc8e
--- /dev/null
+++ b/lib/Zend/Mail/Storage/Writable/Interface.php
@@ -0,0 +1,108 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Storage
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Interface.php 9098 2008-03-30 19:29:10Z thomas $
+ */
+
+
+/**
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Storage
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+interface Zend_Mail_Storage_Writable_Interface
+{
+ /**
+ * create a new folder
+ *
+ * This method also creates parent folders if necessary. Some mail storages may restrict, which folder
+ * may be used as parent or which chars may be used in the folder name
+ *
+ * @param string $name global name of folder, local name if $parentFolder is set
+ * @param string|Zend_Mail_Storage_Folder $parentFolder parent folder for new folder, else root folder is parent
+ * @return null
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function createFolder($name, $parentFolder = null);
+
+ /**
+ * remove a folder
+ *
+ * @param string|Zend_Mail_Storage_Folder $name name or instance of folder
+ * @return null
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function removeFolder($name);
+
+ /**
+ * rename and/or move folder
+ *
+ * The new name has the same restrictions as in createFolder()
+ *
+ * @param string|Zend_Mail_Storage_Folder $oldName name or instance of folder
+ * @param string $newName new global name of folder
+ * @return null
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function renameFolder($oldName, $newName);
+
+ /**
+ * append a new message to mail storage
+ *
+ * @param string|Zend_Mail_Message|Zend_Mime_Message $message message as string or instance of message class
+ * @param null|string|Zend_Mail_Storage_Folder $folder folder for new message, else current folder is taken
+ * @param null|array $flags set flags for new message, else a default set is used
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function appendMessage($message, $folder = null, $flags = null);
+
+ /**
+ * copy an existing message
+ *
+ * @param int $id number of message
+ * @param string|Zend_Mail_Storage_Folder $folder name or instance of targer folder
+ * @return null
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function copyMessage($id, $folder);
+
+ /**
+ * move an existing message
+ *
+ * @param int $id number of message
+ * @param string|Zend_Mail_Storage_Folder $folder name or instance of targer folder
+ * @return null
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function moveMessage($id, $folder);
+
+ /**
+ * set flags for message
+ *
+ * NOTE: this method can't set the recent flag.
+ *
+ * @param int $id number of message
+ * @param array $flags new flags for message
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function setFlags($id, $flags);
+} \ No newline at end of file
diff --git a/lib/Zend/Mail/Storage/Writable/Maildir.php b/lib/Zend/Mail/Storage/Writable/Maildir.php
new file mode 100644
index 0000000..9ba7573
--- /dev/null
+++ b/lib/Zend/Mail/Storage/Writable/Maildir.php
@@ -0,0 +1,1049 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Storage
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Maildir.php 12519 2008-11-10 18:41:24Z alexander $
+ */
+
+
+/**
+ * @see Zend_Mail_Storage_Folder_Maildir
+ */
+require_once 'Zend/Mail/Storage/Folder/Maildir.php';
+
+/**
+ * @see Zend_Mail_Storage_Writable_Interface
+ */
+require_once 'Zend/Mail/Storage/Writable/Interface.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Storage
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Mail_Storage_Writable_Maildir extends Zend_Mail_Storage_Folder_Maildir
+ implements Zend_Mail_Storage_Writable_Interface
+{
+ // TODO: init maildir (+ constructor option create if not found)
+
+ /**
+ * use quota and size of quota if given
+ * @var bool|int
+ */
+ protected $_quota;
+
+ /**
+ * create a new maildir
+ *
+ * If the given dir is already a valid maildir this will not fail.
+ *
+ * @param string $dir directory for the new maildir (may already exist)
+ * @return null
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public static function initMaildir($dir)
+ {
+ if (file_exists($dir)) {
+ if (!is_dir($dir)) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('maildir must be a directory if already exists');
+ }
+ } else {
+ if (!mkdir($dir)) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ $dir = dirname($dir);
+ if (!file_exists($dir)) {
+ throw new Zend_Mail_Storage_Exception("parent $dir not found");
+ } else if (!is_dir($dir)) {
+ throw new Zend_Mail_Storage_Exception("parent $dir not a directory");
+ } else {
+ throw new Zend_Mail_Storage_Exception('cannot create maildir');
+ }
+ }
+ }
+
+ foreach (array('cur', 'tmp', 'new') as $subdir) {
+ if (!@mkdir($dir . DIRECTORY_SEPARATOR . $subdir)) {
+ // ignore if dir exists (i.e. was already valid maildir or two processes try to create one)
+ if (!file_exists($dir . DIRECTORY_SEPARATOR . $subdir)) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('could not create subdir ' . $subdir);
+ }
+ }
+ }
+ }
+
+ /**
+ * Create instance with parameters
+ * Additional parameters are (see parent for more):
+ * - create if true a new maildir is create if none exists
+ *
+ * @param $params array mail reader specific parameters
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function __construct($params) {
+ if (is_array($params)) {
+ $params = (object)$params;
+ }
+
+ if (!empty($params->create) && isset($params->dirname) && !file_exists($params->dirname . DIRECTORY_SEPARATOR . 'cur')) {
+ self::initMaildir($params->dirname);
+ }
+
+ parent::__construct($params);
+ }
+
+ /**
+ * create a new folder
+ *
+ * This method also creates parent folders if necessary. Some mail storages may restrict, which folder
+ * may be used as parent or which chars may be used in the folder name
+ *
+ * @param string $name global name of folder, local name if $parentFolder is set
+ * @param string|Zend_Mail_Storage_Folder $parentFolder parent folder for new folder, else root folder is parent
+ * @return string only used internally (new created maildir)
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function createFolder($name, $parentFolder = null)
+ {
+ if ($parentFolder instanceof Zend_Mail_Storage_Folder) {
+ $folder = $parentFolder->getGlobalName() . $this->_delim . $name;
+ } else if ($parentFolder != null) {
+ $folder = rtrim($parentFolder, $this->_delim) . $this->_delim . $name;
+ } else {
+ $folder = $name;
+ }
+
+ $folder = trim($folder, $this->_delim);
+
+ // first we check if we try to create a folder that does exist
+ $exists = null;
+ try {
+ $exists = $this->getFolders($folder);
+ } catch (Zend_Mail_Exception $e) {
+ // ok
+ }
+ if ($exists) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('folder already exists');
+ }
+
+ if (strpos($folder, $this->_delim . $this->_delim) !== false) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('invalid name - folder parts may not be empty');
+ }
+
+ if (strpos($folder, 'INBOX' . $this->_delim) === 0) {
+ $folder = substr($folder, 6);
+ }
+
+ $fulldir = $this->_rootdir . '.' . $folder;
+
+ // check if we got tricked and would create a dir outside of the rootdir or not as direct child
+ if (strpos($folder, DIRECTORY_SEPARATOR) !== false || strpos($folder, '/') !== false
+ || dirname($fulldir) . DIRECTORY_SEPARATOR != $this->_rootdir) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('invalid name - no directory seprator allowed in folder name');
+ }
+
+ // has a parent folder?
+ $parent = null;
+ if (strpos($folder, $this->_delim)) {
+ // let's see if the parent folder exists
+ $parent = substr($folder, 0, strrpos($folder, $this->_delim));
+ try {
+ $this->getFolders($parent);
+ } catch (Zend_Mail_Exception $e) {
+ // does not - create parent folder
+ $this->createFolder($parent);
+ }
+ }
+
+ if (!@mkdir($fulldir) || !@mkdir($fulldir . DIRECTORY_SEPARATOR . 'cur')) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('error while creating new folder, may be created incompletly');
+ }
+
+ mkdir($fulldir . DIRECTORY_SEPARATOR . 'new');
+ mkdir($fulldir . DIRECTORY_SEPARATOR . 'tmp');
+
+ $localName = $parent ? substr($folder, strlen($parent) + 1) : $folder;
+ $this->getFolders($parent)->$localName = new Zend_Mail_Storage_Folder($localName, $folder, true);
+
+ return $fulldir;
+ }
+
+ /**
+ * remove a folder
+ *
+ * @param string|Zend_Mail_Storage_Folder $name name or instance of folder
+ * @return null
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function removeFolder($name)
+ {
+ // TODO: This could fail in the middle of the task, which is not optimal.
+ // But there is no defined standard way to mark a folder as removed and there is no atomar fs-op
+ // to remove a directory. Also moving the folder to a/the trash folder is not possible, as
+ // all parent folders must be created. What we could do is add a dash to the front of the
+ // directory name and it should be ignored as long as other processes obey the standard.
+
+ if ($name instanceof Zend_Mail_Storage_Folder) {
+ $name = $name->getGlobalName();
+ }
+
+ $name = trim($name, $this->_delim);
+ if (strpos($name, 'INBOX' . $this->_delim) === 0) {
+ $name = substr($name, 6);
+ }
+
+ // check if folder exists and has no children
+ if (!$this->getFolders($name)->isLeaf()) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('delete children first');
+ }
+
+ if ($name == 'INBOX' || $name == DIRECTORY_SEPARATOR || $name == '/') {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('wont delete INBOX');
+ }
+
+ if ($name == $this->getCurrentFolder()) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('wont delete selected folder');
+ }
+
+ foreach (array('tmp', 'new', 'cur', '.') as $subdir) {
+ $dir = $this->_rootdir . '.' . $name . DIRECTORY_SEPARATOR . $subdir;
+ if (!file_exists($dir)) {
+ continue;
+ }
+ $dh = opendir($dir);
+ if (!$dh) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception("error opening $subdir");
+ }
+ while (($entry = readdir($dh)) !== false) {
+ if ($entry == '.' || $entry == '..') {
+ continue;
+ }
+ if (!unlink($dir . DIRECTORY_SEPARATOR . $entry)) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception("error cleaning $subdir");
+ }
+ }
+ closedir($dh);
+ if ($subdir !== '.') {
+ if (!rmdir($dir)) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception("error removing $subdir");
+ }
+ }
+ }
+
+ if (!rmdir($this->_rootdir . '.' . $name)) {
+ // at least we should try to make it a valid maildir again
+ mkdir($this->_rootdir . '.' . $name . DIRECTORY_SEPARATOR . 'cur');
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception("error removing maindir");
+ }
+
+ $parent = strpos($name, $this->_delim) ? substr($name, 0, strrpos($name, $this->_delim)) : null;
+ $localName = $parent ? substr($name, strlen($parent) + 1) : $name;
+ unset($this->getFolders($parent)->$localName);
+ }
+
+ /**
+ * rename and/or move folder
+ *
+ * The new name has the same restrictions as in createFolder()
+ *
+ * @param string|Zend_Mail_Storage_Folder $oldName name or instance of folder
+ * @param string $newName new global name of folder
+ * @return null
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function renameFolder($oldName, $newName)
+ {
+ // TODO: This is also not atomar and has similar problems as removeFolder()
+
+ if ($oldName instanceof Zend_Mail_Storage_Folder) {
+ $oldName = $oldName->getGlobalName();
+ }
+
+ $oldName = trim($oldName, $this->_delim);
+ if (strpos($oldName, 'INBOX' . $this->_delim) === 0) {
+ $oldName = substr($oldName, 6);
+ }
+
+ $newName = trim($newName, $this->_delim);
+ if (strpos($newName, 'INBOX' . $this->_delim) === 0) {
+ $newName = substr($newName, 6);
+ }
+
+ if (strpos($newName, $oldName . $this->_delim) === 0) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('new folder cannot be a child of old folder');
+ }
+
+ // check if folder exists and has no children
+ $folder = $this->getFolders($oldName);
+
+ if ($oldName == 'INBOX' || $oldName == DIRECTORY_SEPARATOR || $oldName == '/') {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('wont rename INBOX');
+ }
+
+ if ($oldName == $this->getCurrentFolder()) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('wont rename selected folder');
+ }
+
+ $newdir = $this->createFolder($newName);
+
+ if (!$folder->isLeaf()) {
+ foreach ($folder as $k => $v) {
+ $this->renameFolder($v->getGlobalName(), $newName . $this->_delim . $k);
+ }
+ }
+
+ $olddir = $this->_rootdir . '.' . $folder;
+ foreach (array('tmp', 'new', 'cur') as $subdir) {
+ $subdir = DIRECTORY_SEPARATOR . $subdir;
+ if (!file_exists($olddir . $subdir)) {
+ continue;
+ }
+ // using copy or moving files would be even better - but also much slower
+ if (!rename($olddir . $subdir, $newdir . $subdir)) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('error while moving ' . $subdir);
+ }
+ }
+ // create a dummy if removing fails - otherwise we can't read it next time
+ mkdir($olddir . DIRECTORY_SEPARATOR . 'cur');
+ $this->removeFolder($oldName);
+ }
+
+ /**
+ * create a uniqueid for maildir filename
+ *
+ * This is nearly the format defined in the maildir standard. The microtime() call should already
+ * create a uniqueid, the pid is for multicore/-cpu machine that manage to call this function at the
+ * exact same time, and uname() gives us the hostname for multiple machines accessing the same storage.
+ *
+ * If someone disables posix we create a random number of the same size, so this method should also
+ * work on Windows - if you manage to get maildir working on Windows.
+ * Microtime could also be disabled, altough I've never seen it.
+ *
+ * @return string new uniqueid
+ */
+ protected function _createUniqueId()
+ {
+ $id = '';
+ $id .= function_exists('microtime') ? microtime(true) : (time() . ' ' . rand(0, 100000));
+ $id .= '.' . (function_exists('posix_getpid') ? posix_getpid() : rand(50, 65535));
+ $id .= '.' . php_uname('n');
+
+ return $id;
+ }
+
+ /**
+ * open a temporary maildir file
+ *
+ * makes sure tmp/ exists and create a file with a unique name
+ * you should close the returned filehandle!
+ *
+ * @param string $folder name of current folder without leading .
+ * @return array array('dirname' => dir of maildir folder, 'uniq' => unique id, 'filename' => name of create file
+ * 'handle' => file opened for writing)
+ * @throws Zend_Mail_Storage_Exception
+ */
+ protected function _createTmpFile($folder = 'INBOX')
+ {
+ if ($folder == 'INBOX') {
+ $tmpdir = $this->_rootdir . DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR;
+ } else {
+ $tmpdir = $this->_rootdir . '.' . $folder . DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR;
+ }
+ if (!file_exists($tmpdir)) {
+ if (!mkdir($tmpdir)) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('problems creating tmp dir');
+ }
+ }
+
+ // we should retry to create a unique id if a file with the same name exists
+ // to avoid a script timeout we only wait 1 second (instead of 2) and stop
+ // after a defined retry count
+ // if you change this variable take into account that it can take up to $max_tries seconds
+ // normally we should have a valid unique name after the first try, we're just following the "standard" here
+ $max_tries = 5;
+ for ($i = 0; $i < $max_tries; ++$i) {
+ $uniq = $this->_createUniqueId();
+ if (!file_exists($tmpdir . $uniq)) {
+ // here is the race condition! - as defined in the standard
+ // to avoid having a long time between stat()ing the file and creating it we're opening it here
+ // to mark the filename as taken
+ $fh = fopen($tmpdir . $uniq, 'w');
+ if (!$fh) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('could not open temp file');
+ }
+ break;
+ }
+ sleep(1);
+ }
+
+ if (!$fh) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception("tried $max_tries unique ids for a temp file, but all were taken"
+ . ' - giving up');
+ }
+
+ return array('dirname' => $this->_rootdir . '.' . $folder, 'uniq' => $uniq, 'filename' => $tmpdir . $uniq,
+ 'handle' => $fh);
+ }
+
+ /**
+ * create an info string for filenames with given flags
+ *
+ * @param array $flags wanted flags, with the reference you'll get the set flags with correct key (= char for flag)
+ * @return string info string for version 2 filenames including the leading colon
+ * @throws Zend_Mail_Storage_Exception
+ */
+ protected function _getInfoString(&$flags)
+ {
+ // accessing keys is easier, faster and it removes duplicated flags
+ $wanted_flags = array_flip($flags);
+ if (isset($wanted_flags[Zend_Mail_Storage::FLAG_RECENT])) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('recent flag may not be set');
+ }
+
+ $info = ':2,';
+ $flags = array();
+ foreach (Zend_Mail_Storage_Maildir::$_knownFlags as $char => $flag) {
+ if (!isset($wanted_flags[$flag])) {
+ continue;
+ }
+ $info .= $char;
+ $flags[$char] = $flag;
+ unset($wanted_flags[$flag]);
+ }
+
+ if (!empty($wanted_flags)) {
+ $wanted_flags = implode(', ', array_keys($wanted_flags));
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('unknown flag(s): ' . $wanted_flags);
+ }
+
+ return $info;
+ }
+
+ /**
+ * append a new message to mail storage
+ *
+ * @param string|stream $message message as string or stream resource
+ * @param null|string|Zend_Mail_Storage_Folder $folder folder for new message, else current folder is taken
+ * @param null|array $flags set flags for new message, else a default set is used
+ * @param bool $recent handle this mail as if recent flag has been set,
+ * should only be used in delivery
+ * @throws Zend_Mail_Storage_Exception
+ */
+ // not yet * @param string|Zend_Mail_Message|Zend_Mime_Message $message message as string or instance of message class
+
+ public function appendMessage($message, $folder = null, $flags = null, $recent = false)
+ {
+ if ($this->_quota && $this->checkQuota()) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('storage is over quota!');
+ }
+
+ if ($folder === null) {
+ $folder = $this->_currentFolder;
+ }
+
+ if (!($folder instanceof Zend_Mail_Storage_Folder)) {
+ $folder = $this->getFolders($folder);
+ }
+
+ if ($flags === null) {
+ $flags = array(Zend_Mail_Storage::FLAG_SEEN);
+ }
+ $info = $this->_getInfoString($flags);
+ $temp_file = $this->_createTmpFile($folder->getGlobalName());
+
+ // TODO: handle class instances for $message
+ if (is_resource($message) && get_resource_type($message) == 'stream') {
+ stream_copy_to_stream($message, $temp_file['handle']);
+ } else {
+ fputs($temp_file['handle'], $message);
+ }
+ fclose($temp_file['handle']);
+
+ // we're adding the size to the filename for maildir++
+ $size = filesize($temp_file['filename']);
+ if ($size !== false) {
+ $info = ',S=' . $size . $info;
+ }
+ $new_filename = $temp_file['dirname'] . DIRECTORY_SEPARATOR;
+ $new_filename .= $recent ? 'new' : 'cur';
+ $new_filename .= DIRECTORY_SEPARATOR . $temp_file['uniq'] . $info;
+
+ // we're throwing any exception after removing our temp file and saving it to this variable instead
+ $exception = null;
+
+ if (!link($temp_file['filename'], $new_filename)) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ $exception = new Zend_Mail_Storage_Exception('cannot link message file to final dir');
+ }
+ @unlink($temp_file['filename']);
+
+ if ($exception) {
+ throw $exception;
+ }
+
+ $this->_files[] = array('uniq' => $temp_file['uniq'],
+ 'flags' => $flags,
+ 'filename' => $new_filename);
+ if ($this->_quota) {
+ $this->_addQuotaEntry((int)$size, 1);
+ }
+ }
+
+ /**
+ * copy an existing message
+ *
+ * @param int $id number of message
+ * @param string|Zend_Mail_Storage_Folder $folder name or instance of targer folder
+ * @return null
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function copyMessage($id, $folder)
+ {
+ if ($this->_quota && $this->checkQuota()) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('storage is over quota!');
+ }
+
+ if (!($folder instanceof Zend_Mail_Storage_Folder)) {
+ $folder = $this->getFolders($folder);
+ }
+
+ $filedata = $this->_getFileData($id);
+ $old_file = $filedata['filename'];
+ $flags = $filedata['flags'];
+
+ // copied message can't be recent
+ while (($key = array_search(Zend_Mail_Storage::FLAG_RECENT, $flags)) !== false) {
+ unset($flags[$key]);
+ }
+ $info = $this->_getInfoString($flags);
+
+ // we're creating the copy as temp file before moving to cur/
+ $temp_file = $this->_createTmpFile($folder->getGlobalName());
+ // we don't write directly to the file
+ fclose($temp_file['handle']);
+
+ // we're adding the size to the filename for maildir++
+ $size = filesize($old_file);
+ if ($size !== false) {
+ $info = ',S=' . $size . $info;
+ }
+
+ $new_file = $temp_file['dirname'] . DIRECTORY_SEPARATOR . 'cur' . DIRECTORY_SEPARATOR . $temp_file['uniq'] . $info;
+
+ // we're throwing any exception after removing our temp file and saving it to this variable instead
+ $exception = null;
+
+ if (!copy($old_file, $temp_file['filename'])) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ $exception = new Zend_Mail_Storage_Exception('cannot copy message file');
+ } else if (!link($temp_file['filename'], $new_file)) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ $exception = new Zend_Mail_Storage_Exception('cannot link message file to final dir');
+ }
+ @unlink($temp_file['filename']);
+
+ if ($exception) {
+ throw $exception;
+ }
+
+ if ($folder->getGlobalName() == $this->_currentFolder
+ || ($this->_currentFolder == 'INBOX' && $folder->getGlobalName() == '/')) {
+ $this->_files[] = array('uniq' => $temp_file['uniq'],
+ 'flags' => $flags,
+ 'filename' => $new_file);
+ }
+
+ if ($this->_quota) {
+ $this->_addQuotaEntry((int)$size, 1);
+ }
+ }
+
+ /**
+ * move an existing message
+ *
+ * @param int $id number of message
+ * @param string|Zend_Mail_Storage_Folder $folder name or instance of targer folder
+ * @return null
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function moveMessage($id, $folder) {
+ if (!($folder instanceof Zend_Mail_Storage_Folder)) {
+ $folder = $this->getFolders($folder);
+ }
+
+ if ($folder->getGlobalName() == $this->_currentFolder
+ || ($this->_currentFolder == 'INBOX' && $folder->getGlobalName() == '/')) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('target is current folder');
+ }
+
+ $filedata = $this->_getFileData($id);
+ $old_file = $filedata['filename'];
+ $flags = $filedata['flags'];
+
+ // moved message can't be recent
+ while (($key = array_search(Zend_Mail_Storage::FLAG_RECENT, $flags)) !== false) {
+ unset($flags[$key]);
+ }
+ $info = $this->_getInfoString($flags);
+
+ // reserving a new name
+ $temp_file = $this->_createTmpFile($folder->getGlobalName());
+ fclose($temp_file['handle']);
+
+ // we're adding the size to the filename for maildir++
+ $size = filesize($old_file);
+ if ($size !== false) {
+ $info = ',S=' . $size . $info;
+ }
+
+ $new_file = $temp_file['dirname'] . DIRECTORY_SEPARATOR . 'cur' . DIRECTORY_SEPARATOR . $temp_file['uniq'] . $info;
+
+ // we're throwing any exception after removing our temp file and saving it to this variable instead
+ $exception = null;
+
+ if (!rename($old_file, $new_file)) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ $exception = new Zend_Mail_Storage_Exception('cannot move message file');
+ }
+ @unlink($temp_file['filename']);
+
+ if ($exception) {
+ throw $exception;
+ }
+
+ unset($this->_files[$id - 1]);
+ // remove the gap
+ $this->_files = array_values($this->_files);
+ }
+
+
+ /**
+ * set flags for message
+ *
+ * NOTE: this method can't set the recent flag.
+ *
+ * @param int $id number of message
+ * @param array $flags new flags for message
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function setFlags($id, $flags)
+ {
+ $info = $this->_getInfoString($flags);
+ $filedata = $this->_getFileData($id);
+
+ // NOTE: double dirname to make sure we always move to cur. if recent flag has been set (message is in new) it will be moved to cur.
+ $new_filename = dirname(dirname($filedata['filename'])) . DIRECTORY_SEPARATOR . 'cur' . DIRECTORY_SEPARATOR . "$filedata[uniq]$info";
+
+ if (!@rename($filedata['filename'], $new_filename)) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('cannot rename file');
+ }
+
+ $filedata['flags'] = $flags;
+ $filedata['filename'] = $new_filename;
+
+ $this->_files[$id - 1] = $filedata;
+ }
+
+
+ /**
+ * stub for not supported message deletion
+ *
+ * @return null
+ * @throws Zend_Mail_Storage_Exception
+ */
+ public function removeMessage($id)
+ {
+ $filename = $this->_getFileData($id, 'filename');
+
+ if ($this->_quota) {
+ $size = filesize($filename);
+ }
+
+ if (!@unlink($filename)) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('cannot remove message');
+ }
+ unset($this->_files[$id - 1]);
+ // remove the gap
+ $this->_files = array_values($this->_files);
+ if ($this->_quota) {
+ $this->_addQuotaEntry(0 - (int)$size, -1);
+ }
+ }
+
+ /**
+ * enable/disable quota and set a quota value if wanted or needed
+ *
+ * You can enable/disable quota with true/false. If you don't have
+ * a MDA or want to enforce a quota value you can also set this value
+ * here. Use array('size' => SIZE_QUOTA, 'count' => MAX_MESSAGE) do
+ * define your quota. Order of these fields does matter!
+ *
+ * @param bool|array $value new quota value
+ * @return null
+ */
+ public function setQuota($value) {
+ $this->_quota = $value;
+ }
+
+ /**
+ * get currently set quota
+ *
+ * @see Zend_Mail_Storage_Writable_Maildir::setQuota()
+ *
+ * @return bool|array
+ */
+ public function getQuota($fromStorage = false) {
+ if ($fromStorage) {
+ $fh = @fopen($this->_rootdir . 'maildirsize', 'r');
+ if (!$fh) {
+ /**
+ * @see Zend_Mail_Storage_Exception
+ */
+ require_once 'Zend/Mail/Storage/Exception.php';
+ throw new Zend_Mail_Storage_Exception('cannot open maildirsize');
+ }
+ $definition = fgets($fh);
+ fclose($fh);
+ $definition = explode(',', trim($definition));
+ $quota = array();
+ foreach ($definition as $member) {
+ $key = $member[strlen($member) - 1];
+ if ($key == 'S' || $key == 'C') {
+ $key = $key == 'C' ? 'count' : 'size';
+ }
+ $quota[$key] = substr($member, 0, -1);
+ }
+ return $quota;
+ }
+
+ return $this->_quota;
+ }
+
+ /**
+ * @see http://www.inter7.com/courierimap/README.maildirquota.html "Calculating maildirsize"
+ */
+ protected function _calculateMaildirsize() {
+ $timestamps = array();
+ $messages = 0;
+ $total_size = 0;
+
+ if (is_array($this->_quota)) {
+ $quota = $this->_quota;
+ } else {
+ try {
+ $quota = $this->getQuota(true);
+ } catch (Zend_Mail_Storage_Exception $e) {
+ throw new Zend_Mail_Storage_Exception('no quota defintion found');
+ }
+ }
+
+ $folders = new RecursiveIteratorIterator($this->getFolders(), RecursiveIteratorIterator::SELF_FIRST);
+ foreach ($folders as $folder) {
+ $subdir = $folder->getGlobalName();
+ if ($subdir == 'INBOX') {
+ $subdir = '';
+ } else {
+ $subdir = '.' . $subdir;
+ }
+ if ($subdir == 'Trash') {
+ continue;
+ }
+
+ foreach (array('cur', 'new') as $subsubdir) {
+ $dirname = $this->_rootdir . $subdir . DIRECTORY_SEPARATOR . $subsubdir . DIRECTORY_SEPARATOR;
+ if (!file_exists($dirname)) {
+ continue;
+ }
+ // NOTE: we are using mtime instead of "the latest timestamp". The latest would be atime
+ // and as we are accessing the directory it would make the whole calculation useless.
+ $timestamps[$dirname] = filemtime($dirname);
+
+ $dh = opendir($dirname);
+ // NOTE: Should have been checked in constructor. Not throwing an exception here, quotas will
+ // therefore not be fully enforeced, but next request will fail anyway, if problem persists.
+ if (!$dh) {
+ continue;
+ }
+
+
+ while (($entry = readdir()) !== false) {
+ if ($entry[0] == '.' || !is_file($dirname . $entry)) {
+ continue;
+ }
+
+ if (strpos($entry, ',S=')) {
+ strtok($entry, '=');
+ $filesize = strtok(':');
+ if (is_numeric($filesize)) {
+ $total_size += $filesize;
+ ++$messages;
+ continue;
+ }
+ }
+ $size = filesize($dirname . $entry);
+ if ($size === false) {
+ // ignore, as we assume file got removed
+ continue;
+ }
+ $total_size += $size;
+ ++$messages;
+ }
+ }
+ }
+
+ $tmp = $this->_createTmpFile();
+ $fh = $tmp['handle'];
+ $definition = array();
+ foreach ($quota as $type => $value) {
+ if ($type == 'size' || $type == 'count') {
+ $type = $type == 'count' ? 'C' : 'S';
+ }
+ $definition[] = $value . $type;
+ }
+ $definition = implode(',', $definition);
+ fputs($fh, "$definition\n");
+ fputs($fh, "$total_size $messages\n");
+ fclose($fh);
+ rename($tmp['filename'], $this->_rootdir . 'maildirsize');
+ foreach ($timestamps as $dir => $timestamp) {
+ if ($timestamp < filemtime($dir)) {
+ unlink($this->_rootdir . 'maildirsize');
+ break;
+ }
+ }
+
+ return array('size' => $total_size, 'count' => $messages, 'quota' => $quota);
+ }
+
+ /**
+ * @see http://www.inter7.com/courierimap/README.maildirquota.html "Calculating the quota for a Maildir++"
+ */
+ protected function _calculateQuota($forceRecalc = false) {
+ $fh = null;
+ $total_size = 0;
+ $messages = 0;
+ $maildirsize = '';
+ if (!$forceRecalc && file_exists($this->_rootdir . 'maildirsize') && filesize($this->_rootdir . 'maildirsize') < 5120) {
+ $fh = fopen($this->_rootdir . 'maildirsize', 'r');
+ }
+ if ($fh) {
+ $maildirsize = fread($fh, 5120);
+ if (strlen($maildirsize) >= 5120) {
+ fclose($fh);
+ $fh = null;
+ $maildirsize = '';
+ }
+ }
+ if (!$fh) {
+ $result = $this->_calculateMaildirsize();
+ $total_size = $result['size'];
+ $messages = $result['count'];
+ $quota = $result['quota'];
+ } else {
+ $maildirsize = explode("\n", $maildirsize);
+ if (is_array($this->_quota)) {
+ $quota = $this->_quota;
+ } else {
+ $definition = explode(',', $maildirsize[0]);
+ $quota = array();
+ foreach ($definition as $member) {
+ $key = $member[strlen($member) - 1];
+ if ($key == 'S' || $key == 'C') {
+ $key = $key == 'C' ? 'count' : 'size';
+ }
+ $quota[$key] = substr($member, 0, -1);
+ }
+ }
+ unset($maildirsize[0]);
+ foreach ($maildirsize as $line) {
+ list($size, $count) = explode(' ', trim($line));
+ $total_size += $size;
+ $messages += $count;
+ }
+ }
+
+ $over_quota = false;
+ $over_quota = $over_quota || (isset($quota['size']) && $total_size > $quota['size']);
+ $over_quota = $over_quota || (isset($quota['count']) && $messages > $quota['count']);
+ // NOTE: $maildirsize equals false if it wasn't set (AKA we recalculated) or it's only
+ // one line, because $maildirsize[0] gets unsetted.
+ // Also we're using local time to calculate the 15 minute offset. Touching a file just for known the
+ // local time of the file storage isn't worth the hassle.
+ if ($over_quota && ($maildirsize || filemtime($this->_rootdir . 'maildirsize') > time() - 900)) {
+ $result = $this->_calculateMaildirsize();
+ $total_size = $result['size'];
+ $messages = $result['count'];
+ $quota = $result['quota'];
+ $over_quota = false;
+ $over_quota = $over_quota || (isset($quota['size']) && $total_size > $quota['size']);
+ $over_quota = $over_quota || (isset($quota['count']) && $messages > $quota['count']);
+ }
+
+ if ($fh) {
+ // TODO is there a safe way to keep the handle open for writing?
+ fclose($fh);
+ }
+
+ return array('size' => $total_size, 'count' => $messages, 'quota' => $quota, 'over_quota' => $over_quota);
+ }
+
+ protected function _addQuotaEntry($size, $count = 1) {
+ if (!file_exists($this->_rootdir . 'maildirsize')) {
+ // TODO: should get file handler from _calculateQuota
+ }
+ $size = (int)$size;
+ $count = (int)$count;
+ file_put_contents($this->_rootdir . 'maildirsize', "$size $count\n", FILE_APPEND);
+ }
+
+ /**
+ * check if storage is currently over quota
+ *
+ * @param bool $detailedResponse return known data of quota and current size and message count @see _calculateQuota()
+ * @return bool|array over quota state or detailed response
+ */
+ public function checkQuota($detailedResponse = false, $forceRecalc = false) {
+ $result = $this->_calculateQuota($forceRecalc);
+ return $detailedResponse ? $result : $result['over_quota'];
+ }
+}
diff --git a/lib/Zend/Mail/Transport/Abstract.php b/lib/Zend/Mail/Transport/Abstract.php
new file mode 100644
index 0000000..603411a
--- /dev/null
+++ b/lib/Zend/Mail/Transport/Abstract.php
@@ -0,0 +1,350 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Transport
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Abstract.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Mime
+ */
+require_once 'Zend/Mime.php';
+
+
+/**
+ * Abstract for sending eMails through different
+ * ways of transport
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Transport
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+abstract class Zend_Mail_Transport_Abstract
+{
+ /**
+ * Mail body
+ * @var string
+ * @access public
+ */
+ public $body = '';
+
+ /**
+ * MIME boundary
+ * @var string
+ * @access public
+ */
+ public $boundary = '';
+
+ /**
+ * Mail header string
+ * @var string
+ * @access public
+ */
+ public $header = '';
+
+ /**
+ * Array of message headers
+ * @var array
+ * @access protected
+ */
+ protected $_headers = array();
+
+ /**
+ * Message is a multipart message
+ * @var boolean
+ * @access protected
+ */
+ protected $_isMultipart = false;
+
+ /**
+ * Zend_Mail object
+ * @var false|Zend_Mail
+ * @access protected
+ */
+ protected $_mail = false;
+
+ /**
+ * Array of message parts
+ * @var array
+ * @access protected
+ */
+ protected $_parts = array();
+
+ /**
+ * Recipients string
+ * @var string
+ * @access public
+ */
+ public $recipients = '';
+
+ /**
+ * EOL character string used by transport
+ * @var string
+ * @access public
+ */
+ public $EOL = "\r\n";
+
+ /**
+ * Send an email independent from the used transport
+ *
+ * The requisite information for the email will be found in the following
+ * properties:
+ *
+ * - {@link $recipients} - list of recipients (string)
+ * - {@link $header} - message header
+ * - {@link $body} - message body
+ */
+ abstract protected function _sendMail();
+
+ /**
+ * Return all mail headers as an array
+ *
+ * If a boundary is given, a multipart header is generated with a
+ * Content-Type of either multipart/alternative or multipart/mixed depending
+ * on the mail parts present in the {@link $_mail Zend_Mail object} present.
+ *
+ * @param string $boundary
+ * @return array
+ */
+ protected function _getHeaders($boundary)
+ {
+ if (null !== $boundary) {
+ // Build multipart mail
+ $type = $this->_mail->getType();
+ if (!$type) {
+ if ($this->_mail->hasAttachments) {
+ $type = Zend_Mime::MULTIPART_MIXED;
+ } elseif ($this->_mail->getBodyText() && $this->_mail->getBodyHtml()) {
+ $type = Zend_Mime::MULTIPART_ALTERNATIVE;
+ } else {
+ $type = Zend_Mime::MULTIPART_MIXED;
+ }
+ }
+
+ $this->_headers['Content-Type'] = array(
+ $type . '; charset="' . $this->_mail->getCharset() . '";'
+ . $this->EOL
+ . " " . 'boundary="' . $boundary . '"'
+ );
+ $this->_headers['MIME-Version'] = array('1.0');
+
+ $this->boundary = $boundary;
+ }
+
+ return $this->_headers;
+ }
+
+ /**
+ * Prepend header name to header value
+ *
+ * @param string $item
+ * @param string $key
+ * @param string $prefix
+ * @static
+ * @access protected
+ * @return void
+ */
+ protected static function _formatHeader(&$item, $key, $prefix)
+ {
+ $item = $prefix . ': ' . $item;
+ }
+
+ /**
+ * Prepare header string for use in transport
+ *
+ * Prepares and generates {@link $header} based on the headers provided.
+ *
+ * @param mixed $headers
+ * @access protected
+ * @return void
+ * @throws Zend_Mail_Transport_Exception if any header lines exceed 998
+ * characters
+ */
+ protected function _prepareHeaders($headers)
+ {
+ if (!$this->_mail) {
+ /**
+ * @see Zend_Mail_Transport_Exception
+ */
+ require_once 'Zend/Mail/Transport/Exception.php';
+ throw new Zend_Mail_Transport_Exception('Missing Zend_Mail object in _mail property');
+ }
+
+ $this->header = '';
+
+ foreach ($headers as $header => $content) {
+ if (isset($content['append'])) {
+ unset($content['append']);
+ $value = implode(',' . $this->EOL . ' ', $content);
+ $this->header .= $header . ': ' . $value . $this->EOL;
+ } else {
+ array_walk($content, array(get_class($this), '_formatHeader'), $header);
+ $this->header .= implode($this->EOL, $content) . $this->EOL;
+ }
+ }
+
+ // Sanity check on headers -- should not be > 998 characters
+ $sane = true;
+ foreach (explode($this->EOL, $this->header) as $line) {
+ if (strlen(trim($line)) > 998) {
+ $sane = false;
+ break;
+ }
+ }
+ if (!$sane) {
+ /**
+ * @see Zend_Mail_Transport_Exception
+ */
+ require_once 'Zend/Mail/Transport/Exception.php';
+ throw new Zend_Mail_Exception('At least one mail header line is too long');
+ }
+ }
+
+ /**
+ * Generate MIME compliant message from the current configuration
+ *
+ * If both a text and HTML body are present, generates a
+ * multipart/alternative Zend_Mime_Part containing the headers and contents
+ * of each. Otherwise, uses whichever of the text or HTML parts present.
+ *
+ * The content part is then prepended to the list of Zend_Mime_Parts for
+ * this message.
+ *
+ * @return void
+ */
+ protected function _buildBody()
+ {
+ if (($text = $this->_mail->getBodyText())
+ && ($html = $this->_mail->getBodyHtml()))
+ {
+ // Generate unique boundary for multipart/alternative
+ $mime = new Zend_Mime(null);
+ $boundaryLine = $mime->boundaryLine($this->EOL);
+ $boundaryEnd = $mime->mimeEnd($this->EOL);
+
+ $text->disposition = false;
+ $html->disposition = false;
+
+ $body = $boundaryLine
+ . $text->getHeaders($this->EOL)
+ . $this->EOL
+ . $text->getContent($this->EOL)
+ . $this->EOL
+ . $boundaryLine
+ . $html->getHeaders($this->EOL)
+ . $this->EOL
+ . $html->getContent($this->EOL)
+ . $this->EOL
+ . $boundaryEnd;
+
+ $mp = new Zend_Mime_Part($body);
+ $mp->type = Zend_Mime::MULTIPART_ALTERNATIVE;
+ $mp->boundary = $mime->boundary();
+
+ $this->_isMultipart = true;
+
+ // Ensure first part contains text alternatives
+ array_unshift($this->_parts, $mp);
+
+ // Get headers
+ $this->_headers = $this->_mail->getHeaders();
+ return;
+ }
+
+ // If not multipart, then get the body
+ if (false !== ($body = $this->_mail->getBodyHtml())) {
+ array_unshift($this->_parts, $body);
+ } elseif (false !== ($body = $this->_mail->getBodyText())) {
+ array_unshift($this->_parts, $body);
+ }
+
+ if (!$body) {
+ /**
+ * @see Zend_Mail_Transport_Exception
+ */
+ require_once 'Zend/Mail/Transport/Exception.php';
+ throw new Zend_Mail_Transport_Exception('No body specified');
+ }
+
+ // Get headers
+ $this->_headers = $this->_mail->getHeaders();
+ $headers = $body->getHeadersArray($this->EOL);
+ foreach ($headers as $header) {
+ // Headers in Zend_Mime_Part are kept as arrays with two elements, a
+ // key and a value
+ $this->_headers[$header[0]] = array($header[1]);
+ }
+ }
+
+ /**
+ * Send a mail using this transport
+ *
+ * @param Zend_Mail $mail
+ * @access public
+ * @return void
+ * @throws Zend_Mail_Transport_Exception if mail is empty
+ */
+ public function send(Zend_Mail $mail)
+ {
+ $this->_isMultipart = false;
+ $this->_mail = $mail;
+ $this->_parts = $mail->getParts();
+ $mime = $mail->getMime();
+
+ // Build body content
+ $this->_buildBody();
+
+ // Determine number of parts and boundary
+ $count = count($this->_parts);
+ $boundary = null;
+ if ($count < 1) {
+ /**
+ * @see Zend_Mail_Transport_Exception
+ */
+ require_once 'Zend/Mail/Transport/Exception.php';
+ throw new Zend_Mail_Transport_Exception('Empty mail cannot be sent');
+ }
+
+ if ($count > 1) {
+ // Multipart message; create new MIME object and boundary
+ $mime = new Zend_Mime($this->_mail->getMimeBoundary());
+ $boundary = $mime->boundary();
+ } elseif ($this->_isMultipart) {
+ // multipart/alternative -- grab boundary
+ $boundary = $this->_parts[0]->boundary;
+ }
+
+ // Determine recipients, and prepare headers
+ $this->recipients = implode(',', $mail->getRecipients());
+ $this->_prepareHeaders($this->_getHeaders($boundary));
+
+ // Create message body
+ // This is done so that the same Zend_Mail object can be used in
+ // multiple transports
+ $message = new Zend_Mime_Message();
+ $message->setParts($this->_parts);
+ $message->setMime($mime);
+ $this->body = $message->generateMessage($this->EOL);
+
+ // Send to transport!
+ $this->_sendMail();
+ }
+}
diff --git a/lib/Zend/Mail/Transport/Exception.php b/lib/Zend/Mail/Transport/Exception.php
new file mode 100644
index 0000000..16ef240
--- /dev/null
+++ b/lib/Zend/Mail/Transport/Exception.php
@@ -0,0 +1,39 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Transport
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Exception.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Mail_Exception
+ */
+require_once 'Zend/Mail/Exception.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Transport
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Mail_Transport_Exception extends Zend_Mail_Exception
+{}
+
diff --git a/lib/Zend/Mail/Transport/Sendmail.php b/lib/Zend/Mail/Transport/Sendmail.php
new file mode 100644
index 0000000..651df3c
--- /dev/null
+++ b/lib/Zend/Mail/Transport/Sendmail.php
@@ -0,0 +1,170 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Transport
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Sendmail.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Mail_Transport_Abstract
+ */
+require_once 'Zend/Mail/Transport/Abstract.php';
+
+
+/**
+ * Class for sending eMails via the PHP internal mail() function
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Transport
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Mail_Transport_Sendmail extends Zend_Mail_Transport_Abstract
+{
+ /**
+ * Subject
+ * @var string
+ * @access public
+ */
+ public $subject = null;
+
+
+ /**
+ * Config options for sendmail parameters
+ *
+ * @var string
+ */
+ public $parameters;
+
+
+ /**
+ * EOL character string
+ * @var string
+ * @access public
+ */
+ public $EOL = PHP_EOL;
+
+
+ /**
+ * Constructor.
+ *
+ * @param string $parameters OPTIONAL (Default: null)
+ * @return void
+ */
+ public function __construct($parameters = null)
+ {
+ $this->parameters = $parameters;
+ }
+
+
+ /**
+ * Send mail using PHP native mail()
+ *
+ * @access public
+ * @return void
+ * @throws Zend_Mail_Transport_Exception on mail() failure
+ */
+ public function _sendMail()
+ {
+ if ($this->parameters === null) {
+ $result = mail(
+ $this->recipients,
+ $this->_mail->getSubject(),
+ $this->body,
+ $this->header);
+ } else {
+ $result = mail(
+ $this->recipients,
+ $this->_mail->getSubject(),
+ $this->body,
+ $this->header,
+ $this->parameters);
+ }
+ if (!$result) {
+ /**
+ * @see Zend_Mail_Transport_Exception
+ */
+ require_once 'Zend/Mail/Transport/Exception.php';
+ throw new Zend_Mail_Transport_Exception('Unable to send mail');
+ }
+ }
+
+
+ /**
+ * Format and fix headers
+ *
+ * mail() uses its $to and $subject arguments to set the To: and Subject:
+ * headers, respectively. This method strips those out as a sanity check to
+ * prevent duplicate header entries.
+ *
+ * @access protected
+ * @param array $headers
+ * @return void
+ * @throws Zend_Mail_Transport_Exception
+ */
+ protected function _prepareHeaders($headers)
+ {
+ if (!$this->_mail) {
+ /**
+ * @see Zend_Mail_Transport_Exception
+ */
+ require_once 'Zend/Mail/Transport/Exception.php';
+ throw new Zend_Mail_Transport_Exception('_prepareHeaders requires a registered Zend_Mail object');
+ }
+
+ // mail() uses its $to parameter to set the To: header, and the $subject
+ // parameter to set the Subject: header. We need to strip them out.
+ if (0 === strpos(PHP_OS, 'WIN')) {
+ // If the current recipients list is empty, throw an error
+ if (empty($this->recipients)) {
+ /**
+ * @see Zend_Mail_Transport_Exception
+ */
+ require_once 'Zend/Mail/Transport/Exception.php';
+ throw new Zend_Mail_Transport_Exception('Missing To addresses');
+ }
+ } else {
+ // All others, simply grab the recipients and unset the To: header
+ if (!isset($headers['To'])) {
+ /**
+ * @see Zend_Mail_Transport_Exception
+ */
+ require_once 'Zend/Mail/Transport/Exception.php';
+ throw new Zend_Mail_Transport_Exception('Missing To header');
+ }
+
+ unset($headers['To']['append']);
+ $this->recipients = implode(',', $headers['To']);
+ }
+
+ // Remove recipient header
+ unset($headers['To']);
+
+ // Remove subject header, if present
+ if (isset($headers['Subject'])) {
+ unset($headers['Subject']);
+ }
+
+ // Prepare headers
+ parent::_prepareHeaders($headers);
+ }
+
+}
+
diff --git a/lib/Zend/Mail/Transport/Smtp.php b/lib/Zend/Mail/Transport/Smtp.php
new file mode 100644
index 0000000..714266e
--- /dev/null
+++ b/lib/Zend/Mail/Transport/Smtp.php
@@ -0,0 +1,241 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Transport
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Smtp.php 12519 2008-11-10 18:41:24Z alexander $
+ */
+
+
+/**
+ * @see Zend_Loader
+ */
+require_once 'Zend/Loader.php';
+
+/**
+ * @see Zend_Mime
+ */
+require_once 'Zend/Mime.php';
+
+/**
+ * @see Zend_Mail_Protocol_Smtp
+ */
+require_once 'Zend/Mail/Protocol/Smtp.php';
+
+/**
+ * @see Zend_Mail_Transport_Abstract
+ */
+require_once 'Zend/Mail/Transport/Abstract.php';
+
+
+/**
+ * SMTP connection object
+ *
+ * Loads an instance of Zend_Mail_Protocol_Smtp and forwards smtp transactions
+ *
+ * @category Zend
+ * @package Zend_Mail
+ * @subpackage Transport
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Mail_Transport_Smtp extends Zend_Mail_Transport_Abstract
+{
+ /**
+ * EOL character string used by transport
+ * @var string
+ * @access public
+ */
+ public $EOL = "\n";
+
+ /**
+ * Remote smtp hostname or i.p.
+ *
+ * @var string
+ */
+ protected $_host;
+
+
+ /**
+ * Port number
+ *
+ * @var integer|null
+ */
+ protected $_port;
+
+
+ /**
+ * Local client hostname or i.p.
+ *
+ * @var string
+ */
+ protected $_name = 'localhost';
+
+
+ /**
+ * Authentication type OPTIONAL
+ *
+ * @var string
+ */
+ protected $_auth;
+
+
+ /**
+ * Config options for authentication
+ *
+ * @var array
+ */
+ protected $_config;
+
+
+ /**
+ * Instance of Zend_Mail_Protocol_Smtp
+ *
+ * @var Zend_Mail_Protocol_Smtp
+ */
+ protected $_connection;
+
+
+ /**
+ * Constructor.
+ *
+ * @param string $host OPTIONAL (Default: 127.0.0.1)
+ * @param array|null $config OPTIONAL (Default: null)
+ * @return void
+ */
+ public function __construct($host = '127.0.0.1', Array $config = array())
+ {
+ if (isset($config['name'])) {
+ $this->_name = $config['name'];
+ }
+ if (isset($config['port'])) {
+ $this->_port = $config['port'];
+ }
+ if (isset($config['auth'])) {
+ $this->_auth = $config['auth'];
+ }
+
+ $this->_host = $host;
+ $this->_config = $config;
+ }
+
+
+ /**
+ * Class destructor to ensure all open connections are closed
+ *
+ * @return void
+ */
+ public function __destruct()
+ {
+ if ($this->_connection instanceof Zend_Mail_Protocol_Smtp) {
+ try {
+ $this->_connection->quit();
+ } catch (Zend_Mail_Protocol_Exception $e) {
+ // ignore
+ }
+ $this->_connection->disconnect();
+ }
+ }
+
+
+ /**
+ * Sets the connection protocol instance
+ *
+ * @param Zend_Mail_Protocol_Abstract $client
+ *
+ * @return void
+ */
+ public function setConnection(Zend_Mail_Protocol_Abstract $connection)
+ {
+ $this->_connection = $connection;
+ }
+
+
+ /**
+ * Gets the connection protocol instance
+ *
+ * @return Zend_Mail_Protocol|null
+ */
+ public function getConnection()
+ {
+ return $this->_connection;
+ }
+
+ /**
+ * Send an email via the SMTP connection protocol
+ *
+ * The connection via the protocol adapter is made just-in-time to allow a
+ * developer to add a custom adapter if required before mail is sent.
+ *
+ * @return void
+ */
+ public function _sendMail()
+ {
+ // If sending multiple messages per session use existing adapter
+ if (!($this->_connection instanceof Zend_Mail_Protocol_Smtp)) {
+ // Check if authentication is required and determine required class
+ $connectionClass = 'Zend_Mail_Protocol_Smtp';
+ if ($this->_auth) {
+ $connectionClass .= '_Auth_' . ucwords($this->_auth);
+ }
+ Zend_Loader::loadClass($connectionClass);
+ $this->setConnection(new $connectionClass($this->_host, $this->_port, $this->_config));
+ $this->_connection->connect();
+ $this->_connection->helo($this->_name);
+ } else {
+ // Reset connection to ensure reliable transaction
+ $this->_connection->rset();
+ }
+
+ // Set mail return path from sender email address
+ $this->_connection->mail($this->_mail->getReturnPath());
+
+ // Set recipient forward paths
+ foreach ($this->_mail->getRecipients() as $recipient) {
+ $this->_connection->rcpt($recipient);
+ }
+
+ // Issue DATA command to client
+ $this->_connection->data($this->header . Zend_Mime::LINEEND . $this->body);
+ }
+
+ /**
+ * Format and fix headers
+ *
+ * Some SMTP servers do not strip BCC headers. Most clients do it themselves as do we.
+ *
+ * @access protected
+ * @param array $headers
+ * @return void
+ * @throws Zend_Transport_Exception
+ */
+ protected function _prepareHeaders($headers)
+ {
+ if (!$this->_mail) {
+ /**
+ * @see Zend_Mail_Transport_Exception
+ */
+ require_once 'Zend/Mail/Transport/Exception.php';
+ throw new Zend_Mail_Transport_Exception('_prepareHeaders requires a registered Zend_Mail object');
+ }
+
+ unset($headers['Bcc']);
+
+ // Prepare headers
+ parent::_prepareHeaders($headers);
+ }
+}
diff --git a/lib/Zend/Mime.php b/lib/Zend/Mime.php
new file mode 100644
index 0000000..414e541
--- /dev/null
+++ b/lib/Zend/Mime.php
@@ -0,0 +1,365 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mime
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+
+/**
+ * Support class for MultiPart Mime Messages
+ *
+ * @category Zend
+ * @package Zend_Mime
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Mime
+{
+ const TYPE_OCTETSTREAM = 'application/octet-stream';
+ const TYPE_TEXT = 'text/plain';
+ const TYPE_HTML = 'text/html';
+ const ENCODING_7BIT = '7bit';
+ const ENCODING_8BIT = '8bit';
+ const ENCODING_QUOTEDPRINTABLE = 'quoted-printable';
+ const ENCODING_BASE64 = 'base64';
+ const DISPOSITION_ATTACHMENT = 'attachment';
+ const DISPOSITION_INLINE = 'inline';
+ const LINELENGTH = 72;
+ const LINEEND = "\n";
+ const MULTIPART_ALTERNATIVE = 'multipart/alternative';
+ const MULTIPART_MIXED = 'multipart/mixed';
+ const MULTIPART_RELATED = 'multipart/related';
+
+ protected $_boundary;
+ protected static $makeUnique = 0;
+
+ // lookup-Tables for QuotedPrintable
+ public static $qpKeys = array(
+ "\x00","\x01","\x02","\x03","\x04","\x05","\x06","\x07",
+ "\x08","\x09","\x0A","\x0B","\x0C","\x0D","\x0E","\x0F",
+ "\x10","\x11","\x12","\x13","\x14","\x15","\x16","\x17",
+ "\x18","\x19","\x1A","\x1B","\x1C","\x1D","\x1E","\x1F",
+ "\x7F","\x80","\x81","\x82","\x83","\x84","\x85","\x86",
+ "\x87","\x88","\x89","\x8A","\x8B","\x8C","\x8D","\x8E",
+ "\x8F","\x90","\x91","\x92","\x93","\x94","\x95","\x96",
+ "\x97","\x98","\x99","\x9A","\x9B","\x9C","\x9D","\x9E",
+ "\x9F","\xA0","\xA1","\xA2","\xA3","\xA4","\xA5","\xA6",
+ "\xA7","\xA8","\xA9","\xAA","\xAB","\xAC","\xAD","\xAE",
+ "\xAF","\xB0","\xB1","\xB2","\xB3","\xB4","\xB5","\xB6",
+ "\xB7","\xB8","\xB9","\xBA","\xBB","\xBC","\xBD","\xBE",
+ "\xBF","\xC0","\xC1","\xC2","\xC3","\xC4","\xC5","\xC6",
+ "\xC7","\xC8","\xC9","\xCA","\xCB","\xCC","\xCD","\xCE",
+ "\xCF","\xD0","\xD1","\xD2","\xD3","\xD4","\xD5","\xD6",
+ "\xD7","\xD8","\xD9","\xDA","\xDB","\xDC","\xDD","\xDE",
+ "\xDF","\xE0","\xE1","\xE2","\xE3","\xE4","\xE5","\xE6",
+ "\xE7","\xE8","\xE9","\xEA","\xEB","\xEC","\xED","\xEE",
+ "\xEF","\xF0","\xF1","\xF2","\xF3","\xF4","\xF5","\xF6",
+ "\xF7","\xF8","\xF9","\xFA","\xFB","\xFC","\xFD","\xFE",
+ "\xFF"
+ );
+
+ public static $qpReplaceValues = array(
+ "=00","=01","=02","=03","=04","=05","=06","=07",
+ "=08","=09","=0A","=0B","=0C","=0D","=0E","=0F",
+ "=10","=11","=12","=13","=14","=15","=16","=17",
+ "=18","=19","=1A","=1B","=1C","=1D","=1E","=1F",
+ "=7F","=80","=81","=82","=83","=84","=85","=86",
+ "=87","=88","=89","=8A","=8B","=8C","=8D","=8E",
+ "=8F","=90","=91","=92","=93","=94","=95","=96",
+ "=97","=98","=99","=9A","=9B","=9C","=9D","=9E",
+ "=9F","=A0","=A1","=A2","=A3","=A4","=A5","=A6",
+ "=A7","=A8","=A9","=AA","=AB","=AC","=AD","=AE",
+ "=AF","=B0","=B1","=B2","=B3","=B4","=B5","=B6",
+ "=B7","=B8","=B9","=BA","=BB","=BC","=BD","=BE",
+ "=BF","=C0","=C1","=C2","=C3","=C4","=C5","=C6",
+ "=C7","=C8","=C9","=CA","=CB","=CC","=CD","=CE",
+ "=CF","=D0","=D1","=D2","=D3","=D4","=D5","=D6",
+ "=D7","=D8","=D9","=DA","=DB","=DC","=DD","=DE",
+ "=DF","=E0","=E1","=E2","=E3","=E4","=E5","=E6",
+ "=E7","=E8","=E9","=EA","=EB","=EC","=ED","=EE",
+ "=EF","=F0","=F1","=F2","=F3","=F4","=F5","=F6",
+ "=F7","=F8","=F9","=FA","=FB","=FC","=FD","=FE",
+ "=FF"
+ );
+
+ public static $qpKeysString =
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x7F\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF";
+
+ /**
+ * Check if the given string is "printable"
+ *
+ * Checks that a string contains no unprintable characters. If this returns
+ * false, encode the string for secure delivery.
+ *
+ * @param string $str
+ * @return boolean
+ */
+ public static function isPrintable($str)
+ {
+ return (strcspn($str, self::$qpKeysString) == strlen($str));
+ }
+
+ /**
+ * Encode a given string with the QUOTED_PRINTABLE mechanism and wrap the lines.
+ *
+ * @param string $str
+ * @param int $lineLength Defaults to {@link LINELENGTH}
+ * @param int $lineEnd Defaults to {@link LINEEND}
+ * @return string
+ */
+ public static function encodeQuotedPrintable($str,
+ $lineLength = self::LINELENGTH,
+ $lineEnd = self::LINEEND)
+ {
+ $out = '';
+ $str = self::_encodeQuotedPrintable($str);
+
+ // Split encoded text into separate lines
+ while ($str) {
+ $ptr = strlen($str);
+ if ($ptr > $lineLength) {
+ $ptr = $lineLength;
+ }
+
+ // Ensure we are not splitting across an encoded character
+ $pos = strrpos(substr($str, 0, $ptr), '=');
+ if ($pos !== false && $pos >= $ptr - 2) {
+ $ptr = $pos;
+ }
+
+ // Check if there is a space at the end of the line and rewind
+ if ($ptr > 0 && $str[$ptr - 1] == ' ') {
+ --$ptr;
+ }
+
+ // Add string and continue
+ $out .= substr($str, 0, $ptr) . '=' . $lineEnd;
+ $str = substr($str, $ptr);
+ }
+
+ $out = rtrim($out, $lineEnd);
+ $out = rtrim($out, '=');
+ return $out;
+ }
+
+ /**
+ * Converts a string into quoted printable format.
+ *
+ * @param string $str
+ * @return string
+ */
+ private static function _encodeQuotedPrintable($str)
+ {
+ $str = str_replace('=', '=3D', $str);
+ $str = str_replace(self::$qpKeys, self::$qpReplaceValues, $str);
+ $str = rtrim($str);
+ return $str;
+ }
+
+ /**
+ * Encode a given string with the QUOTED_PRINTABLE mechanism for Mail Headers.
+ *
+ * Mail headers depend on an extended quoted printable algorithm otherwise
+ * a range of bugs can occur.
+ *
+ * @param string $str
+ * @param string $charset
+ * @param int $lineLength Defaults to {@link LINELENGTH}
+ * @param int $lineEnd Defaults to {@link LINEEND}
+ * @return string
+ */
+ public static function encodeQuotedPrintableHeader($str, $charset,
+ $lineLength = self::LINELENGTH,
+ $lineEnd = self::LINEEND)
+ {
+ // Reduce line-length by the length of the required delimiter, charsets and encoding
+ $prefix = sprintf('=?%s?Q?', $charset);
+ $lineLength = $lineLength-strlen($prefix)-3;
+
+ $str = self::_encodeQuotedPrintable($str);
+
+ // Mail-Header required chars have to be encoded also:
+ $str = str_replace(array('?', ' ', '_'), array('=3F', '=20', '=5F'), $str);
+
+ // initialize first line, we need it anyways
+ $lines = array(0 => "");
+
+ // Split encoded text into separate lines
+ $tmp = "";
+ while(strlen($str) > 0) {
+ $currentLine = max(count($lines)-1, 0);
+ $token = self::getNextQuotedPrintableToken($str);
+ $str = substr($str, strlen($token));
+
+ $tmp .= $token;
+ if( (strlen($token) == 1 && strpbrk($token, '!%,.:;<>'))
+ || in_array($token, array("=3F", "=20", "=5F")) ) {
+ // only if we have a single char token or space, we can append the
+ // tempstring it to the current line or start a new line if necessary.
+ if(strlen($lines[$currentLine].$tmp) > $lineLength) {
+ $lines[$currentLine+1] = $tmp;
+ } else {
+ $lines[$currentLine] .= $tmp;
+ }
+ $tmp = "";
+ }
+ // don't forget to append the rest to the last line
+ if(strlen($str) == 0) {
+ $lines[$currentLine] .= $tmp;
+ }
+ }
+
+ // assemble the lines together by pre- and appending delimiters, charset, encoding.
+ for($i = 0; $i < count($lines); $i++) {
+ $lines[$i] = " ".$prefix.$lines[$i]."?=";
+ }
+ $str = trim(implode($lineEnd, $lines));
+ return $str;
+ }
+
+ /**
+ * Retrieves the first token from a quoted printable string.
+ *
+ * @param string $str
+ * @return string
+ */
+ private static function getNextQuotedPrintableToken($str)
+ {
+ if(substr($str, 0, 1) == "=") {
+ $token = substr($str, 0, 3);
+ } else {
+ $token = substr($str, 0, 1);
+ }
+ return $token;
+ }
+
+ /**
+ * Encode a given string in mail header compatible base64 encoding.
+ *
+ * @param string $str
+ * @param string $charset
+ * @param int $lineLength Defaults to {@link LINELENGTH}
+ * @param int $lineEnd Defaults to {@link LINEEND}
+ * @return string
+ */
+ public static function encodeBase64Header($str,
+ $charset,
+ $lineLength = self::LINELENGTH,
+ $lineEnd = self::LINEEND)
+ {
+ $prefix = '=?' . $charset . '?B?';
+ $suffix = '?=';
+ $remainingLength = $lineLength - strlen($prefix) - strlen($suffix);
+
+ $encodedValue = self::encodeBase64($str, $remainingLength, $lineEnd);
+ $encodedValue = str_replace($lineEnd, $suffix . $lineEnd . ' ' . $prefix, $encodedValue);
+ $encodedValue = $prefix . $encodedValue . $suffix;
+ return $encodedValue;
+ }
+
+ /**
+ * Encode a given string in base64 encoding and break lines
+ * according to the maximum linelength.
+ *
+ * @param string $str
+ * @param int $lineLength Defaults to {@link LINELENGTH}
+ * @param int $lineEnd Defaults to {@link LINEEND}
+ * @return string
+ */
+ public static function encodeBase64($str,
+ $lineLength = self::LINELENGTH,
+ $lineEnd = self::LINEEND)
+ {
+ return rtrim(chunk_split(base64_encode($str), $lineLength, $lineEnd));
+ }
+
+ /**
+ * Constructor
+ *
+ * @param null|string $boundary
+ * @access public
+ * @return void
+ */
+ public function __construct($boundary = null)
+ {
+ // This string needs to be somewhat unique
+ if ($boundary === null) {
+ $this->_boundary = '=_' . md5(microtime(1) . self::$makeUnique++);
+ } else {
+ $this->_boundary = $boundary;
+ }
+ }
+
+ /**
+ * Encode the given string with the given encoding.
+ *
+ * @param string $str
+ * @param string $encoding
+ * @param string $EOL EOL string; defaults to {@link Zend_Mime::LINEEND}
+ * @return string
+ */
+ public static function encode($str, $encoding, $EOL = self::LINEEND)
+ {
+ switch ($encoding) {
+ case self::ENCODING_BASE64:
+ return self::encodeBase64($str, self::LINELENGTH, $EOL);
+
+ case self::ENCODING_QUOTEDPRINTABLE:
+ return self::encodeQuotedPrintable($str, self::LINELENGTH, $EOL);
+
+ default:
+ /**
+ * @todo 7Bit and 8Bit is currently handled the same way.
+ */
+ return $str;
+ }
+ }
+
+ /**
+ * Return a MIME boundary
+ *
+ * @access public
+ * @return string
+ */
+ public function boundary()
+ {
+ return $this->_boundary;
+ }
+
+ /**
+ * Return a MIME boundary line
+ *
+ * @param mixed $EOL Defaults to {@link LINEEND}
+ * @access public
+ * @return string
+ */
+ public function boundaryLine($EOL = self::LINEEND)
+ {
+ return $EOL . '--' . $this->_boundary . $EOL;
+ }
+
+ /**
+ * Return MIME ending
+ *
+ * @access public
+ * @return string
+ */
+ public function mimeEnd($EOL = self::LINEEND)
+ {
+ return $EOL . '--' . $this->_boundary . '--' . $EOL;
+ }
+}
diff --git a/lib/Zend/Mime/Decode.php b/lib/Zend/Mime/Decode.php
new file mode 100644
index 0000000..cc4de8b
--- /dev/null
+++ b/lib/Zend/Mime/Decode.php
@@ -0,0 +1,243 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mime
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+/**
+ * @see Zend_Mime
+ */
+require_once 'Zend/Mime.php';
+
+/**
+ * @category Zend
+ * @package Zend_Mime
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Mime_Decode
+{
+ /**
+ * Explode MIME multipart string into seperate parts
+ *
+ * Parts consist of the header and the body of each MIME part.
+ *
+ * @param string $body raw body of message
+ * @param string $boundary boundary as found in content-type
+ * @return array parts with content of each part, empty if no parts found
+ * @throws Zend_Exception
+ */
+ public static function splitMime($body, $boundary)
+ {
+ // TODO: we're ignoring \r for now - is this function fast enough and is it safe to asume noone needs \r?
+ $body = str_replace("\r", '', $body);
+
+ $start = 0;
+ $res = array();
+ // find every mime part limiter and cut out the
+ // string before it.
+ // the part before the first boundary string is discarded:
+ $p = strpos($body, '--' . $boundary . "\n", $start);
+ if ($p === false) {
+ // no parts found!
+ return array();
+ }
+
+ // position after first boundary line
+ $start = $p + 3 + strlen($boundary);
+
+ while (($p = strpos($body, '--' . $boundary . "\n", $start)) !== false) {
+ $res[] = substr($body, $start, $p-$start);
+ $start = $p + 3 + strlen($boundary);
+ }
+
+ // no more parts, find end boundary
+ $p = strpos($body, '--' . $boundary . '--', $start);
+ if ($p===false) {
+ throw new Zend_Exception('Not a valid Mime Message: End Missing');
+ }
+
+ // the remaining part also needs to be parsed:
+ $res[] = substr($body, $start, $p-$start);
+ return $res;
+ }
+
+ /**
+ * decodes a mime encoded String and returns a
+ * struct of parts with header and body
+ *
+ * @param string $message raw message content
+ * @param string $boundary boundary as found in content-type
+ * @param string $EOL EOL string; defaults to {@link Zend_Mime::LINEEND}
+ * @return array|null parts as array('header' => array(name => value), 'body' => content), null if no parts found
+ * @throws Zend_Exception
+ */
+ public static function splitMessageStruct($message, $boundary, $EOL = Zend_Mime::LINEEND)
+ {
+ $parts = self::splitMime($message, $boundary);
+ if (count($parts) <= 0) {
+ return null;
+ }
+ $result = array();
+ foreach ($parts as $part) {
+ self::splitMessage($part, $headers, $body, $EOL);
+ $result[] = array('header' => $headers,
+ 'body' => $body );
+ }
+ return $result;
+ }
+
+ /**
+ * split a message in header and body part, if no header or an
+ * invalid header is found $headers is empty
+ *
+ * The charset of the returned headers depend on your iconv settings.
+ *
+ * @param string $message raw message with header and optional content
+ * @param array $headers output param, array with headers as array(name => value)
+ * @param string $body output param, content of message
+ * @param string $EOL EOL string; defaults to {@link Zend_Mime::LINEEND}
+ * @return null
+ */
+ public static function splitMessage($message, &$headers, &$body, $EOL = Zend_Mime::LINEEND)
+ {
+ // check for valid header at first line
+ $firstline = strtok($message, "\n");
+ if (!preg_match('%^[^\s]+[^:]*:%', $firstline)) {
+ $headers = array();
+ // TODO: we're ignoring \r for now - is this function fast enough and is it safe to asume noone needs \r?
+ $body = str_replace(array("\r", "\n"), array('', $EOL), $message);
+ return;
+ }
+
+ // find an empty line between headers and body
+ // default is set new line
+ if (strpos($message, $EOL . $EOL)) {
+ list($headers, $body) = explode($EOL . $EOL, $message, 2);
+ // next is the standard new line
+ } else if ($EOL != "\r\n" && strpos($message, "\r\n\r\n")) {
+ list($headers, $body) = explode("\r\n\r\n", $message, 2);
+ // next is the other "standard" new line
+ } else if ($EOL != "\n" && strpos($message, "\n\n")) {
+ list($headers, $body) = explode("\n\n", $message, 2);
+ // at last resort find anything that looks like a new line
+ } else {
+ @list($headers, $body) = @preg_split("%([\r\n]+)\\1%U", $message, 2);
+ }
+
+ $headers = iconv_mime_decode_headers($headers, ICONV_MIME_DECODE_CONTINUE_ON_ERROR);
+
+ if ($headers === false ) {
+ // an error occurs during the decoding
+ return;
+ }
+
+ // normalize header names
+ foreach ($headers as $name => $header) {
+ $lower = strtolower($name);
+ if ($lower == $name) {
+ continue;
+ }
+ unset($headers[$name]);
+ if (!isset($headers[$lower])) {
+ $headers[$lower] = $header;
+ continue;
+ }
+ if (is_array($headers[$lower])) {
+ $headers[$lower][] = $header;
+ continue;
+ }
+ $headers[$lower] = array($headers[$lower], $header);
+ }
+ }
+
+ /**
+ * split a content type in its different parts
+ *
+ * @param string $type content-type
+ * @param string $wantedPart the wanted part, else an array with all parts is returned
+ * @return string|array wanted part or all parts as array('type' => content-type, partname => value)
+ */
+ public static function splitContentType($type, $wantedPart = null)
+ {
+ return self::splitHeaderField($type, $wantedPart, 'type');
+ }
+
+ /**
+ * split a header field like content type in its different parts
+ *
+ * @param string $type header field
+ * @param string $wantedPart the wanted part, else an array with all parts is returned
+ * @param string $firstName key name for the first part
+ * @return string|array wanted part or all parts as array($firstName => firstPart, partname => value)
+ * @throws Zend_Exception
+ */
+ public static function splitHeaderField($field, $wantedPart = null, $firstName = 0)
+ {
+ $wantedPart = strtolower($wantedPart);
+ $firstName = strtolower($firstName);
+
+ // special case - a bit optimized
+ if ($firstName === $wantedPart) {
+ $field = strtok($field, ';');
+ return $field[0] == '"' ? substr($field, 1, -1) : $field;
+ }
+
+ $field = $firstName . '=' . $field;
+ if (!preg_match_all('%([^=\s]+)\s*=\s*("[^"]+"|[^;]+)(;\s*|$)%', $field, $matches)) {
+ throw new Zend_Exception('not a valid header field');
+ }
+
+ if ($wantedPart) {
+ foreach ($matches[1] as $key => $name) {
+ if (strcasecmp($name, $wantedPart)) {
+ continue;
+ }
+ if ($matches[2][$key][0] != '"') {
+ return $matches[2][$key];
+ }
+ return substr($matches[2][$key], 1, -1);
+ }
+ return null;
+ }
+
+ $split = array();
+ foreach ($matches[1] as $key => $name) {
+ $name = strtolower($name);
+ if ($matches[2][$key][0] == '"') {
+ $split[$name] = substr($matches[2][$key], 1, -1);
+ } else {
+ $split[$name] = $matches[2][$key];
+ }
+ }
+
+ return $split;
+ }
+
+ /**
+ * decode a quoted printable encoded string
+ *
+ * The charset of the returned string depends on your iconv settings.
+ *
+ * @param string encoded string
+ * @return string decoded string
+ */
+ public static function decodeQuotedPrintable($string)
+ {
+ return iconv_mime_decode($string, ICONV_MIME_DECODE_CONTINUE_ON_ERROR);
+ }
+}
diff --git a/lib/Zend/Mime/Exception.php b/lib/Zend/Mime/Exception.php
new file mode 100644
index 0000000..2c22e9b
--- /dev/null
+++ b/lib/Zend/Mime/Exception.php
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mime
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+
+/**
+ * Zend_Exception
+ */
+require_once 'Zend/Exception.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Mime
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Mime_Exception extends Zend_Exception
+{}
+
diff --git a/lib/Zend/Mime/Message.php b/lib/Zend/Mime/Message.php
new file mode 100644
index 0000000..613be1d
--- /dev/null
+++ b/lib/Zend/Mime/Message.php
@@ -0,0 +1,285 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mime
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+
+/**
+ * Zend_Mime
+ */
+require_once 'Zend/Mime.php';
+
+/**
+ * Zend_Mime_Part
+ */
+require_once 'Zend/Mime/Part.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Mime
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Mime_Message
+{
+
+ protected $_parts = array();
+ protected $_mime = null;
+
+ /**
+ * Returns the list of all Zend_Mime_Parts in the message
+ *
+ * @return array of Zend_Mime_Part
+ */
+ public function getParts()
+ {
+ return $this->_parts;
+ }
+
+ /**
+ * Sets the given array of Zend_Mime_Parts as the array for the message
+ *
+ * @param array $parts
+ */
+ public function setParts($parts)
+ {
+ $this->_parts = $parts;
+ }
+
+ /**
+ * Append a new Zend_Mime_Part to the current message
+ *
+ * @param Zend_Mime_Part $part
+ */
+ public function addPart(Zend_Mime_Part $part)
+ {
+ /**
+ * @todo check for duplicate object handle
+ */
+ $this->_parts[] = $part;
+ }
+
+ /**
+ * Check if message needs to be sent as multipart
+ * MIME message or if it has only one part.
+ *
+ * @return boolean
+ */
+ public function isMultiPart()
+ {
+ return (count($this->_parts) > 1);
+ }
+
+ /**
+ * Set Zend_Mime object for the message
+ *
+ * This can be used to set the boundary specifically or to use a subclass of
+ * Zend_Mime for generating the boundary.
+ *
+ * @param Zend_Mime $mime
+ */
+ public function setMime(Zend_Mime $mime)
+ {
+ $this->_mime = $mime;
+ }
+
+ /**
+ * Returns the Zend_Mime object in use by the message
+ *
+ * If the object was not present, it is created and returned. Can be used to
+ * determine the boundary used in this message.
+ *
+ * @return Zend_Mime
+ */
+ public function getMime()
+ {
+ if ($this->_mime === null) {
+ $this->_mime = new Zend_Mime();
+ }
+
+ return $this->_mime;
+ }
+
+ /**
+ * Generate MIME-compliant message from the current configuration
+ *
+ * This can be a multipart message if more than one MIME part was added. If
+ * only one part is present, the content of this part is returned. If no
+ * part had been added, an empty string is returned.
+ *
+ * Parts are seperated by the mime boundary as defined in Zend_Mime. If
+ * {@link setMime()} has been called before this method, the Zend_Mime
+ * object set by this call will be used. Otherwise, a new Zend_Mime object
+ * is generated and used.
+ *
+ * @param string $EOL EOL string; defaults to {@link Zend_Mime::LINEEND}
+ * @return string
+ */
+ public function generateMessage($EOL = Zend_Mime::LINEEND)
+ {
+ if (! $this->isMultiPart()) {
+ $body = array_shift($this->_parts);
+ $body = $body->getContent($EOL);
+ } else {
+ $mime = $this->getMime();
+
+ $boundaryLine = $mime->boundaryLine($EOL);
+ $body = 'This is a message in Mime Format. If you see this, '
+ . "your mail reader does not support this format." . $EOL;
+
+ foreach (array_keys($this->_parts) as $p) {
+ $body .= $boundaryLine
+ . $this->getPartHeaders($p, $EOL)
+ . $EOL
+ . $this->getPartContent($p, $EOL);
+ }
+
+ $body .= $mime->mimeEnd($EOL);
+ }
+
+ return trim($body);
+ }
+
+ /**
+ * Get the headers of a given part as an array
+ *
+ * @param int $partnum
+ * @return array
+ */
+ public function getPartHeadersArray($partnum)
+ {
+ return $this->_parts[$partnum]->getHeadersArray();
+ }
+
+ /**
+ * Get the headers of a given part as a string
+ *
+ * @param int $partnum
+ * @return string
+ */
+ public function getPartHeaders($partnum, $EOL = Zend_Mime::LINEEND)
+ {
+ return $this->_parts[$partnum]->getHeaders($EOL);
+ }
+
+ /**
+ * Get the (encoded) content of a given part as a string
+ *
+ * @param int $partnum
+ * @return string
+ */
+ public function getPartContent($partnum, $EOL = Zend_Mime::LINEEND)
+ {
+ return $this->_parts[$partnum]->getContent($EOL);
+ }
+
+ /**
+ * Explode MIME multipart string into seperate parts
+ *
+ * Parts consist of the header and the body of each MIME part.
+ *
+ * @param string $body
+ * @param string $boundary
+ * @return array
+ */
+ protected static function _disassembleMime($body, $boundary)
+ {
+ $start = 0;
+ $res = array();
+ // find every mime part limiter and cut out the
+ // string before it.
+ // the part before the first boundary string is discarded:
+ $p = strpos($body, '--'.$boundary."\n", $start);
+ if ($p === false) {
+ // no parts found!
+ return array();
+ }
+
+ // position after first boundary line
+ $start = $p + 3 + strlen($boundary);
+
+ while (($p = strpos($body, '--' . $boundary . "\n", $start)) !== false) {
+ $res[] = substr($body, $start, $p-$start);
+ $start = $p + 3 + strlen($boundary);
+ }
+
+ // no more parts, find end boundary
+ $p = strpos($body, '--' . $boundary . '--', $start);
+ if ($p===false) {
+ throw new Zend_Exception('Not a valid Mime Message: End Missing');
+ }
+
+ // the remaining part also needs to be parsed:
+ $res[] = substr($body, $start, $p-$start);
+ return $res;
+ }
+
+ /**
+ * Decodes a MIME encoded string and returns a Zend_Mime_Message object with
+ * all the MIME parts set according to the given string
+ *
+ * @param string $message
+ * @param string $boundary
+ * @param string $EOL EOL string; defaults to {@link Zend_Mime::LINEEND}
+ * @return Zend_Mime_Message
+ */
+ public static function createFromMessage($message, $boundary, $EOL = Zend_Mime::LINEEND)
+ {
+ require_once 'Zend/Mime/Decode.php';
+ $parts = Zend_Mime_Decode::splitMessageStruct($message, $boundary, $EOL);
+
+ $res = new self();
+ foreach ($parts as $part) {
+ // now we build a new MimePart for the current Message Part:
+ $newPart = new Zend_Mime_Part($part['body']);
+ foreach ($part['header'] as $key => $value) {
+ /**
+ * @todo check for characterset and filename
+ */
+ switch(strtolower($key)) {
+ case 'content-type':
+ $newPart->type = $value;
+ break;
+ case 'content-transfer-encoding':
+ $newPart->encoding = $value;
+ break;
+ case 'content-id':
+ $newPart->id = trim($value,'<>');
+ break;
+ case 'content-disposition':
+ $newPart->disposition = $value;
+ break;
+ case 'content-description':
+ $newPart->description = $value;
+ break;
+ case 'content-location':
+ $newPart->location = $value;
+ break;
+ case 'content-language':
+ $newPart->language = $value;
+ break;
+ default:
+ throw new Zend_Exception('Unknown header ignored for MimePart:' . $key);
+ }
+ }
+ $res->addPart($newPart);
+ }
+ return $res;
+ }
+}
diff --git a/lib/Zend/Mime/Part.php b/lib/Zend/Mime/Part.php
new file mode 100644
index 0000000..a3e261b
--- /dev/null
+++ b/lib/Zend/Mime/Part.php
@@ -0,0 +1,216 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Mime
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+/**
+ * Zend_Mime
+ */
+require_once 'Zend/Mime.php';
+
+/**
+ * Class representing a MIME part.
+ *
+ * @category Zend
+ * @package Zend_Mime
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Mime_Part {
+
+ public $type = Zend_Mime::TYPE_OCTETSTREAM;
+ public $encoding = Zend_Mime::ENCODING_8BIT;
+ public $id;
+ public $disposition;
+ public $filename;
+ public $description;
+ public $charset;
+ public $boundary;
+ public $location;
+ public $language;
+ protected $_content;
+ protected $_isStream = false;
+
+
+ /**
+ * create a new Mime Part.
+ * The (unencoded) content of the Part as passed
+ * as a string or stream
+ *
+ * @param mixed $content String or Stream containing the content
+ */
+ public function __construct($content)
+ {
+ $this->_content = $content;
+ if (is_resource($content)) {
+ $this->_isStream = true;
+ }
+ }
+
+ /**
+ * @todo setters/getters
+ * @todo error checking for setting $type
+ * @todo error checking for setting $encoding
+ */
+
+ /**
+ * check if this part can be read as a stream.
+ * if true, getEncodedStream can be called, otherwise
+ * only getContent can be used to fetch the encoded
+ * content of the part
+ *
+ * @return bool
+ */
+ public function isStream()
+ {
+ return $this->_isStream;
+ }
+
+ /**
+ * if this was created with a stream, return a filtered stream for
+ * reading the content. very useful for large file attachments.
+ *
+ * @return stream
+ * @throws Zend_Mime_Exception if not a stream or unable to append filter
+ */
+ public function getEncodedStream()
+ {
+ if (!$this->_isStream) {
+ require_once 'Zend/Mime/Exception.php';
+ throw new Zend_Mime_Exception('Attempt to get a stream from a string part');
+ }
+
+ //stream_filter_remove(); // ??? is that right?
+ switch ($this->encoding) {
+ case Zend_Mime::ENCODING_QUOTEDPRINTABLE:
+ $filter = stream_filter_append(
+ $this->_content,
+ 'convert.quoted-printable-encode',
+ STREAM_FILTER_READ,
+ array(
+ 'line-length' => 76,
+ 'line-break-chars' => Zend_Mime::LINEEND
+ )
+ );
+ if (!is_resource($filter)) {
+ require_once 'Zend/Mime/Exception.php';
+ throw new Zend_Mime_Exception('Failed to append quoted-printable filter');
+ }
+ break;
+ case Zend_Mime::ENCODING_BASE64:
+ $filter = stream_filter_append(
+ $this->_content,
+ 'convert.base64-encode',
+ STREAM_FILTER_READ,
+ array(
+ 'line-length' => 76,
+ 'line-break-chars' => Zend_Mime::LINEEND
+ )
+ );
+ if (!is_resource($filter)) {
+ require_once 'Zend/Mime/Exception.php';
+ throw new Zend_Mime_Exception('Failed to append base64 filter');
+ }
+ break;
+ default:
+ }
+ return $this->_content;
+ }
+
+ /**
+ * Get the Content of the current Mime Part in the given encoding.
+ *
+ * @return String
+ */
+ public function getContent($EOL = Zend_Mime::LINEEND)
+ {
+ if ($this->_isStream) {
+ return stream_get_contents($this->getEncodedStream());
+ } else {
+ return Zend_Mime::encode($this->_content, $this->encoding, $EOL);
+ }
+ }
+
+ /**
+ * Create and return the array of headers for this MIME part
+ *
+ * @access public
+ * @return array
+ */
+ public function getHeadersArray($EOL = Zend_Mime::LINEEND)
+ {
+ $headers = array();
+
+ $contentType = $this->type;
+ if ($this->charset) {
+ $contentType .= '; charset="' . $this->charset . '"';
+ }
+
+ if ($this->boundary) {
+ $contentType .= ';' . $EOL
+ . " boundary=\"" . $this->boundary . '"';
+ }
+
+ $headers[] = array('Content-Type', $contentType);
+
+ if ($this->encoding) {
+ $headers[] = array('Content-Transfer-Encoding', $this->encoding);
+ }
+
+ if ($this->id) {
+ $headers[] = array('Content-ID', '<' . $this->id . '>');
+ }
+
+ if ($this->disposition) {
+ $disposition = $this->disposition;
+ if ($this->filename) {
+ $disposition .= '; filename="' . $this->filename . '"';
+ }
+ $headers[] = array('Content-Disposition', $disposition);
+ }
+
+ if ($this->description) {
+ $headers[] = array('Content-Description', $this->description);
+ }
+
+ if ($this->location) {
+ $headers[] = array('Content-Location', $this->location);
+ }
+
+ if ($this->language){
+ $headers[] = array('Content-Language', $this->language);
+ }
+
+ return $headers;
+ }
+
+ /**
+ * Return the headers for this part as a string
+ *
+ * @return String
+ */
+ public function getHeaders($EOL = Zend_Mime::LINEEND)
+ {
+ $res = '';
+ foreach ($this->getHeadersArray($EOL) as $header) {
+ $res .= $header[0] . ': ' . $header[1] . $EOL;
+ }
+
+ return $res;
+ }
+}
diff --git a/lib/Zend/Registry.php b/lib/Zend/Registry.php
new file mode 100644
index 0000000..2bc8814
--- /dev/null
+++ b/lib/Zend/Registry.php
@@ -0,0 +1,207 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Registry
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Registry.php 12065 2008-10-21 20:56:32Z doctorrock83 $
+ */
+
+/**
+ * Generic storage class helps to manage global data.
+ *
+ * @category Zend
+ * @package Zend_Registry
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Registry extends ArrayObject
+{
+ /**
+ * Class name of the singleton registry object.
+ * @var string
+ */
+ private static $_registryClassName = 'Zend_Registry';
+
+ /**
+ * Registry object provides storage for shared objects.
+ * @var Zend_Registry
+ */
+ private static $_registry = null;
+
+ /**
+ * Retrieves the default registry instance.
+ *
+ * @return Zend_Registry
+ */
+ public static function getInstance()
+ {
+ if (self::$_registry === null) {
+ self::init();
+ }
+
+ return self::$_registry;
+ }
+
+ /**
+ * Set the default registry instance to a specified instance.
+ *
+ * @param Zend_Registry $registry An object instance of type Zend_Registry,
+ * or a subclass.
+ * @return void
+ * @throws Zend_Exception if registry is already initialized.
+ */
+ public static function setInstance(Zend_Registry $registry)
+ {
+ if (self::$_registry !== null) {
+ require_once 'Zend/Exception.php';
+ throw new Zend_Exception('Registry is already initialized');
+ }
+
+ self::setClassName(get_class($registry));
+ self::$_registry = $registry;
+ }
+
+ /**
+ * Initialize the default registry instance.
+ *
+ * @return void
+ */
+ protected static function init()
+ {
+ self::setInstance(new self::$_registryClassName());
+ }
+
+ /**
+ * Set the class name to use for the default registry instance.
+ * Does not affect the currently initialized instance, it only applies
+ * for the next time you instantiate.
+ *
+ * @param string $registryClassName
+ * @return void
+ * @throws Zend_Exception if the registry is initialized or if the
+ * class name is not valid.
+ */
+ public static function setClassName($registryClassName = 'Zend_Registry')
+ {
+ if (self::$_registry !== null) {
+ require_once 'Zend/Exception.php';
+ throw new Zend_Exception('Registry is already initialized');
+ }
+
+ if (!is_string($registryClassName)) {
+ require_once 'Zend/Exception.php';
+ throw new Zend_Exception("Argument is not a class name");
+ }
+
+ /**
+ * @see Zend_Loader
+ */
+ require_once 'Zend/Loader.php';
+ Zend_Loader::loadClass($registryClassName);
+
+ self::$_registryClassName = $registryClassName;
+ }
+
+ /**
+ * Unset the default registry instance.
+ * Primarily used in tearDown() in unit tests.
+ * @returns void
+ */
+ public static function _unsetInstance()
+ {
+ self::$_registry = null;
+ }
+
+ /**
+ * getter method, basically same as offsetGet().
+ *
+ * This method can be called from an object of type Zend_Registry, or it
+ * can be called statically. In the latter case, it uses the default
+ * static instance stored in the class.
+ *
+ * @param string $index - get the value associated with $index
+ * @return mixed
+ * @throws Zend_Exception if no entry is registerd for $index.
+ */
+ public static function get($index)
+ {
+ $instance = self::getInstance();
+
+ if (!$instance->offsetExists($index)) {
+ require_once 'Zend/Exception.php';
+ throw new Zend_Exception("No entry is registered for key '$index'");
+ }
+
+ return $instance->offsetGet($index);
+ }
+
+ /**
+ * setter method, basically same as offsetSet().
+ *
+ * This method can be called from an object of type Zend_Registry, or it
+ * can be called statically. In the latter case, it uses the default
+ * static instance stored in the class.
+ *
+ * @param string $index The location in the ArrayObject in which to store
+ * the value.
+ * @param mixed $value The object to store in the ArrayObject.
+ * @return void
+ */
+ public static function set($index, $value)
+ {
+ $instance = self::getInstance();
+ $instance->offsetSet($index, $value);
+ }
+
+ /**
+ * Returns TRUE if the $index is a named value in the registry,
+ * or FALSE if $index was not found in the registry.
+ *
+ * @param string $index
+ * @return boolean
+ */
+ public static function isRegistered($index)
+ {
+ if (self::$_registry === null) {
+ return false;
+ }
+ return self::$_registry->offsetExists($index);
+ }
+
+ /**
+ * Constructs a parent ArrayObject with default
+ * ARRAY_AS_PROPS to allow acces as an object
+ *
+ * @param array $array data array
+ * @param integer $flags ArrayObject flags
+ */
+ public function __construct($array = array(), $flags = parent::ARRAY_AS_PROPS)
+ {
+ parent::__construct($array, $flags);
+ }
+
+ /**
+ * @param string $index
+ * @returns mixed
+ *
+ * Workaround for http://bugs.php.net/bug.php?id=40442 (ZF-960).
+ */
+ public function offsetExists($index)
+ {
+ return array_key_exists($index, $this);
+ }
+
+}
diff --git a/lib/Zend/Validate.php b/lib/Zend/Validate.php
new file mode 100644
index 0000000..b57930a
--- /dev/null
+++ b/lib/Zend/Validate.php
@@ -0,0 +1,171 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Validate.php 8729 2008-03-10 11:44:10Z thomas $
+ */
+
+
+/**
+ * @see Zend_Validate_Interface
+ */
+require_once 'Zend/Validate/Interface.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate implements Zend_Validate_Interface
+{
+ /**
+ * Validator chain
+ *
+ * @var array
+ */
+ protected $_validators = array();
+
+ /**
+ * Array of validation failure messages
+ *
+ * @var array
+ */
+ protected $_messages = array();
+
+ /**
+ * Array of validation failure message codes
+ *
+ * @var array
+ * @deprecated Since 1.5.0
+ */
+ protected $_errors = array();
+
+ /**
+ * Adds a validator to the end of the chain
+ *
+ * If $breakChainOnFailure is true, then if the validator fails, the next validator in the chain,
+ * if one exists, will not be executed.
+ *
+ * @param Zend_Validate_Interface $validator
+ * @param boolean $breakChainOnFailure
+ * @return Zend_Validate Provides a fluent interface
+ */
+ public function addValidator(Zend_Validate_Interface $validator, $breakChainOnFailure = false)
+ {
+ $this->_validators[] = array(
+ 'instance' => $validator,
+ 'breakChainOnFailure' => (boolean) $breakChainOnFailure
+ );
+ return $this;
+ }
+
+ /**
+ * Returns true if and only if $value passes all validations in the chain
+ *
+ * Validators are run in the order in which they were added to the chain (FIFO).
+ *
+ * @param mixed $value
+ * @return boolean
+ */
+ public function isValid($value)
+ {
+ $this->_messages = array();
+ $this->_errors = array();
+ $result = true;
+ foreach ($this->_validators as $element) {
+ $validator = $element['instance'];
+ if ($validator->isValid($value)) {
+ continue;
+ }
+ $result = false;
+ $messages = $validator->getMessages();
+ $this->_messages = array_merge($this->_messages, $messages);
+ $this->_errors = array_merge($this->_errors, array_keys($messages));
+ if ($element['breakChainOnFailure']) {
+ break;
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns array of validation failure messages
+ *
+ * @return array
+ */
+ public function getMessages()
+ {
+ return $this->_messages;
+ }
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns array of validation failure message codes
+ *
+ * @return array
+ * @deprecated Since 1.5.0
+ */
+ public function getErrors()
+ {
+ return $this->_errors;
+ }
+
+ /**
+ * @param mixed $value
+ * @param string $classBaseName
+ * @param array $args OPTIONAL
+ * @param mixed $namespaces OPTIONAL
+ * @return boolean
+ * @throws Zend_Validate_Exception
+ */
+ public static function is($value, $classBaseName, array $args = array(), $namespaces = array())
+ {
+ $namespaces = array_merge(array('Zend_Validate'), (array) $namespaces);
+ foreach ($namespaces as $namespace) {
+ $className = $namespace . '_' . ucfirst($classBaseName);
+ try {
+ require_once 'Zend/Loader.php';
+ @Zend_Loader::loadClass($className);
+ if (class_exists($className, false)) {
+ $class = new ReflectionClass($className);
+ if ($class->implementsInterface('Zend_Validate_Interface')) {
+ if ($class->hasMethod('__construct')) {
+ $object = $class->newInstanceArgs($args);
+ } else {
+ $object = $class->newInstance();
+ }
+ return $object->isValid($value);
+ }
+ }
+ } catch (Zend_Validate_Exception $ze) {
+ // if there is an exception while validating throw it
+ throw $ze;
+ } catch (Zend_Exception $ze) {
+ // fallthrough and continue for missing validation classes
+ }
+ }
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception("Validate class not found from basename '$classBaseName'");
+ }
+
+}
diff --git a/lib/Zend/Validate/Abstract.php b/lib/Zend/Validate/Abstract.php
new file mode 100644
index 0000000..66566a9
--- /dev/null
+++ b/lib/Zend/Validate/Abstract.php
@@ -0,0 +1,358 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Abstract.php 13351 2008-12-18 15:26:14Z alexander $
+ */
+
+
+/**
+ * @see Zend_Validate_Interface
+ */
+require_once 'Zend/Validate/Interface.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+abstract class Zend_Validate_Abstract implements Zend_Validate_Interface
+{
+ /**
+ * The value to be validated
+ *
+ * @var mixed
+ */
+ protected $_value;
+
+ /**
+ * Additional variables available for validation failure messages
+ *
+ * @var array
+ */
+ protected $_messageVariables = array();
+
+ /**
+ * Validation failure message template definitions
+ *
+ * @var array
+ */
+ protected $_messageTemplates = array();
+
+ /**
+ * Array of validation failure messages
+ *
+ * @var array
+ */
+ protected $_messages = array();
+
+ /**
+ * Flag indidcating whether or not value should be obfuscated in error
+ * messages
+ * @var bool
+ */
+ protected $_obscureValue = false;
+
+ /**
+ * Array of validation failure message codes
+ *
+ * @var array
+ * @deprecated Since 1.5.0
+ */
+ protected $_errors = array();
+
+ /**
+ * Translation object
+ * @var Zend_Translate
+ */
+ protected $_translator;
+
+ /**
+ * Default translation object for all validate objects
+ * @var Zend_Translate
+ */
+ protected static $_defaultTranslator;
+
+ /**
+ * Returns array of validation failure messages
+ *
+ * @return array
+ */
+ public function getMessages()
+ {
+ return $this->_messages;
+ }
+
+ /**
+ * Returns an array of the names of variables that are used in constructing validation failure messages
+ *
+ * @return array
+ */
+ public function getMessageVariables()
+ {
+ return array_keys($this->_messageVariables);
+ }
+
+ /**
+ * Sets the validation failure message template for a particular key
+ *
+ * @param string $messageString
+ * @param string $messageKey OPTIONAL
+ * @return Zend_Validate_Abstract Provides a fluent interface
+ * @throws Zend_Validate_Exception
+ */
+ public function setMessage($messageString, $messageKey = null)
+ {
+ if ($messageKey === null) {
+ $keys = array_keys($this->_messageTemplates);
+ $messageKey = current($keys);
+ }
+ if (!isset($this->_messageTemplates[$messageKey])) {
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception("No message template exists for key '$messageKey'");
+ }
+ $this->_messageTemplates[$messageKey] = $messageString;
+ return $this;
+ }
+
+ /**
+ * Sets validation failure message templates given as an array, where the array keys are the message keys,
+ * and the array values are the message template strings.
+ *
+ * @param array $messages
+ * @return Zend_Validate_Abstract
+ */
+ public function setMessages(array $messages)
+ {
+ foreach ($messages as $key => $message) {
+ $this->setMessage($message, $key);
+ }
+ return $this;
+ }
+
+ /**
+ * Magic function returns the value of the requested property, if and only if it is the value or a
+ * message variable.
+ *
+ * @param string $property
+ * @return mixed
+ * @throws Zend_Validate_Exception
+ */
+ public function __get($property)
+ {
+ if ($property == 'value') {
+ return $this->_value;
+ }
+ if (array_key_exists($property, $this->_messageVariables)) {
+ return $this->{$this->_messageVariables[$property]};
+ }
+ /**
+ * @see Zend_Validate_Exception
+ */
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception("No property exists by the name '$property'");
+ }
+
+ /**
+ * Constructs and returns a validation failure message with the given message key and value.
+ *
+ * Returns null if and only if $messageKey does not correspond to an existing template.
+ *
+ * If a translator is available and a translation exists for $messageKey,
+ * the translation will be used.
+ *
+ * @param string $messageKey
+ * @param string $value
+ * @return string
+ */
+ protected function _createMessage($messageKey, $value)
+ {
+ if (!isset($this->_messageTemplates[$messageKey])) {
+ return null;
+ }
+
+ $message = $this->_messageTemplates[$messageKey];
+
+ if (null !== ($translator = $this->getTranslator())) {
+ if ($translator->isTranslated($message)) {
+ $message = $translator->translate($message);
+ } elseif ($translator->isTranslated($messageKey)) {
+ $message = $translator->translate($messageKey);
+ }
+ }
+
+ if (is_object($value)) {
+ if (!in_array('__toString', get_class_methods($value))) {
+ $value = get_class($value) . ' object';
+ } else {
+ $value = $value->__toString();
+ }
+ } else {
+ $value = (string)$value;
+ }
+
+ if ($this->getObscureValue()) {
+ $value = str_repeat('*', strlen($value));
+ }
+
+ $message = str_replace('%value%', (string) $value, $message);
+ foreach ($this->_messageVariables as $ident => $property) {
+ $message = str_replace("%$ident%", (string) $this->$property, $message);
+ }
+ return $message;
+ }
+
+ /**
+ * @param string $messageKey OPTIONAL
+ * @param string $value OPTIONAL
+ * @return void
+ */
+ protected function _error($messageKey = null, $value = null)
+ {
+ if ($messageKey === null) {
+ $keys = array_keys($this->_messageTemplates);
+ $messageKey = current($keys);
+ }
+ if ($value === null) {
+ $value = $this->_value;
+ }
+ $this->_errors[] = $messageKey;
+ $this->_messages[$messageKey] = $this->_createMessage($messageKey, $value);
+ }
+
+ /**
+ * Sets the value to be validated and clears the messages and errors arrays
+ *
+ * @param mixed $value
+ * @return void
+ */
+ protected function _setValue($value)
+ {
+ $this->_value = $value;
+ $this->_messages = array();
+ $this->_errors = array();
+ }
+
+ /**
+ * Returns array of validation failure message codes
+ *
+ * @return array
+ * @deprecated Since 1.5.0
+ */
+ public function getErrors()
+ {
+ return $this->_errors;
+ }
+
+ /**
+ * Set flag indicating whether or not value should be obfuscated in messages
+ *
+ * @param bool $flag
+ * @return Zend_Validate_Abstract
+ */
+ public function setObscureValue($flag)
+ {
+ $this->_obscureValue = (bool) $flag;
+ return $this;
+ }
+
+ /**
+ * Retrieve flag indicating whether or not value should be obfuscated in
+ * messages
+ *
+ * @return bool
+ */
+ public function getObscureValue()
+ {
+ return $this->_obscureValue;
+ }
+
+ /**
+ * Set translation object
+ *
+ * @param Zend_Translate|Zend_Translate_Adapter|null $translator
+ * @return Zend_Validate_Abstract
+ */
+ public function setTranslator($translator = null)
+ {
+ if ((null === $translator) || ($translator instanceof Zend_Translate_Adapter)) {
+ $this->_translator = $translator;
+ } elseif ($translator instanceof Zend_Translate) {
+ $this->_translator = $translator->getAdapter();
+ } else {
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception('Invalid translator specified');
+ }
+ return $this;
+ }
+
+ /**
+ * Return translation object
+ *
+ * @return Zend_Translate_Adapter|null
+ */
+ public function getTranslator()
+ {
+ if (null === $this->_translator) {
+ return self::getDefaultTranslator();
+ }
+
+ return $this->_translator;
+ }
+
+ /**
+ * Set default translation object for all validate objects
+ *
+ * @param Zend_Translate|Zend_Translate_Adapter|null $translator
+ * @return void
+ */
+ public static function setDefaultTranslator($translator = null)
+ {
+ if ((null === $translator) || ($translator instanceof Zend_Translate_Adapter)) {
+ self::$_defaultTranslator = $translator;
+ } elseif ($translator instanceof Zend_Translate) {
+ self::$_defaultTranslator = $translator->getAdapter();
+ } else {
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception('Invalid translator specified');
+ }
+ }
+
+ /**
+ * Get default translation object for all validate objects
+ *
+ * @return Zend_Translate_Adapter|null
+ */
+ public static function getDefaultTranslator()
+ {
+ if (null === self::$_defaultTranslator) {
+ require_once 'Zend/Registry.php';
+ if (Zend_Registry::isRegistered('Zend_Translate')) {
+ $translator = Zend_Registry::get('Zend_Translate');
+ if ($translator instanceof Zend_Translate_Adapter) {
+ return $translator;
+ } elseif ($translator instanceof Zend_Translate) {
+ return $translator->getAdapter();
+ }
+ }
+ }
+ return self::$_defaultTranslator;
+ }
+}
diff --git a/lib/Zend/Validate/Alnum.php b/lib/Zend/Validate/Alnum.php
new file mode 100644
index 0000000..36b0adc
--- /dev/null
+++ b/lib/Zend/Validate/Alnum.php
@@ -0,0 +1,120 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Alnum.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_Alnum extends Zend_Validate_Abstract
+{
+ /**
+ * Validation failure message key for when the value contains non-alphabetic or non-digit characters
+ */
+ const NOT_ALNUM = 'notAlnum';
+
+ /**
+ * Validation failure message key for when the value is an empty string
+ */
+ const STRING_EMPTY = 'stringEmpty';
+
+ /**
+ * Whether to allow white space characters; off by default
+ *
+ * @var boolean
+ */
+ public $allowWhiteSpace;
+
+ /**
+ * Alphanumeric filter used for validation
+ *
+ * @var Zend_Filter_Alnum
+ */
+ protected static $_filter = null;
+
+ /**
+ * Validation failure message template definitions
+ *
+ * @var array
+ */
+ protected $_messageTemplates = array(
+ self::NOT_ALNUM => "'%value%' has not only alphabetic and digit characters",
+ self::STRING_EMPTY => "'%value%' is an empty string"
+ );
+
+ /**
+ * Sets default option values for this instance
+ *
+ * @param boolean $allowWhiteSpace
+ * @return void
+ */
+ public function __construct($allowWhiteSpace = false)
+ {
+ $this->allowWhiteSpace = (boolean) $allowWhiteSpace;
+ }
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if and only if $value contains only alphabetic and digit characters
+ *
+ * @param string $value
+ * @return boolean
+ */
+ public function isValid($value)
+ {
+ $valueString = (string) $value;
+
+ $this->_setValue($valueString);
+
+ if ('' === $valueString) {
+ $this->_error(self::STRING_EMPTY);
+ return false;
+ }
+
+ if (null === self::$_filter) {
+ /**
+ * @see Zend_Filter_Alnum
+ */
+ require_once 'Zend/Filter/Alnum.php';
+ self::$_filter = new Zend_Filter_Alnum();
+ }
+
+ self::$_filter->allowWhiteSpace = $this->allowWhiteSpace;
+
+ if ($valueString !== self::$_filter->filter($valueString)) {
+ $this->_error(self::NOT_ALNUM);
+ return false;
+ }
+
+ return true;
+ }
+
+}
diff --git a/lib/Zend/Validate/Alpha.php b/lib/Zend/Validate/Alpha.php
new file mode 100644
index 0000000..0f2298e
--- /dev/null
+++ b/lib/Zend/Validate/Alpha.php
@@ -0,0 +1,120 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Alpha.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_Alpha extends Zend_Validate_Abstract
+{
+ /**
+ * Validation failure message key for when the value contains non-alphabetic characters
+ */
+ const NOT_ALPHA = 'notAlpha';
+
+ /**
+ * Validation failure message key for when the value is an empty string
+ */
+ const STRING_EMPTY = 'stringEmpty';
+
+ /**
+ * Whether to allow white space characters; off by default
+ *
+ * @var boolean
+ */
+ public $allowWhiteSpace;
+
+ /**
+ * Alphabetic filter used for validation
+ *
+ * @var Zend_Filter_Alpha
+ */
+ protected static $_filter = null;
+
+ /**
+ * Validation failure message template definitions
+ *
+ * @var array
+ */
+ protected $_messageTemplates = array(
+ self::NOT_ALPHA => "'%value%' has not only alphabetic characters",
+ self::STRING_EMPTY => "'%value%' is an empty string"
+ );
+
+ /**
+ * Sets default option values for this instance
+ *
+ * @param boolean $allowWhiteSpace
+ * @return void
+ */
+ public function __construct($allowWhiteSpace = false)
+ {
+ $this->allowWhiteSpace = (boolean) $allowWhiteSpace;
+ }
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if and only if $value contains only alphabetic characters
+ *
+ * @param string $value
+ * @return boolean
+ */
+ public function isValid($value)
+ {
+ $valueString = (string) $value;
+
+ $this->_setValue($valueString);
+
+ if ('' === $valueString) {
+ $this->_error(self::STRING_EMPTY);
+ return false;
+ }
+
+ if (null === self::$_filter) {
+ /**
+ * @see Zend_Filter_Alpha
+ */
+ require_once 'Zend/Filter/Alpha.php';
+ self::$_filter = new Zend_Filter_Alpha();
+ }
+
+ self::$_filter->allowWhiteSpace = $this->allowWhiteSpace;
+
+ if ($valueString !== self::$_filter->filter($valueString)) {
+ $this->_error(self::NOT_ALPHA);
+ return false;
+ }
+
+ return true;
+ }
+
+}
diff --git a/lib/Zend/Validate/Barcode.php b/lib/Zend/Validate/Barcode.php
new file mode 100644
index 0000000..d51f11b
--- /dev/null
+++ b/lib/Zend/Validate/Barcode.php
@@ -0,0 +1,99 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Barcode.php 8211 2008-02-20 14:29:24Z darby $
+ */
+
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_Barcode extends Zend_Validate_Abstract
+{
+ /**
+ * Barcode validator
+ *
+ * @var Zend_Validate_Abstract
+ */
+ protected $_barcodeValidator;
+
+ /**
+ * Generates the standard validator object
+ *
+ * @param string $barcodeType - Barcode validator to use
+ * @return void
+ * @throws Zend_Validate_Exception
+ */
+ public function __construct($barcodeType)
+ {
+ $this->setType($barcodeType);
+ }
+
+ /**
+ * Sets a new barcode validator
+ *
+ * @param string $barcodeType - Barcode validator to use
+ * @return void
+ * @throws Zend_Validate_Exception
+ */
+ public function setType($barcodeType)
+ {
+ switch (strtolower($barcodeType)) {
+ case 'upc':
+ case 'upc-a':
+ $className = 'UpcA';
+ break;
+ case 'ean13':
+ case 'ean-13':
+ $className = 'Ean13';
+ break;
+ default:
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception("Barcode type '$barcodeType' is not supported'");
+ break;
+ }
+
+ require_once 'Zend/Validate/Barcode/' . $className . '.php';
+
+ $class = 'Zend_Validate_Barcode_' . $className;
+ $this->_barcodeValidator = new $class;
+ }
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if and only if $value contains a valid barcode
+ *
+ * @param string $value
+ * @return boolean
+ */
+ public function isValid($value)
+ {
+ return call_user_func(array($this->_barcodeValidator, 'isValid'), $value);
+ }
+}
diff --git a/lib/Zend/Validate/Barcode/Ean13.php b/lib/Zend/Validate/Barcode/Ean13.php
new file mode 100644
index 0000000..393091d
--- /dev/null
+++ b/lib/Zend/Validate/Barcode/Ean13.php
@@ -0,0 +1,112 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Ean13.php 11791 2008-10-09 18:19:13Z andries $
+ */
+
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_Barcode_Ean13 extends Zend_Validate_Abstract
+{
+ /**
+ * Validation failure message key for when the value is
+ * an invalid barcode
+ */
+ const INVALID = 'invalid';
+
+ /**
+ * Validation failure message key for when the value is
+ * not 13 characters long
+ */
+ const INVALID_LENGTH = 'invalidLength';
+
+ /**
+ * Validation failure message key for when the value
+ * does not only contain numeric characters
+ */
+ const NOT_NUMERIC = 'ean13NotNumeric';
+
+ /**
+ * Validation failure message template definitions
+ *
+ * @var array
+ */
+ protected $_messageTemplates = array(
+ self::INVALID => "'%value%' is an invalid EAN-13 barcode",
+ self::INVALID_LENGTH => "'%value%' should be 13 characters",
+ self::NOT_NUMERIC => "'%value%' should contain only numeric characters",
+ );
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if and only if $value contains a valid barcode
+ *
+ * @param string $value
+ * @return boolean
+ */
+ public function isValid($value)
+ {
+ if (false === ctype_digit($value)) {
+ $this->_error(self::NOT_NUMERIC);
+ return false;
+ }
+
+ $valueString = (string) $value;
+ $this->_setValue($valueString);
+
+ if (strlen($valueString) !== 13) {
+ $this->_error(self::INVALID_LENGTH);
+ return false;
+ }
+
+ $barcode = strrev(substr($valueString, 0, -1));
+ $oddSum = 0;
+ $evenSum = 0;
+
+ for ($i = 0; $i < 12; $i++) {
+ if ($i % 2 === 0) {
+ $oddSum += $barcode[$i] * 3;
+ } elseif ($i % 2 === 1) {
+ $evenSum += $barcode[$i];
+ }
+ }
+
+ $calculation = ($oddSum + $evenSum) % 10;
+ $checksum = ($calculation === 0) ? 0 : 10 - $calculation;
+
+ if ($valueString[12] != $checksum) {
+ $this->_error(self::INVALID);
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/lib/Zend/Validate/Barcode/UpcA.php b/lib/Zend/Validate/Barcode/UpcA.php
new file mode 100644
index 0000000..c584e81
--- /dev/null
+++ b/lib/Zend/Validate/Barcode/UpcA.php
@@ -0,0 +1,100 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: UpcA.php 8210 2008-02-20 14:09:05Z andries $
+ */
+
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_Barcode_UpcA extends Zend_Validate_Abstract
+{
+ /**
+ * Validation failure message key for when the value is
+ * an invalid barcode
+ */
+ const INVALID = 'invalid';
+
+ /**
+ * Validation failure message key for when the value is
+ * not 12 characters long
+ */
+ const INVALID_LENGTH = 'invalidLength';
+
+ /**
+ * Validation failure message template definitions
+ *
+ * @var array
+ */
+ protected $_messageTemplates = array(
+ self::INVALID => "'%value%' is an invalid UPC-A barcode",
+ self::INVALID_LENGTH => "'%value%' should be 12 characters",
+ );
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if and only if $value contains a valid barcode
+ *
+ * @param string $value
+ * @return boolean
+ */
+ public function isValid($value)
+ {
+ $valueString = (string) $value;
+ $this->_setValue($valueString);
+
+ if (strlen($valueString) !== 12) {
+ $this->_error(self::INVALID_LENGTH);
+ return false;
+ }
+
+ $barcode = substr($valueString, 0, -1);
+ $oddSum = 0;
+ $evenSum = 0;
+
+ for ($i = 0; $i < 11; $i++) {
+ if ($i % 2 === 0) {
+ $oddSum += $barcode[$i] * 3;
+ } elseif ($i % 2 === 1) {
+ $evenSum += $barcode[$i];
+ }
+ }
+
+ $calculation = ($oddSum + $evenSum) % 10;
+ $checksum = ($calculation === 0) ? 0 : 10 - $calculation;
+
+ if ($valueString[11] != $checksum) {
+ $this->_error(self::INVALID);
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/lib/Zend/Validate/Between.php b/lib/Zend/Validate/Between.php
new file mode 100644
index 0000000..bb0b726
--- /dev/null
+++ b/lib/Zend/Validate/Between.php
@@ -0,0 +1,200 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Between.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_Between extends Zend_Validate_Abstract
+{
+ /**
+ * Validation failure message key for when the value is not between the min and max, inclusively
+ */
+ const NOT_BETWEEN = 'notBetween';
+
+ /**
+ * Validation failure message key for when the value is not strictly between the min and max
+ */
+ const NOT_BETWEEN_STRICT = 'notBetweenStrict';
+
+ /**
+ * Validation failure message template definitions
+ *
+ * @var array
+ */
+ protected $_messageTemplates = array(
+ self::NOT_BETWEEN => "'%value%' is not between '%min%' and '%max%', inclusively",
+ self::NOT_BETWEEN_STRICT => "'%value%' is not strictly between '%min%' and '%max%'"
+ );
+
+ /**
+ * Additional variables available for validation failure messages
+ *
+ * @var array
+ */
+ protected $_messageVariables = array(
+ 'min' => '_min',
+ 'max' => '_max'
+ );
+
+ /**
+ * Minimum value
+ *
+ * @var mixed
+ */
+ protected $_min;
+
+ /**
+ * Maximum value
+ *
+ * @var mixed
+ */
+ protected $_max;
+
+ /**
+ * Whether to do inclusive comparisons, allowing equivalence to min and/or max
+ *
+ * If false, then strict comparisons are done, and the value may equal neither
+ * the min nor max options
+ *
+ * @var boolean
+ */
+ protected $_inclusive;
+
+ /**
+ * Sets validator options
+ *
+ * @param mixed $min
+ * @param mixed $max
+ * @param boolean $inclusive
+ * @return void
+ */
+ public function __construct($min, $max, $inclusive = true)
+ {
+ $this->setMin($min)
+ ->setMax($max)
+ ->setInclusive($inclusive);
+ }
+
+ /**
+ * Returns the min option
+ *
+ * @return mixed
+ */
+ public function getMin()
+ {
+ return $this->_min;
+ }
+
+ /**
+ * Sets the min option
+ *
+ * @param mixed $min
+ * @return Zend_Validate_Between Provides a fluent interface
+ */
+ public function setMin($min)
+ {
+ $this->_min = $min;
+ return $this;
+ }
+
+ /**
+ * Returns the max option
+ *
+ * @return mixed
+ */
+ public function getMax()
+ {
+ return $this->_max;
+ }
+
+ /**
+ * Sets the max option
+ *
+ * @param mixed $max
+ * @return Zend_Validate_Between Provides a fluent interface
+ */
+ public function setMax($max)
+ {
+ $this->_max = $max;
+ return $this;
+ }
+
+ /**
+ * Returns the inclusive option
+ *
+ * @return boolean
+ */
+ public function getInclusive()
+ {
+ return $this->_inclusive;
+ }
+
+ /**
+ * Sets the inclusive option
+ *
+ * @param boolean $inclusive
+ * @return Zend_Validate_Between Provides a fluent interface
+ */
+ public function setInclusive($inclusive)
+ {
+ $this->_inclusive = $inclusive;
+ return $this;
+ }
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if and only if $value is between min and max options, inclusively
+ * if inclusive option is true.
+ *
+ * @param mixed $value
+ * @return boolean
+ */
+ public function isValid($value)
+ {
+ $this->_setValue($value);
+
+ if ($this->_inclusive) {
+ if ($this->_min > $value || $value > $this->_max) {
+ $this->_error(self::NOT_BETWEEN);
+ return false;
+ }
+ } else {
+ if ($this->_min >= $value || $value >= $this->_max) {
+ $this->_error(self::NOT_BETWEEN_STRICT);
+ return false;
+ }
+ }
+ return true;
+ }
+
+}
diff --git a/lib/Zend/Validate/Ccnum.php b/lib/Zend/Validate/Ccnum.php
new file mode 100644
index 0000000..227a4ec
--- /dev/null
+++ b/lib/Zend/Validate/Ccnum.php
@@ -0,0 +1,111 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Ccnum.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_Ccnum extends Zend_Validate_Abstract
+{
+ /**
+ * Validation failure message key for when the value is not of valid length
+ */
+ const LENGTH = 'ccnumLength';
+
+ /**
+ * Validation failure message key for when the value fails the mod-10 checksum
+ */
+ const CHECKSUM = 'ccnumChecksum';
+
+ /**
+ * Digits filter for input
+ *
+ * @var Zend_Filter_Digits
+ */
+ protected static $_filter = null;
+
+ /**
+ * Validation failure message template definitions
+ *
+ * @var array
+ */
+ protected $_messageTemplates = array(
+ self::LENGTH => "'%value%' must contain between 13 and 19 digits",
+ self::CHECKSUM => "Luhn algorithm (mod-10 checksum) failed on '%value%'"
+ );
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if and only if $value follows the Luhn algorithm (mod-10 checksum)
+ *
+ * @param string $value
+ * @return boolean
+ */
+ public function isValid($value)
+ {
+ $this->_setValue($value);
+
+ if (null === self::$_filter) {
+ /**
+ * @see Zend_Filter_Digits
+ */
+ require_once 'Zend/Filter/Digits.php';
+ self::$_filter = new Zend_Filter_Digits();
+ }
+
+ $valueFiltered = self::$_filter->filter($value);
+
+ $length = strlen($valueFiltered);
+
+ if ($length < 13 || $length > 19) {
+ $this->_error(self::LENGTH);
+ return false;
+ }
+
+ $sum = 0;
+ $weight = 2;
+
+ for ($i = $length - 2; $i >= 0; $i--) {
+ $digit = $weight * $valueFiltered[$i];
+ $sum += floor($digit / 10) + $digit % 10;
+ $weight = $weight % 2 + 1;
+ }
+
+ if ((10 - $sum % 10) % 10 != $valueFiltered[$length - 1]) {
+ $this->_error(self::CHECKSUM, $valueFiltered);
+ return false;
+ }
+
+ return true;
+ }
+
+}
diff --git a/lib/Zend/Validate/Date.php b/lib/Zend/Validate/Date.php
new file mode 100644
index 0000000..156c584
--- /dev/null
+++ b/lib/Zend/Validate/Date.php
@@ -0,0 +1,235 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Date.php 13687 2009-01-18 15:33:52Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_Date extends Zend_Validate_Abstract
+{
+ /**
+ * Validation failure message key for when the value does not follow the YYYY-MM-DD format
+ */
+ const NOT_YYYY_MM_DD = 'dateNotYYYY-MM-DD';
+
+ /**
+ * Validation failure message key for when the value does not appear to be a valid date
+ */
+ const INVALID = 'dateInvalid';
+
+ /**
+ * Validation failure message key for when the value does not fit the given dateformat or locale
+ */
+ const FALSEFORMAT = 'dateFalseFormat';
+
+ /**
+ * Validation failure message template definitions
+ *
+ * @var array
+ */
+ protected $_messageTemplates = array(
+ self::NOT_YYYY_MM_DD => "'%value%' is not of the format YYYY-MM-DD",
+ self::INVALID => "'%value%' does not appear to be a valid date",
+ self::FALSEFORMAT => "'%value%' does not fit given date format"
+ );
+
+ /**
+ * Optional format
+ *
+ * @var string|null
+ */
+ protected $_format;
+
+ /**
+ * Optional locale
+ *
+ * @var string|Zend_Locale|null
+ */
+ protected $_locale;
+
+ /**
+ * Sets validator options
+ *
+ * @param string $format OPTIONAL
+ * @param string|Zend_Locale $locale OPTIONAL
+ * @return void
+ */
+ public function __construct($format = null, $locale = null)
+ {
+ $this->setFormat($format);
+ if ($locale !== null) {
+ $this->setLocale($locale);
+ }
+ }
+
+ /**
+ * Returns the locale option
+ *
+ * @return string|Zend_Locale|null
+ */
+ public function getLocale()
+ {
+ return $this->_locale;
+ }
+
+ /**
+ * Sets the locale option
+ *
+ * @param string|Zend_Locale $locale
+ * @return Zend_Validate_Date provides a fluent interface
+ */
+ public function setLocale($locale = null)
+ {
+ require_once 'Zend/Locale.php';
+ $this->_locale = Zend_Locale::findLocale($locale);
+ return $this;
+ }
+
+ /**
+ * Returns the locale option
+ *
+ * @return string|null
+ */
+ public function getFormat()
+ {
+ return $this->_format;
+ }
+
+ /**
+ * Sets the format option
+ *
+ * @param string $format
+ * @return Zend_Validate_Date provides a fluent interface
+ */
+ public function setFormat($format = null)
+ {
+ $this->_format = $format;
+ return $this;
+ }
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if $value is a valid date of the format YYYY-MM-DD
+ * If optional $format or $locale is set the date format is checked
+ * according to Zend_Date, see Zend_Date::isDate()
+ *
+ * @param string $value
+ * @return boolean
+ */
+ public function isValid($value)
+ {
+ $valueString = (string) $value;
+
+ $this->_setValue($valueString);
+
+ if (($this->_format !== null) or ($this->_locale !== null)) {
+ require_once 'Zend/Date.php';
+ if (!Zend_Date::isDate($value, $this->_format, $this->_locale)) {
+ if ($this->_checkFormat($value) === false) {
+ $this->_error(self::FALSEFORMAT);
+ } else {
+ $this->_error(self::INVALID);
+ }
+ return false;
+ }
+ } else {
+ if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $valueString)) {
+ $this->_error(self::NOT_YYYY_MM_DD);
+ return false;
+ }
+
+ list($year, $month, $day) = sscanf($valueString, '%d-%d-%d');
+
+ if (!checkdate($month, $day, $year)) {
+ $this->_error(self::INVALID);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Check if the given date fits the given format
+ *
+ * @param string $value Date to check
+ * @return boolean False when date does not fit the format
+ */
+ private function _checkFormat($value)
+ {
+ try {
+ require_once 'Zend/Locale/Format.php';
+ $parsed = Zend_Locale_Format::getDate($value, array(
+ 'date_format' => $this->_format, 'format_type' => 'iso',
+ 'fix_date' => false));
+ if (isset($parsed['year']) and ((strpos(strtoupper($this->_format), 'YY') !== false) and
+ (strpos(strtoupper($this->_format), 'YYYY') === false))) {
+ $parsed['year'] = Zend_Date::getFullYear($parsed['year']);
+ }
+ } catch (Exception $e) {
+ // Date can not be parsed
+ return false;
+ }
+
+ if (((strpos($this->_format, 'Y') !== false) or (strpos($this->_format, 'y') !== false)) and
+ (!isset($parsed['year']))) {
+ // Year expected but not found
+ return false;
+ }
+
+ if ((strpos($this->_format, 'M') !== false) and (!isset($parsed['month']))) {
+ // Month expected but not found
+ return false;
+ }
+
+ if ((strpos($this->_format, 'd') !== false) and (!isset($parsed['day']))) {
+ // Day expected but not found
+ return false;
+ }
+
+ if (((strpos($this->_format, 'H') !== false) or (strpos($this->_format, 'h') !== false)) and
+ (!isset($parsed['hour']))) {
+ // Hour expected but not found
+ return false;
+ }
+
+ if ((strpos($this->_format, 'm') !== false) and (!isset($parsed['minute']))) {
+ // Minute expected but not found
+ return false;
+ }
+
+ if ((strpos($this->_format, 's') !== false) and (!isset($parsed['second']))) {
+ // Second expected but not found
+ return false;
+ }
+
+ // Date fits the format
+ return true;
+ }
+}
diff --git a/lib/Zend/Validate/Digits.php b/lib/Zend/Validate/Digits.php
new file mode 100644
index 0000000..c42ec0a
--- /dev/null
+++ b/lib/Zend/Validate/Digits.php
@@ -0,0 +1,100 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Digits.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_Digits extends Zend_Validate_Abstract
+{
+ /**
+ * Validation failure message key for when the value contains non-digit characters
+ */
+ const NOT_DIGITS = 'notDigits';
+
+ /**
+ * Validation failure message key for when the value is an empty string
+ */
+ const STRING_EMPTY = 'stringEmpty';
+
+ /**
+ * Digits filter used for validation
+ *
+ * @var Zend_Filter_Digits
+ */
+ protected static $_filter = null;
+
+ /**
+ * Validation failure message template definitions
+ *
+ * @var array
+ */
+ protected $_messageTemplates = array(
+ self::NOT_DIGITS => "'%value%' contains not only digit characters",
+ self::STRING_EMPTY => "'%value%' is an empty string"
+ );
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if and only if $value only contains digit characters
+ *
+ * @param string $value
+ * @return boolean
+ */
+ public function isValid($value)
+ {
+ $valueString = (string) $value;
+
+ $this->_setValue($valueString);
+
+ if ('' === $valueString) {
+ $this->_error(self::STRING_EMPTY);
+ return false;
+ }
+
+ if (null === self::$_filter) {
+ /**
+ * @see Zend_Filter_Digits
+ */
+ require_once 'Zend/Filter/Digits.php';
+ self::$_filter = new Zend_Filter_Digits();
+ }
+
+ if ($valueString !== self::$_filter->filter($valueString)) {
+ $this->_error(self::NOT_DIGITS);
+ return false;
+ }
+
+ return true;
+ }
+
+}
diff --git a/lib/Zend/Validate/EmailAddress.php b/lib/Zend/Validate/EmailAddress.php
new file mode 100644
index 0000000..82ed91b
--- /dev/null
+++ b/lib/Zend/Validate/EmailAddress.php
@@ -0,0 +1,247 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: EmailAddress.php 13253 2008-12-14 20:28:06Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * @see Zend_Validate_Hostname
+ */
+require_once 'Zend/Validate/Hostname.php';
+
+/**
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_EmailAddress extends Zend_Validate_Abstract
+{
+ const INVALID = 'emailAddressInvalid';
+ const INVALID_HOSTNAME = 'emailAddressInvalidHostname';
+ const INVALID_MX_RECORD = 'emailAddressInvalidMxRecord';
+ const DOT_ATOM = 'emailAddressDotAtom';
+ const QUOTED_STRING = 'emailAddressQuotedString';
+ const INVALID_LOCAL_PART = 'emailAddressInvalidLocalPart';
+ const LENGTH_EXCEEDED = 'emailAddressLengthExceeded';
+
+ /**
+ * @var array
+ */
+ protected $_messageTemplates = array(
+ self::INVALID => "'%value%' is not a valid email address in the basic format local-part@hostname",
+ self::INVALID_HOSTNAME => "'%hostname%' is not a valid hostname for email address '%value%'",
+ self::INVALID_MX_RECORD => "'%hostname%' does not appear to have a valid MX record for the email address '%value%'",
+ self::DOT_ATOM => "'%localPart%' not matched against dot-atom format",
+ self::QUOTED_STRING => "'%localPart%' not matched against quoted-string format",
+ self::INVALID_LOCAL_PART => "'%localPart%' is not a valid local part for email address '%value%'",
+ self::LENGTH_EXCEEDED => "'%value%' exceeds the allowed length"
+ );
+
+ /**
+ * @var array
+ */
+ protected $_messageVariables = array(
+ 'hostname' => '_hostname',
+ 'localPart' => '_localPart'
+ );
+
+ /**
+ * Local object for validating the hostname part of an email address
+ *
+ * @var Zend_Validate_Hostname
+ */
+ public $hostnameValidator;
+
+ /**
+ * Whether we check for a valid MX record via DNS
+ *
+ * @var boolean
+ */
+ protected $_validateMx = false;
+
+ /**
+ * @var string
+ */
+ protected $_hostname;
+
+ /**
+ * @var string
+ */
+ protected $_localPart;
+
+ /**
+ * Instantiates hostname validator for local use
+ *
+ * You can pass a bitfield to determine what types of hostnames are allowed.
+ * These bitfields are defined by the ALLOW_* constants in Zend_Validate_Hostname
+ * The default is to allow DNS hostnames only
+ *
+ * @param integer $allow OPTIONAL
+ * @param bool $validateMx OPTIONAL
+ * @param Zend_Validate_Hostname $hostnameValidator OPTIONAL
+ * @return void
+ */
+ public function __construct($allow = Zend_Validate_Hostname::ALLOW_DNS, $validateMx = false, Zend_Validate_Hostname $hostnameValidator = null)
+ {
+ $this->setValidateMx($validateMx);
+ $this->setHostnameValidator($hostnameValidator, $allow);
+ }
+
+ /**
+ * @param Zend_Validate_Hostname $hostnameValidator OPTIONAL
+ * @param int $allow OPTIONAL
+ * @return void
+ */
+ public function setHostnameValidator(Zend_Validate_Hostname $hostnameValidator = null, $allow = Zend_Validate_Hostname::ALLOW_DNS)
+ {
+ if ($hostnameValidator === null) {
+ $hostnameValidator = new Zend_Validate_Hostname($allow);
+ }
+ $this->hostnameValidator = $hostnameValidator;
+ }
+
+ /**
+ * Whether MX checking via dns_get_mx is supported or not
+ *
+ * This currently only works on UNIX systems
+ *
+ * @return boolean
+ */
+ public function validateMxSupported()
+ {
+ return function_exists('dns_get_mx');
+ }
+
+ /**
+ * Set whether we check for a valid MX record via DNS
+ *
+ * This only applies when DNS hostnames are validated
+ *
+ * @param boolean $allowed Set allowed to true to validate for MX records, and false to not validate them
+ */
+ public function setValidateMx($allowed)
+ {
+ $this->_validateMx = (bool) $allowed;
+ }
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if and only if $value is a valid email address
+ * according to RFC2822
+ *
+ * @link http://www.ietf.org/rfc/rfc2822.txt RFC2822
+ * @link http://www.columbia.edu/kermit/ascii.html US-ASCII characters
+ * @param string $value
+ * @return boolean
+ */
+ public function isValid($value)
+ {
+ $valueString = (string) $value;
+ $matches = array();
+ $length = true;
+
+ $this->_setValue($valueString);
+
+ // Split email address up and disallow '..'
+ if ((strpos($valueString, '..') !== false) or
+ (!preg_match('/^(.+)@([^@]+)$/', $valueString, $matches))) {
+ $this->_error(self::INVALID);
+ return false;
+ }
+
+ $this->_localPart = $matches[1];
+ $this->_hostname = $matches[2];
+
+ if ((strlen($this->_localPart) > 64) || (strlen($this->_hostname) > 255)) {
+ $length = false;
+ $this->_error(self::LENGTH_EXCEEDED);
+ }
+
+ // Match hostname part
+ $hostnameResult = $this->hostnameValidator->setTranslator($this->getTranslator())
+ ->isValid($this->_hostname);
+ if (!$hostnameResult) {
+ $this->_error(self::INVALID_HOSTNAME);
+
+ // Get messages and errors from hostnameValidator
+ foreach ($this->hostnameValidator->getMessages() as $code => $message) {
+ $this->_messages[$code] = $message;
+ }
+ foreach ($this->hostnameValidator->getErrors() as $error) {
+ $this->_errors[] = $error;
+ }
+ } else if ($this->_validateMx) {
+ // MX check on hostname via dns_get_record()
+ if ($this->validateMxSupported()) {
+ $result = dns_get_mx($this->_hostname, $mxHosts);
+ if (count($mxHosts) < 1) {
+ $hostnameResult = false;
+ $this->_error(self::INVALID_MX_RECORD);
+ }
+ } else {
+ /**
+ * MX checks are not supported by this system
+ * @see Zend_Validate_Exception
+ */
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception('Internal error: MX checking not available on this system');
+ }
+ }
+
+ // First try to match the local part on the common dot-atom format
+ $localResult = false;
+
+ // Dot-atom characters are: 1*atext *("." 1*atext)
+ // atext: ALPHA / DIGIT / and "!", "#", "$", "%", "&", "'", "*",
+ // "-", "/", "=", "?", "^", "_", "`", "{", "|", "}", "~"
+ $atext = 'a-zA-Z0-9\x21\x23\x24\x25\x26\x27\x2a\x2b\x2d\x2f\x3d\x3f\x5e\x5f\x60\x7b\x7c\x7d';
+ if (preg_match('/^[' . $atext . ']+(\x2e+[' . $atext . ']+)*$/', $this->_localPart)) {
+ $localResult = true;
+ } else {
+ // Try quoted string format
+
+ // Quoted-string characters are: DQUOTE *([FWS] qtext/quoted-pair) [FWS] DQUOTE
+ // qtext: Non white space controls, and the rest of the US-ASCII characters not
+ // including "\" or the quote character
+ $noWsCtl = '\x01-\x08\x0b\x0c\x0e-\x1f\x7f';
+ $qtext = $noWsCtl . '\x21\x23-\x5b\x5d-\x7e';
+ $ws = '\x20\x09';
+ if (preg_match('/^\x22([' . $ws . $qtext . '])*[$ws]?\x22$/', $this->_localPart)) {
+ $localResult = true;
+ } else {
+ $this->_error(self::DOT_ATOM);
+ $this->_error(self::QUOTED_STRING);
+ $this->_error(self::INVALID_LOCAL_PART);
+ }
+ }
+
+ // If both parts valid, return true
+ if ($localResult && $hostnameResult && $length) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/lib/Zend/Validate/Exception.php b/lib/Zend/Validate/Exception.php
new file mode 100644
index 0000000..a38077e
--- /dev/null
+++ b/lib/Zend/Validate/Exception.php
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Exception.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Exception
+ */
+require_once 'Zend/Exception.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_Exception extends Zend_Exception
+{}
diff --git a/lib/Zend/Validate/File/Count.php b/lib/Zend/Validate/File/Count.php
new file mode 100644
index 0000000..97667ab
--- /dev/null
+++ b/lib/Zend/Validate/File/Count.php
@@ -0,0 +1,275 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * Validator for counting all given files
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_File_Count extends Zend_Validate_Abstract
+{
+ /**#@+
+ * @const string Error constants
+ */
+ const TOO_MUCH = 'fileCountTooMuch';
+ const TOO_LESS = 'fileCountTooLess';
+ /**#@-*/
+
+ /**
+ * @var array Error message templates
+ */
+ protected $_messageTemplates = array(
+ self::TOO_MUCH => "Too much files, maximum '%max%' are allowed but '%count%' are given",
+ self::TOO_LESS => "Too less files, minimum '%min%' are expected but '%count%' are given"
+ );
+
+ /**
+ * @var array Error message template variables
+ */
+ protected $_messageVariables = array(
+ 'min' => '_min',
+ 'max' => '_max',
+ 'count' => '_count'
+ );
+
+ /**
+ * Minimum file count
+ *
+ * If null, there is no minimum file count
+ *
+ * @var integer
+ */
+ protected $_min;
+
+ /**
+ * Maximum file count
+ *
+ * If null, there is no maximum file count
+ *
+ * @var integer|null
+ */
+ protected $_max;
+
+ /**
+ * Actual filecount
+ *
+ * @var integer
+ */
+ protected $_count;
+
+ /**
+ * Internal file array
+ * @var array
+ */
+ protected $_files;
+
+ /**
+ * Sets validator options
+ *
+ * Min limits the file count, when used with max=null it is the maximum file count
+ * It also accepts an array with the keys 'min' and 'max'
+ *
+ * If $options is a integer, it will be used as maximum file count
+ * As Array is accepts the following keys:
+ * 'min': Minimum filecount
+ * 'max': Maximum filecount
+ *
+ * @param integer|array $options Options for the adapter
+ * @param integer $max (Deprecated) Maximum value (implies $options is the minimum)
+ * @return void
+ */
+ public function __construct($options)
+ {
+ if ($options instanceof Zend_Config) {
+ $options = $options->toArray();
+ } elseif (is_string($options) || is_numeric($options)) {
+ $options = array('max' => $options);
+ } elseif (!is_array($options)) {
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception ('Invalid options to validator provided');
+ }
+
+ if (1 < func_num_args()) {
+ trigger_error('Multiple arguments are deprecated in favor of an array of named arguments', E_USER_NOTICE);
+ $options['min'] = func_get_arg(0);
+ $options['max'] = func_get_arg(1);
+ }
+
+ if (isset($options['min'])) {
+ $this->setMin($options);
+ }
+
+ if (isset($options['max'])) {
+ $this->setMax($options);
+ }
+ }
+
+ /**
+ * Returns the minimum file count
+ *
+ * @return integer
+ */
+ public function getMin()
+ {
+ return $this->_min;
+ }
+
+ /**
+ * Sets the minimum file count
+ *
+ * @param integer|array $min The minimum file count
+ * @return Zend_Validate_File_Count Provides a fluent interface
+ * @throws Zend_Validate_Exception When min is greater than max
+ */
+ public function setMin($min)
+ {
+ if (is_array($min) and isset($min['min'])) {
+ $min = $min['min'];
+ }
+
+ if (!is_string($min) and !is_numeric($min)) {
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception ('Invalid options to validator provided');
+ }
+
+ $min = (integer) $min;
+ if (($this->_max !== null) && ($min > $this->_max)) {
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception("The minimum must be less than or equal to the maximum file count, but $min >"
+ . " {$this->_max}");
+ }
+
+ $this->_min = $min;
+ return $this;
+ }
+
+ /**
+ * Returns the maximum file count
+ *
+ * @return integer
+ */
+ public function getMax()
+ {
+ return $this->_max;
+ }
+
+ /**
+ * Sets the maximum file count
+ *
+ * @param integer|array $max The maximum file count
+ * @return Zend_Validate_StringLength Provides a fluent interface
+ * @throws Zend_Validate_Exception When max is smaller than min
+ */
+ public function setMax($max)
+ {
+ if (is_array($max) and isset($max['max'])) {
+ $max = $max['max'];
+ }
+
+ if (!is_string($max) and !is_numeric($max)) {
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception ('Invalid options to validator provided');
+ }
+
+ $max = (integer) $max;
+ if (($this->_min !== null) && ($max < $this->_min)) {
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception("The maximum must be greater than or equal to the minimum file count, but "
+ . "$max < {$this->_min}");
+ }
+
+ $this->_max = $max;
+ return $this;
+ }
+
+ /**
+ * Adds a file for validation
+ *
+ * @param string|array $file
+ */
+ public function addFile($file)
+ {
+ if (is_string($file)) {
+ $file = array($file);
+ }
+
+ if (is_array($file)) {
+ foreach ($file as $name) {
+ if (!isset($this->_files[$name])) {
+ $this->_files[$name] = $name;
+ }
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if and only if the file count of all checked files is at least min and
+ * not bigger than max (when max is not null). Attention: When checking with set min you
+ * must give all files with the first call, otherwise you will get an false.
+ *
+ * @param string|array $value Filenames to check for count
+ * @param array $file File data from Zend_File_Transfer
+ * @return boolean
+ */
+ public function isValid($value, $file = null)
+ {
+ $this->addFile($value);
+ $this->_count = count($this->_files);
+ if (($this->_max !== null) && ($this->_count > $this->_max)) {
+ return $this->_throw($file, self::TOO_MUCH);
+ }
+
+ if (($this->_min !== null) && ($this->_count < $this->_min)) {
+ return $this->_throw($file, self::TOO_LESS);
+ }
+
+ return true;
+ }
+
+ /**
+ * Throws an error of the given type
+ *
+ * @param string $file
+ * @param string $errorType
+ * @return false
+ */
+ protected function _throw($file, $errorType)
+ {
+ if ($file !== null) {
+ $this->_value = $file['name'];
+ }
+
+ $this->_error($errorType);
+ return false;
+ }
+}
diff --git a/lib/Zend/Validate/File/Crc32.php b/lib/Zend/Validate/File/Crc32.php
new file mode 100644
index 0000000..47ace9d
--- /dev/null
+++ b/lib/Zend/Validate/File/Crc32.php
@@ -0,0 +1,179 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: $
+ */
+
+/**
+ * @see Zend_Validate_File_Hash
+ */
+require_once 'Zend/Validate/File/Hash.php';
+
+/**
+ * Validator for the crc32 hash of given files
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_File_Crc32 extends Zend_Validate_File_Hash
+{
+ /**
+ * @const string Error constants
+ */
+ const DOES_NOT_MATCH = 'fileCrc32DoesNotMatch';
+ const NOT_DETECTED = 'fileCrc32NotDetected';
+ const NOT_FOUND = 'fileCrc32NotFound';
+
+ /**
+ * @var array Error message templates
+ */
+ protected $_messageTemplates = array(
+ self::DOES_NOT_MATCH => "The file '%value%' does not match the given crc32 hashes",
+ self::NOT_DETECTED => "There was no crc32 hash detected for the given file",
+ self::NOT_FOUND => "The file '%value%' could not be found"
+ );
+
+ /**
+ * Hash of the file
+ *
+ * @var string
+ */
+ protected $_hash;
+
+ /**
+ * Sets validator options
+ *
+ * @param string|array $options
+ * @return void
+ */
+ public function __construct($options)
+ {
+ if ($options instanceof Zend_Config) {
+ $options = $options->toArray();
+ } elseif (is_scalar($options)) {
+ $options = array('hash1' => $options);
+ } elseif (!is_array($options)) {
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception('Invalid options to validator provided');
+ }
+
+ $this->setCrc32($options);
+ }
+
+ /**
+ * Returns all set crc32 hashes
+ *
+ * @return array
+ */
+ public function getCrc32()
+ {
+ return $this->getHash();
+ }
+
+ /**
+ * Sets the crc32 hash for one or multiple files
+ *
+ * @param string|array $options
+ * @return Zend_Validate_File_Hash Provides a fluent interface
+ */
+ public function setHash($options)
+ {
+ if (!is_array($options)) {
+ $options = array($options);
+ }
+
+ $options['algorithm'] = 'crc32';
+ parent::setHash($options);
+ return $this;
+ }
+
+ /**
+ * Sets the crc32 hash for one or multiple files
+ *
+ * @param string|array $options
+ * @return Zend_Validate_File_Hash Provides a fluent interface
+ */
+ public function setCrc32($options)
+ {
+ $this->setHash($options);
+ return $this;
+ }
+
+ /**
+ * Adds the crc32 hash for one or multiple files
+ *
+ * @param string|array $options
+ * @return Zend_Validate_File_Hash Provides a fluent interface
+ */
+ public function addHash($options)
+ {
+ if (!is_array($options)) {
+ $options = array($options);
+ }
+
+ $options['algorithm'] = 'crc32';
+ parent::addHash($options);
+ return $this;
+ }
+
+ /**
+ * Adds the crc32 hash for one or multiple files
+ *
+ * @param string|array $options
+ * @return Zend_Validate_File_Hash Provides a fluent interface
+ */
+ public function addCrc32($options)
+ {
+ $this->addHash($options);
+ return $this;
+ }
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if and only if the given file confirms the set hash
+ *
+ * @param string $value Filename to check for hash
+ * @param array $file File data from Zend_File_Transfer
+ * @return boolean
+ */
+ public function isValid($value, $file = null)
+ {
+ // Is file readable ?
+ require_once 'Zend/Loader.php';
+ if (!Zend_Loader::isReadable($value)) {
+ return $this->_throw($file, self::NOT_FOUND);
+ }
+
+ $hashes = array_unique(array_keys($this->_hash));
+ $filehash = hash_file('crc32', $value);
+ if ($filehash === false) {
+ return $this->_throw($file, self::NOT_DETECTED);
+ }
+
+ foreach($hashes as $hash) {
+ if ($filehash === $hash) {
+ return true;
+ }
+ }
+
+ return $this->_throw($file, self::DOES_NOT_MATCH);
+ }
+} \ No newline at end of file
diff --git a/lib/Zend/Validate/File/ExcludeExtension.php b/lib/Zend/Validate/File/ExcludeExtension.php
new file mode 100644
index 0000000..2a442ab
--- /dev/null
+++ b/lib/Zend/Validate/File/ExcludeExtension.php
@@ -0,0 +1,94 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/File/Extension.php';
+
+/**
+ * Validator for the excluding file extensions
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_File_ExcludeExtension extends Zend_Validate_File_Extension
+{
+ /**
+ * @const string Error constants
+ */
+ const FALSE_EXTENSION = 'fileExcludeExtensionFalse';
+ const NOT_FOUND = 'fileExcludeExtensionNotFound';
+
+ /**
+ * @var array Error message templates
+ */
+ protected $_messageTemplates = array(
+ self::FALSE_EXTENSION => "The file '%value%' has a false extension",
+ self::NOT_FOUND => "The file '%value%' was not found"
+ );
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if and only if the fileextension of $value is not included in the
+ * set extension list
+ *
+ * @param string $value Real file to check for extension
+ * @param array $file File data from Zend_File_Transfer
+ * @return boolean
+ */
+ public function isValid($value, $file = null)
+ {
+ // Is file readable ?
+ require_once 'Zend/Loader.php';
+ if (!Zend_Loader::isReadable($value)) {
+ return $this->_throw($file, self::NOT_FOUND);
+ }
+
+ if ($file !== null) {
+ $info['extension'] = substr($file['name'], strrpos($file['name'], '.') + 1);
+ } else {
+ $info = pathinfo($value);
+ }
+
+ $extensions = $this->getExtension();
+
+ if ($this->_case and (!in_array($info['extension'], $extensions))) {
+ return true;
+ } else if (!$this->_case) {
+ $found = false;
+ foreach ($extensions as $extension) {
+ if (strtolower($extension) == strtolower($info['extension'])) {
+ $found = true;
+ }
+ }
+
+ if (!$found) {
+ return true;
+ }
+ }
+
+ return $this->_throw($file, self::FALSE_EXTENSION);
+ }
+}
diff --git a/lib/Zend/Validate/File/ExcludeMimeType.php b/lib/Zend/Validate/File/ExcludeMimeType.php
new file mode 100644
index 0000000..d69dc06
--- /dev/null
+++ b/lib/Zend/Validate/File/ExcludeMimeType.php
@@ -0,0 +1,91 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: $
+ */
+
+/**
+ * @see Zend_Validate_File_MimeType
+ */
+require_once 'Zend/Validate/File/MimeType.php';
+
+/**
+ * Validator for the mime type of a file
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_File_ExcludeMimeType extends Zend_Validate_File_MimeType
+{
+ const FALSE_TYPE = 'fileExcludeMimeTypeFalse';
+ const NOT_DETECTED = 'fileExcludeMimeTypeNotDetected';
+ const NOT_READABLE = 'fileExcludeMimeTypeNotReadable';
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if the mimetype of the file does not matche the given ones. Also parts
+ * of mimetypes can be checked. If you give for example "image" all image
+ * mime types will not be accepted like "image/gif", "image/jpeg" and so on.
+ *
+ * @param string $value Real file to check for mimetype
+ * @param array $file File data from Zend_File_Transfer
+ * @return boolean
+ */
+ public function isValid($value, $file = null)
+ {
+ // Is file readable ?
+ require_once 'Zend/Loader.php';
+ if (!Zend_Loader::isReadable($value)) {
+ return $this->_throw($file, self::NOT_READABLE);
+ }
+
+ if ($file !== null) {
+ if (class_exists('finfo', false) && defined('MAGIC')) {
+ $mime = new finfo(FILEINFO_MIME);
+ $this->_type = $mime->file($value);
+ unset($mime);
+ } elseif (function_exists('mime_content_type') && ini_get('mime_magic.magicfile')) {
+ $this->_type = mime_content_type($value);
+ } else {
+ $this->_type = $file['type'];
+ }
+ }
+
+ if (empty($this->_type)) {
+ return $this->_throw($file, self::NOT_DETECTED);
+ }
+
+ $mimetype = $this->getMimeType(true);
+ if (in_array($this->_type, $mimetype)) {
+ return $this->_throw($file, self::FALSE_TYPE);
+ }
+
+ $types = explode('/', $this->_type);
+ $types = array_merge($types, explode('-', $this->_type));
+ foreach($mimetype as $mime) {
+ if (in_array($mime, $types)) {
+ return $this->_throw($file, self::FALSE_TYPE);
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/lib/Zend/Validate/File/Exists.php b/lib/Zend/Validate/File/Exists.php
new file mode 100644
index 0000000..cf509aa
--- /dev/null
+++ b/lib/Zend/Validate/File/Exists.php
@@ -0,0 +1,203 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * Validator which checks if the file already exists in the directory
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_File_Exists extends Zend_Validate_Abstract
+{
+ /**
+ * @const string Error constants
+ */
+ const DOES_NOT_EXIST = 'fileExistsDoesNotExist';
+
+ /**
+ * @var array Error message templates
+ */
+ protected $_messageTemplates = array(
+ self::DOES_NOT_EXIST => "The file '%value%' does not exist"
+ );
+
+ /**
+ * Internal list of directories
+ * @var string
+ */
+ protected $_directory = '';
+
+ /**
+ * @var array Error message template variables
+ */
+ protected $_messageVariables = array(
+ 'directory' => '_directory'
+ );
+
+ /**
+ * Sets validator options
+ *
+ * @param string|array $directory
+ * @return void
+ */
+ public function __construct($directory = array())
+ {
+ if ($directory instanceof Zend_Config) {
+ $directory = $directory->toArray();
+ } else if (is_string($directory)) {
+ $directory = explode(',', $directory);
+ } else if (!is_array($directory)) {
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception ('Invalid options to validator provided');
+ }
+
+ $this->setDirectory($directory);
+ }
+
+ /**
+ * Returns the set file directories which are checked
+ *
+ * @param boolean $asArray Returns the values as array, when false an concated string is returned
+ * @return string
+ */
+ public function getDirectory($asArray = false)
+ {
+ $asArray = (bool) $asArray;
+ $directory = (string) $this->_directory;
+ if ($asArray) {
+ $directory = explode(',', $directory);
+ }
+
+ return $directory;
+ }
+
+ /**
+ * Sets the file directory which will be checked
+ *
+ * @param string|array $directory The directories to validate
+ * @return Zend_Validate_File_Extension Provides a fluent interface
+ */
+ public function setDirectory($directory)
+ {
+ $this->_directory = null;
+ $this->addDirectory($directory);
+ return $this;
+ }
+
+ /**
+ * Adds the file directory which will be checked
+ *
+ * @param string|array $directory The directory to add for validation
+ * @return Zend_Validate_File_Extension Provides a fluent interface
+ */
+ public function addDirectory($directory)
+ {
+ $directories = $this->getDirectory(true);
+
+ if (is_string($directory)) {
+ $directory = explode(',', $directory);
+ } else if (!is_array($directory)) {
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception ('Invalid options to validator provided');
+ }
+
+ foreach ($directory as $content) {
+ if (empty($content) || !is_string($content)) {
+ continue;
+ }
+
+ $directories[] = trim($content);
+ }
+ $directories = array_unique($directories);
+
+ // Sanity check to ensure no empty values
+ foreach ($directories as $key => $dir) {
+ if (empty($dir)) {
+ unset($directories[$key]);
+ }
+ }
+
+ $this->_directory = implode(',', $directories);
+
+ return $this;
+ }
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if and only if the file already exists in the set directories
+ *
+ * @param string $value Real file to check for existance
+ * @param array $file File data from Zend_File_Transfer
+ * @return boolean
+ */
+ public function isValid($value, $file = null)
+ {
+ $directories = $this->getDirectory(true);
+ if (($file !== null) and (!empty($file['destination']))) {
+ $directories[] = $file['destination'];
+ } else if (!isset($file['name'])) {
+ $file['name'] = $value;
+ }
+
+ $check = false;
+ foreach ($directories as $directory) {
+ if (empty($directory)) {
+ continue;
+ }
+
+ $check = true;
+ if (!file_exists($directory . DIRECTORY_SEPARATOR . $file['name'])) {
+ return $this->_throw($file, self::DOES_NOT_EXIST);
+ }
+ }
+
+ if (!$check) {
+ return $this->_throw($file, self::DOES_NOT_EXIST);
+ }
+
+ return true;
+ }
+
+ /**
+ * Throws an error of the given type
+ *
+ * @param string $file
+ * @param string $errorType
+ * @return false
+ */
+ protected function _throw($file, $errorType)
+ {
+ if ($file !== null) {
+ $this->_value = $file['name'];
+ }
+
+ $this->_error($errorType);
+ return false;
+ }
+}
diff --git a/lib/Zend/Validate/File/Extension.php b/lib/Zend/Validate/File/Extension.php
new file mode 100644
index 0000000..49411b3
--- /dev/null
+++ b/lib/Zend/Validate/File/Extension.php
@@ -0,0 +1,234 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * Validator for the file extension of a file
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_File_Extension extends Zend_Validate_Abstract
+{
+ /**
+ * @const string Error constants
+ */
+ const FALSE_EXTENSION = 'fileExtensionFalse';
+ const NOT_FOUND = 'fileExtensionNotFound';
+
+ /**
+ * @var array Error message templates
+ */
+ protected $_messageTemplates = array(
+ self::FALSE_EXTENSION => "The file '%value%' has a false extension",
+ self::NOT_FOUND => "The file '%value%' was not found"
+ );
+
+ /**
+ * Internal list of extensions
+ * @var string
+ */
+ protected $_extension = '';
+
+ /**
+ * Validate case sensitive
+ *
+ * @var boolean
+ */
+ protected $_case = false;
+
+ /**
+ * @var array Error message template variables
+ */
+ protected $_messageVariables = array(
+ 'extension' => '_extension'
+ );
+
+ /**
+ * Sets validator options
+ *
+ * @param string|array $extension
+ * @param boolean $case If true validation is done case sensitive
+ * @return void
+ */
+ public function __construct($options)
+ {
+ if ($options instanceof Zend_Config) {
+ $options = $options->toArray();
+ }
+
+ if (1 < func_num_args()) {
+ trigger_error('Multiple arguments to constructor are deprecated in favor of options array', E_USER_NOTICE);
+ $case = func_get_arg(1);
+ $this->setCase($case);
+ }
+
+ if (is_array($options) and isset($options['case'])) {
+ $this->setCase($options['case']);
+ unset($options['case']);
+ }
+
+ $this->setExtension($options);
+ }
+
+ /**
+ * Returns the case option
+ *
+ * @return boolean
+ */
+ public function getCase()
+ {
+ return $this->_case;
+ }
+
+ /**
+ * Sets the case to use
+ *
+ * @param boolean $case
+ * @return Zend_Validate_File_Extension Provides a fluent interface
+ */
+ public function setCase($case)
+ {
+ $this->_case = (boolean) $case;
+ return $this;
+ }
+
+ /**
+ * Returns the set file extension
+ *
+ * @return array
+ */
+ public function getExtension()
+ {
+ $extension = explode(',', $this->_extension);
+
+ return $extension;
+ }
+
+ /**
+ * Sets the file extensions
+ *
+ * @param string|array $extension The extensions to validate
+ * @return Zend_Validate_File_Extension Provides a fluent interface
+ */
+ public function setExtension($extension)
+ {
+ $this->_extension = null;
+ $this->addExtension($extension);
+ return $this;
+ }
+
+ /**
+ * Adds the file extensions
+ *
+ * @param string|array $extension The extensions to add for validation
+ * @return Zend_Validate_File_Extension Provides a fluent interface
+ */
+ public function addExtension($extension)
+ {
+ $extensions = $this->getExtension();
+ if (is_string($extension)) {
+ $extension = explode(',', $extension);
+ }
+
+ foreach ($extension as $content) {
+ if (empty($content) || !is_string($content)) {
+ continue;
+ }
+
+ $extensions[] = trim($content);
+ }
+ $extensions = array_unique($extensions);
+
+ // Sanity check to ensure no empty values
+ foreach ($extensions as $key => $ext) {
+ if (empty($ext)) {
+ unset($extensions[$key]);
+ }
+ }
+
+ $this->_extension = implode(',', $extensions);
+
+ return $this;
+ }
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if and only if the fileextension of $value is included in the
+ * set extension list
+ *
+ * @param string $value Real file to check for extension
+ * @param array $file File data from Zend_File_Transfer
+ * @return boolean
+ */
+ public function isValid($value, $file = null)
+ {
+ // Is file readable ?
+ require_once 'Zend/Loader.php';
+ if (!Zend_Loader::isReadable($value)) {
+ return $this->_throw($file, self::NOT_FOUND);
+ }
+
+ if ($file !== null) {
+ $info['extension'] = substr($file['name'], strrpos($file['name'], '.') + 1);
+ } else {
+ $info = pathinfo($value);
+ }
+
+ $extensions = $this->getExtension();
+
+ if ($this->_case && (in_array($info['extension'], $extensions))) {
+ return true;
+ } else if (!$this->getCase()) {
+ foreach ($extensions as $extension) {
+ if (strtolower($extension) == strtolower($info['extension'])) {
+ return true;
+ }
+ }
+ }
+
+ return $this->_throw($file, self::FALSE_EXTENSION);
+ }
+
+ /**
+ * Throws an error of the given type
+ *
+ * @param string $file
+ * @param string $errorType
+ * @return false
+ */
+ protected function _throw($file, $errorType)
+ {
+ if (null !== $file) {
+ $this->_value = $file['name'];
+ }
+
+ $this->_error($errorType);
+ return false;
+ }
+}
diff --git a/lib/Zend/Validate/File/FilesSize.php b/lib/Zend/Validate/File/FilesSize.php
new file mode 100644
index 0000000..ea8d085
--- /dev/null
+++ b/lib/Zend/Validate/File/FilesSize.php
@@ -0,0 +1,163 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: $
+ */
+
+/**
+ * @see Zend_Validate_File_Size
+ */
+require_once 'Zend/Validate/File/Size.php';
+
+/**
+ * Validator for the size of all files which will be validated in sum
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_File_FilesSize extends Zend_Validate_File_Size
+{
+ /**
+ * @const string Error constants
+ */
+ const TOO_BIG = 'fileFilesSizeTooBig';
+ const TOO_SMALL = 'fileFilesSizeTooSmall';
+ const NOT_READABLE = 'fileFilesSizeNotReadable';
+
+ /**
+ * @var array Error message templates
+ */
+ protected $_messageTemplates = array(
+ self::TOO_BIG => "All files in sum should have a maximum size of '%max%' but '%size%' were detected",
+ self::TOO_SMALL => "All files in sum should have a minimum size of '%min%' but '%size%' were detected",
+ self::NOT_READABLE => "One or more files can not be read"
+ );
+
+ /**
+ * Internal file array
+ *
+ * @var array
+ */
+ protected $_files;
+
+ /**
+ * Sets validator options
+ *
+ * Min limits the used diskspace for all files, when used with max=null it is the maximum filesize
+ * It also accepts an array with the keys 'min' and 'max'
+ *
+ * @param integer|array $min Minimum diskspace for all files
+ * @param integer $max Maximum diskspace for all files (deprecated)
+ * @param boolean $bytestring Use bytestring or real size ? (deprecated)
+ * @return void
+ */
+ public function __construct($options)
+ {
+ $this->_files = array();
+ $this->_setSize(0);
+
+ if (1 < func_num_args()) {
+ trigger_error('Multiple constructor options are deprecated in favor of a single options array', E_USER_NOTICE);
+ if ($options instanceof Zend_Config) {
+ $options = $options->toArray();
+ } elseif (is_scalar($options)) {
+ $options = array('min' => $options);
+ } elseif (!is_array($options)) {
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception('Invalid options to validator provided');
+ }
+
+ $argv = func_get_args();
+ array_shift($argv);
+ $options['max'] = array_shift($argv);
+ if (!empty($argv)) {
+ $options['bytestring'] = array_shift($argv);
+ }
+ }
+
+ parent::__construct($options);
+ }
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if and only if the disk usage of all files is at least min and
+ * not bigger than max (when max is not null).
+ *
+ * @param string|array $value Real file to check for size
+ * @param array $file File data from Zend_File_Transfer
+ * @return boolean
+ */
+ public function isValid($value, $file = null)
+ {
+ require_once 'Zend/Loader.php';
+ if (is_string($value)) {
+ $value = array($value);
+ }
+
+ $min = $this->getMin(true);
+ $max = $this->getMax(true);
+ $size = $this->_getSize();
+ foreach ($value as $files) {
+ // Is file readable ?
+ if (!Zend_Loader::isReadable($files)) {
+ $this->_throw($file, self::NOT_READABLE);
+ continue;
+ }
+
+ if (!isset($this->_files[$files])) {
+ $this->_files[$files] = $files;
+ } else {
+ // file already counted... do not count twice
+ continue;
+ }
+
+ // limited to 2GB files
+ $size += @filesize($files);
+ $this->_setSize($size);
+ if (($max !== null) && ($max < $size)) {
+ if ($this->useByteString()) {
+ $this->setMax($this->_toByteString($max));
+ $this->_throw($file, self::TOO_BIG);
+ $this->setMax($max);
+ } else {
+ $this->_throw($file, self::TOO_BIG);
+ }
+ }
+ }
+
+ // Check that aggregate files are >= minimum size
+ if (($min !== null) && ($size < $min)) {
+ if ($this->useByteString()) {
+ $this->setMin($this->_toByteString($min));
+ $this->_throw($file, self::TOO_SMALL);
+ $this->setMin($min);
+ } else {
+ $this->_throw($file, self::TOO_SMALL);
+ }
+ }
+
+ if (count($this->_messages) > 0) {
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/lib/Zend/Validate/File/Hash.php b/lib/Zend/Validate/File/Hash.php
new file mode 100644
index 0000000..1702593
--- /dev/null
+++ b/lib/Zend/Validate/File/Hash.php
@@ -0,0 +1,195 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * Validator for the hash of given files
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_File_Hash extends Zend_Validate_Abstract
+{
+ /**
+ * @const string Error constants
+ */
+ const DOES_NOT_MATCH = 'fileHashDoesNotMatch';
+ const NOT_DETECTED = 'fileHashHashNotDetected';
+ const NOT_FOUND = 'fileHashNotFound';
+
+ /**
+ * @var array Error message templates
+ */
+ protected $_messageTemplates = array(
+ self::DOES_NOT_MATCH => "The file '%value%' does not match the given hashes",
+ self::NOT_DETECTED => "There was no hash detected for the given file",
+ self::NOT_FOUND => "The file '%value%' could not be found"
+ );
+
+ /**
+ * Hash of the file
+ *
+ * @var string
+ */
+ protected $_hash;
+
+ /**
+ * Sets validator options
+ *
+ * @param string|array $options
+ * @return void
+ */
+ public function __construct($options)
+ {
+ if ($options instanceof Zend_Config) {
+ $options = $options->toArray();
+ } elseif (is_scalar($options)) {
+ $options = array('hash1' => $options);
+ } elseif (!is_array($options)) {
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception('Invalid options to validator provided');
+ }
+
+ if (1 < func_num_args()) {
+ trigger_error('Multiple constructor options are deprecated in favor of a single options array', E_USER_NOTICE);
+ $options['algorithm'] = func_get_arg(1);
+ }
+
+ $this->setHash($options);
+ }
+
+ /**
+ * Returns the set hash values as array, the hash as key and the algorithm the value
+ *
+ * @return array
+ */
+ public function getHash()
+ {
+ return $this->_hash;
+ }
+
+ /**
+ * Sets the hash for one or multiple files
+ *
+ * @param string|array $options
+ * @return Zend_Validate_File_Hash Provides a fluent interface
+ */
+ public function setHash($options)
+ {
+ $this->_hash = null;
+ $this->addHash($options);
+
+ return $this;
+ }
+
+ /**
+ * Adds the hash for one or multiple files
+ *
+ * @param string|array $options
+ * @return Zend_Validate_File_Hash Provides a fluent interface
+ */
+ public function addHash($options)
+ {
+ if (is_string($options)) {
+ $options = array($options);
+ } else if (!is_array($options)) {
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception("False parameter given");
+ }
+
+ $known = hash_algos();
+ if (!isset($options['algorithm'])) {
+ $algorithm = 'crc32';
+ } else {
+ $algorithm = $options['algorithm'];
+ unset($options['algorithm']);
+ }
+
+ if (!in_array($algorithm, $known)) {
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception("Unknown algorithm '{$algorithm}'");
+ }
+
+ foreach ($options as $value) {
+ $this->_hash[$value] = $algorithm;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if and only if the given file confirms the set hash
+ *
+ * @param string $value Filename to check for hash
+ * @param array $file File data from Zend_File_Transfer
+ * @return boolean
+ */
+ public function isValid($value, $file = null)
+ {
+ // Is file readable ?
+ require_once 'Zend/Loader.php';
+ if (!Zend_Loader::isReadable($value)) {
+ return $this->_throw($file, self::NOT_FOUND);
+ }
+
+ $algos = array_unique(array_values($this->_hash));
+ $hashes = array_unique(array_keys($this->_hash));
+ foreach ($algos as $algorithm) {
+ $filehash = hash_file($algorithm, $value);
+ if ($filehash === false) {
+ return $this->_throw($file, self::NOT_DETECTED);
+ }
+
+ foreach($hashes as $hash) {
+ if ($filehash === $hash) {
+ return true;
+ }
+ }
+ }
+
+ return $this->_throw($file, self::DOES_NOT_MATCH);
+ }
+
+ /**
+ * Throws an error of the given type
+ *
+ * @param string $file
+ * @param string $errorType
+ * @return false
+ */
+ protected function _throw($file, $errorType)
+ {
+ if ($file !== null) {
+ $this->_value = $file['name'];
+ }
+
+ $this->_error($errorType);
+ return false;
+ }
+}
diff --git a/lib/Zend/Validate/File/ImageSize.php b/lib/Zend/Validate/File/ImageSize.php
new file mode 100644
index 0000000..ada8bd3
--- /dev/null
+++ b/lib/Zend/Validate/File/ImageSize.php
@@ -0,0 +1,370 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * Validator for the image size of a image file
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_File_ImageSize extends Zend_Validate_Abstract
+{
+ /**
+ * @const string Error constants
+ */
+ const WIDTH_TOO_BIG = 'fileImageSizeWidthTooBig';
+ const WIDTH_TOO_SMALL = 'fileImageSizeWidthTooSmall';
+ const HEIGHT_TOO_BIG = 'fileImageSizeHeightTooBig';
+ const HEIGHT_TOO_SMALL = 'fileImageSizeHeightTooSmall';
+ const NOT_DETECTED = 'fileImageSizeNotDetected';
+ const NOT_READABLE = 'fileImageSizeNotReadable';
+
+ /**
+ * @var array Error message template
+ */
+ protected $_messageTemplates = array(
+ self::WIDTH_TOO_BIG => "Maximum allowed width for image '%value%' should be '%maxwidth%' but '%width%' detected",
+ self::WIDTH_TOO_SMALL => "Minimum expected width for image '%value%' should be '%minwidth%' but '%width%' detected",
+ self::HEIGHT_TOO_BIG => "Maximum allowed height for image '%value%' should be '%maxheight%' but '%height%' detected",
+ self::HEIGHT_TOO_SMALL => "Minimum expected height for image '%value%' should be '%minheight%' but '%height%' detected",
+ self::NOT_DETECTED => "The size of image '%value%' could not be detected",
+ self::NOT_READABLE => "The image '%value%' can not be read"
+ );
+
+ /**
+ * @var array Error message template variables
+ */
+ protected $_messageVariables = array(
+ 'minwidth' => '_minwidth',
+ 'maxwidth' => '_maxwidth',
+ 'minheight' => '_minheight',
+ 'maxheight' => '_maxheight',
+ 'width' => '_width',
+ 'height' => '_height'
+ );
+
+ /**
+ * Minimum image width
+ *
+ * @var integer
+ */
+ protected $_minwidth;
+
+ /**
+ * Maximum image width
+ *
+ * @var integer
+ */
+ protected $_maxwidth;
+
+ /**
+ * Minimum image height
+ *
+ * @var integer
+ */
+ protected $_minheight;
+
+ /**
+ * Maximum image height
+ *
+ * @var integer
+ */
+ protected $_maxheight;
+
+ /**
+ * Detected width
+ *
+ * @var integer
+ */
+ protected $_width;
+
+ /**
+ * Detected height
+ *
+ * @var integer
+ */
+ protected $_height;
+
+ /**
+ * Sets validator options
+ *
+ * Accepts the following option keys:
+ * - minheight
+ * - minwidth
+ * - maxheight
+ * - maxwidth
+ *
+ * @param Zend_Config|array $options
+ * @return void
+ */
+ public function __construct($options)
+ {
+ $minwidth = 0;
+ $minheight = 0;
+ $maxwidth = null;
+ $maxheight = null;
+
+ if ($options instanceof Zend_Config) {
+ $options = $options->toArray();
+ } elseif (1 < func_num_args()) {
+ trigger_error('Multiple constructor options are deprecated in favor of a single options array', E_USER_NOTICE);
+ if (!is_array($options)) {
+ $options = array('minwidth' => $options);
+ }
+ $argv = func_get_args();
+ array_shift($argv);
+ $options['minheight'] = array_shift($argv);
+ if (!empty($argv)) {
+ $options['maxwidth'] = array_shift($argv);
+ if (!empty($argv)) {
+ $options['maxheight'] = array_shift($argv);
+ }
+ }
+ } else if (!is_array($options)) {
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception ('Invalid options to validator provided');
+ }
+
+ if (isset($options['minheight']) || isset($options['minwidth'])) {
+ $this->setImageMin($options);
+ }
+
+ if (isset($options['maxheight']) || isset($options['maxwidth'])) {
+ $this->setImageMax($options);
+ }
+ }
+
+ /**
+ * Returns the set minimum image sizes
+ *
+ * @return array
+ */
+ public function getImageMin()
+ {
+ return array('minwidth' => $this->_minwidth, 'minheight' => $this->_minheight);
+ }
+
+ /**
+ * Returns the set maximum image sizes
+ *
+ * @return array
+ */
+ public function getImageMax()
+ {
+ return array('maxwidth' => $this->_maxwidth, 'maxheight' => $this->_maxheight);
+ }
+
+ /**
+ * Returns the set image width sizes
+ *
+ * @return array
+ */
+ public function getImageWidth()
+ {
+ return array('minwidth' => $this->_minwidth, 'maxwidth' => $this->_maxwidth);
+ }
+
+ /**
+ * Returns the set image height sizes
+ *
+ * @return array
+ */
+ public function getImageHeight()
+ {
+ return array('minheight' => $this->_minheight, 'maxheight' => $this->_maxheight);
+ }
+
+ /**
+ * Sets the minimum image size
+ *
+ * @param array $options The minimum image dimensions
+ * @throws Zend_Validate_Exception When minwidth is greater than maxwidth
+ * @throws Zend_Validate_Exception When minheight is greater than maxheight
+ * @return Zend_Validate_File_ImageSize Provides a fluent interface
+ */
+ public function setImageMin($options)
+ {
+ if (isset($options['minwidth'])) {
+ if (($this->_maxwidth !== null) and ($options['minwidth'] > $this->_maxwidth)) {
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception("The minimum image width must be less than or equal to the "
+ . " maximum image width, but {$options['minwidth']} > {$this->_maxwidth}");
+ }
+ }
+
+ if (isset($options['maxheight'])) {
+ if (($this->_maxheight !== null) and ($options['minheight'] > $this->_maxheight)) {
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception("The minimum image height must be less than or equal to the "
+ . " maximum image height, but {$options['minheight']} > {$this->_maxheight}");
+ }
+ }
+
+ if (isset($options['minwidth'])) {
+ $this->_minwidth = (int) $options['minwidth'];
+ }
+
+ if (isset($options['minheight'])) {
+ $this->_minheight = (int) $options['minheight'];
+ }
+
+ return $this;
+ }
+
+ /**
+ * Sets the maximum image size
+ *
+ * @param array $options The maximum image dimensions
+ * @throws Zend_Validate_Exception When maxwidth is smaller than minwidth
+ * @throws Zend_Validate_Exception When maxheight is smaller than minheight
+ * @return Zend_Validate_StringLength Provides a fluent interface
+ */
+ public function setImageMax($options)
+ {
+ if (isset($options['maxwidth'])) {
+ if (($this->_minwidth !== null) and ($options['maxwidth'] < $this->_minwidth)) {
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception("The maximum image width must be greater than or equal to the "
+ . "minimum image width, but {$options['maxwidth']} < {$this->_minwidth}");
+ }
+ }
+
+ if (isset($options['maxheight'])) {
+ if (($this->_minheight !== null) and ($options['maxheight'] < $this->_minheight)) {
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception("The maximum image height must be greater than or equal to the "
+ . "minimum image height, but {$options['maxheight']} < {$this->_minwidth}");
+ }
+ }
+
+ if (isset($options['maxwidth'])) {
+ $this->_maxwidth = (int) $options['maxwidth'];
+ }
+
+ if (isset($options['maxheight'])) {
+ $this->_maxheight = (int) $options['maxheight'];
+ }
+
+ return $this;
+ }
+
+ /**
+ * Sets the mimimum and maximum image width
+ *
+ * @param array $options The image width dimensions
+ * @return Zend_Validate_File_ImageSize Provides a fluent interface
+ */
+ public function setImageWidth($options)
+ {
+ $this->setImageMin($options);
+ $this->setImageMax($options);
+
+ return $this;
+ }
+
+ /**
+ * Sets the mimimum and maximum image height
+ *
+ * @param array $options The image height dimensions
+ * @return Zend_Validate_File_ImageSize Provides a fluent interface
+ */
+ public function setImageHeight($options)
+ {
+ $this->setImageMin($options);
+ $this->setImageMax($options);
+
+ return $this;
+ }
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if and only if the imagesize of $value is at least min and
+ * not bigger than max
+ *
+ * @param string $value Real file to check for image size
+ * @param array $file File data from Zend_File_Transfer
+ * @return boolean
+ */
+ public function isValid($value, $file = null)
+ {
+ // Is file readable ?
+ require_once 'Zend/Loader.php';
+ if (!Zend_Loader::isReadable($value)) {
+ return $this->_throw($file, self::NOT_READABLE);
+ }
+
+ $size = @getimagesize($value);
+ $this->_setValue($file);
+
+ if (empty($size) or ($size[0] === 0) or ($size[1] === 0)) {
+ return $this->_throw($file, self::NOT_DETECTED);
+ }
+
+ $this->_width = $size[0];
+ $this->_height = $size[1];
+ if ($this->_width < $this->_minwidth) {
+ $this->_throw($file, self::WIDTH_TOO_SMALL);
+ }
+
+ if (($this->_maxwidth !== null) and ($this->_maxwidth < $this->_width)) {
+ $this->_throw($file, self::WIDTH_TOO_BIG);
+ }
+
+ if ($this->_height < $this->_minheight) {
+ $this->_throw($file, self::HEIGHT_TOO_SMALL);
+ }
+
+ if (($this->_maxheight !== null) and ($this->_maxheight < $this->_height)) {
+ $this->_throw($file, self::HEIGHT_TOO_BIG);
+ }
+
+ if (count($this->_messages) > 0) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Throws an error of the given type
+ *
+ * @param string $file
+ * @param string $errorType
+ * @return false
+ */
+ protected function _throw($file, $errorType)
+ {
+ if ($file !== null) {
+ $this->_value = $file['name'];
+ }
+
+ $this->_error($errorType);
+ return false;
+ }
+}
diff --git a/lib/Zend/Validate/File/IsCompressed.php b/lib/Zend/Validate/File/IsCompressed.php
new file mode 100644
index 0000000..050eb1c
--- /dev/null
+++ b/lib/Zend/Validate/File/IsCompressed.php
@@ -0,0 +1,133 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: $
+ */
+
+/**
+ * @see Zend_Validate_File_MimeType
+ */
+require_once 'Zend/Validate/File/MimeType.php';
+
+/**
+ * Validator which checks if the file already exists in the directory
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_File_IsCompressed extends Zend_Validate_File_MimeType
+{
+ /**
+ * @const string Error constants
+ */
+ const FALSE_TYPE = 'fileIsCompressedFalseType';
+ const NOT_DETECTED = 'fileIsCompressedNotDetected';
+ const NOT_READABLE = 'fileIsCompressedNotReadable';
+
+ /**
+ * @var array Error message templates
+ */
+ protected $_messageTemplates = array(
+ self::FALSE_TYPE => "The file '%value%' is not compressed, '%type%' detected",
+ self::NOT_DETECTED => "The mimetype of file '%value%' has not been detected",
+ self::NOT_READABLE => "The file '%value%' can not be read"
+ );
+
+ /**
+ * Sets validator options
+ *
+ * @param string|array $compression
+ * @return void
+ */
+ public function __construct($mimetype = array())
+ {
+ if (empty($mimetype)) {
+ $mimetype = array(
+ 'application/x-tar',
+ 'application/x-cpio',
+ 'application/x-debian-package',
+ 'application/x-archive',
+ 'application/x-arc',
+ 'application/x-arj',
+ 'application/x-lharc',
+ 'application/x-lha',
+ 'application/x-rar',
+ 'application/zip',
+ 'application/zoo',
+ 'application/x-eet',
+ 'application/x-java-pack200',
+ 'application/x-compress',
+ 'application/x-gzip',
+ 'application/x-bzip2'
+ );
+ }
+
+ $this->setMimeType($mimetype);
+ }
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if and only if the file is compression with the set compression types
+ *
+ * @param string $value Real file to check for compression
+ * @param array $file File data from Zend_File_Transfer
+ * @return boolean
+ */
+ public function isValid($value, $file = null)
+ {
+ // Is file readable ?
+ require_once 'Zend/Loader.php';
+ if (!Zend_Loader::isReadable($value)) {
+ return $this->_throw($file, self::NOT_READABLE);
+ }
+
+ if ($file !== null) {
+ if (class_exists('finfo', false) && defined('MAGIC')) {
+ $mime = new finfo(FILEINFO_MIME);
+ $this->_type = $mime->file($value);
+ unset($mime);
+ } elseif (function_exists('mime_content_type') && ini_get('mime_magic.magicfile')) {
+ $this->_type = mime_content_type($value);
+ } else {
+ $this->_type = $file['type'];
+ }
+ }
+
+ if (empty($this->_type)) {
+ return $this->_throw($file, self::NOT_DETECTED);
+ }
+
+ $compressions = $this->getMimeType(true);
+ if (in_array($this->_type, $compressions)) {
+ return true;
+ }
+
+ $types = explode('/', $this->_type);
+ $types = array_merge($types, explode('-', $this->_type));
+ foreach ($compressions as $mime) {
+ if (in_array($mime, $types)) {
+ return true;
+ }
+ }
+
+ return $this->_throw($file, self::FALSE_TYPE);
+ }
+}
diff --git a/lib/Zend/Validate/File/IsImage.php b/lib/Zend/Validate/File/IsImage.php
new file mode 100644
index 0000000..045c7fe
--- /dev/null
+++ b/lib/Zend/Validate/File/IsImage.php
@@ -0,0 +1,137 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: $
+ */
+
+/**
+ * @see Zend_Validate_File_MimeType
+ */
+require_once 'Zend/Validate/File/MimeType.php';
+
+/**
+ * Validator which checks if the file already exists in the directory
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_File_IsImage extends Zend_Validate_File_MimeType
+{
+ /**
+ * @const string Error constants
+ */
+ const FALSE_TYPE = 'fileIsImageFalseType';
+ const NOT_DETECTED = 'fileIsImageNotDetected';
+ const NOT_READABLE = 'fileIsImageNotReadable';
+
+ /**
+ * @var array Error message templates
+ */
+ protected $_messageTemplates = array(
+ self::FALSE_TYPE => "The file '%value%' is no image, '%type%' detected",
+ self::NOT_DETECTED => "The mimetype of file '%value%' has not been detected",
+ self::NOT_READABLE => "The file '%value%' can not be read"
+ );
+
+ /**
+ * Sets validator options
+ *
+ * @param string|array $mimetype
+ * @return void
+ */
+ public function __construct($mimetype = array())
+ {
+ if (empty($mimetype)) {
+ $mimetype = array(
+ 'image/x-quicktime',
+ 'image/jp2',
+ 'image/x-xpmi',
+ 'image/x-portable-bitmap',
+ 'image/x-portable-greymap',
+ 'image/x-portable-pixmap',
+ 'image/x-niff',
+ 'image/tiff',
+ 'image/png',
+ 'image/x-unknown',
+ 'image/gif',
+ 'image/x-ms-bmp',
+ 'application/dicom',
+ 'image/vnd.adobe.photoshop',
+ 'image/vnd.djvu',
+ 'image/x-cpi',
+ 'image/jpeg',
+ 'image/x-ico',
+ 'image/x-coreldraw',
+ 'image/svg+xml'
+ );
+ }
+
+ $this->setMimeType($mimetype);
+ }
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if and only if the file is compression with the set compression types
+ *
+ * @param string $value Real file to check for compression
+ * @param array $file File data from Zend_File_Transfer
+ * @return boolean
+ */
+ public function isValid($value, $file = null)
+ {
+ // Is file readable ?
+ require_once 'Zend/Loader.php';
+ if (!Zend_Loader::isReadable($value)) {
+ return $this->_throw($file, self::NOT_READABLE);
+ }
+
+ if ($file !== null) {
+ if (class_exists('finfo', false) && defined('MAGIC')) {
+ $mime = new finfo(FILEINFO_MIME);
+ $this->_type = $mime->file($value);
+ unset($mime);
+ } elseif (function_exists('mime_content_type') && ini_get('mime_magic.magicfile')) {
+ $this->_type = mime_content_type($value);
+ } else {
+ $this->_type = $file['type'];
+ }
+ }
+
+ if (empty($this->_type)) {
+ return $this->_throw($file, self::NOT_DETECTED);
+ }
+
+ $compressions = $this->getMimeType(true);
+ if (in_array($this->_type, $compressions)) {
+ return true;
+ }
+
+ $types = explode('/', $this->_type);
+ $types = array_merge($types, explode('-', $this->_type));
+ foreach($compressions as $mime) {
+ if (in_array($mime, $types)) {
+ return true;
+ }
+ }
+
+ return $this->_throw($file, self::FALSE_TYPE);
+ }
+}
diff --git a/lib/Zend/Validate/File/Md5.php b/lib/Zend/Validate/File/Md5.php
new file mode 100644
index 0000000..4b612d0
--- /dev/null
+++ b/lib/Zend/Validate/File/Md5.php
@@ -0,0 +1,183 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: $
+ */
+
+/**
+ * @see Zend_Validate_File_Hash
+ */
+require_once 'Zend/Validate/File/Hash.php';
+
+/**
+ * Validator for the md5 hash of given files
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_File_Md5 extends Zend_Validate_File_Hash
+{
+ /**
+ * @const string Error constants
+ */
+ const DOES_NOT_MATCH = 'fileMd5DoesNotMatch';
+ const NOT_DETECTED = 'fileMd5NotDetected';
+ const NOT_FOUND = 'fileMd5NotFound';
+
+ /**
+ * @var array Error message templates
+ */
+ protected $_messageTemplates = array(
+ self::DOES_NOT_MATCH => "The file '%value%' does not match the given md5 hashes",
+ self::NOT_DETECTED => "There was no md5 hash detected for the given file",
+ self::NOT_FOUND => "The file '%value%' could not be found"
+ );
+
+ /**
+ * Hash of the file
+ *
+ * @var string
+ */
+ protected $_hash;
+
+ /**
+ * Sets validator options
+ *
+ * $hash is the hash we accept for the file $file
+ *
+ * @param string|array $options
+ * @return void
+ */
+ public function __construct($options)
+ {
+ if ($options instanceof Zend_Config) {
+ $options = $options->toArray();
+ } elseif (is_scalar($options)) {
+ $options = array('hash1' => $options);
+ } elseif (!is_array($options)) {
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception('Invalid options to validator provided');
+ }
+
+ $this->setMd5($options);
+ }
+
+ /**
+ * Returns all set md5 hashes
+ *
+ * @return array
+ */
+ public function getMd5()
+ {
+ return $this->getHash();
+ }
+
+ /**
+ * Sets the md5 hash for one or multiple files
+ *
+ * @param string|array $options
+ * @param string $algorithm (Deprecated) Algorithm to use, fixed to md5
+ * @return Zend_Validate_File_Hash Provides a fluent interface
+ */
+ public function setHash($options)
+ {
+ if (!is_array($options)) {
+ $options = (array) $options;
+ }
+
+ $options['algorithm'] = 'md5';
+ parent::setHash($options);
+ return $this;
+ }
+
+ /**
+ * Sets the md5 hash for one or multiple files
+ *
+ * @param string|array $options
+ * @return Zend_Validate_File_Hash Provides a fluent interface
+ */
+ public function setMd5($options)
+ {
+ $this->setHash($options);
+ return $this;
+ }
+
+ /**
+ * Adds the md5 hash for one or multiple files
+ *
+ * @param string|array $options
+ * @param string $algorithm (Depreciated) Algorithm to use, fixed to md5
+ * @return Zend_Validate_File_Hash Provides a fluent interface
+ */
+ public function addHash($options)
+ {
+ if (!is_array($options)) {
+ $options = (array) $options;
+ }
+
+ $options['algorithm'] = 'md5';
+ parent::addHash($options);
+ return $this;
+ }
+
+ /**
+ * Adds the md5 hash for one or multiple files
+ *
+ * @param string|array $options
+ * @return Zend_Validate_File_Hash Provides a fluent interface
+ */
+ public function addMd5($options)
+ {
+ $this->addHash($options);
+ return $this;
+ }
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if and only if the given file confirms the set hash
+ *
+ * @param string $value Filename to check for hash
+ * @param array $file File data from Zend_File_Transfer
+ * @return boolean
+ */
+ public function isValid($value, $file = null)
+ {
+ // Is file readable ?
+ require_once 'Zend/Loader.php';
+ if (!Zend_Loader::isReadable($value)) {
+ return $this->_throw($file, self::NOT_FOUND);
+ }
+
+ $hashes = array_unique(array_keys($this->_hash));
+ $filehash = hash_file('md5', $value);
+ if ($filehash === false) {
+ return $this->_throw($file, self::NOT_DETECTED);
+ }
+
+ foreach($hashes as $hash) {
+ if ($filehash === $hash) {
+ return true;
+ }
+ }
+
+ return $this->_throw($file, self::DOES_NOT_MATCH);
+ }
+}
diff --git a/lib/Zend/Validate/File/MimeType.php b/lib/Zend/Validate/File/MimeType.php
new file mode 100644
index 0000000..1605dad
--- /dev/null
+++ b/lib/Zend/Validate/File/MimeType.php
@@ -0,0 +1,283 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * Validator for the mime type of a file
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_File_MimeType extends Zend_Validate_Abstract
+{
+ /**#@+
+ * @const Error type constants
+ */
+ const FALSE_TYPE = 'fileMimeTypeFalse';
+ const NOT_DETECTED = 'fileMimeTypeNotDetected';
+ const NOT_READABLE = 'fileMimeTypeNotReadable';
+ /**#@-*/
+
+ /**
+ * @var array Error message templates
+ */
+ protected $_messageTemplates = array(
+ self::FALSE_TYPE => "The file '%value%' has a false mimetype of '%type%'",
+ self::NOT_DETECTED => "The mimetype of file '%value%' could not been detected",
+ self::NOT_READABLE => "The file '%value%' can not be read"
+ );
+
+ /**
+ * @var array
+ */
+ protected $_messageVariables = array(
+ 'type' => '_type'
+ );
+
+ /**
+ * @var string
+ */
+ protected $_type;
+
+ /**
+ * Mimetypes
+ *
+ * If null, there is no mimetype
+ *
+ * @var string|null
+ */
+ protected $_mimetype;
+
+ /**
+ * Magicfile to use
+ *
+ * @var string|null
+ */
+ protected $_magicfile;
+
+ /**
+ * Sets validator options
+ *
+ * Mimetype to accept
+ *
+ * @param string|array $mimetype MimeType
+ * @return void
+ */
+ public function __construct($mimetype)
+ {
+ if ($mimetype instanceof Zend_Config) {
+ $mimetype = $mimetype->toArray();
+ } elseif (is_string($mimetype)) {
+ $mimetype = explode(',', $mimetype);
+ } elseif (!is_array($mimetype)) {
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception("Invalid options to validator provided");
+ }
+
+ if (isset($mimetype['magicfile'])) {
+ $this->setMagicFile($mimetype['magicfile']);
+ }
+
+ $this->setMimeType($mimetype);
+ }
+
+ /**
+ * Returna the actual set magicfile
+ *
+ * @return string
+ */
+ public function getMagicFile()
+ {
+ return $this->_magicfile;
+ }
+
+ /**
+ * Sets the magicfile to use
+ * if null, the MAGIC constant from php is used
+ *
+ * @param string $file
+ * @return Zend_Validate_File_MimeType Provides fluid interface
+ */
+ public function setMagicFile($file)
+ {
+ if (empty($file)) {
+ $this->_magicfile = null;
+ } else if (!is_readable($file)) {
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception('The given magicfile can not be read');
+ } else {
+ $this->_magicfile = (string) $file;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns the set mimetypes
+ *
+ * @param boolean $asArray Returns the values as array, when false an concated string is returned
+ * @return string|array
+ */
+ public function getMimeType($asArray = false)
+ {
+ $asArray = (bool) $asArray;
+ $mimetype = (string) $this->_mimetype;
+ if ($asArray) {
+ $mimetype = explode(',', $mimetype);
+ }
+
+ return $mimetype;
+ }
+
+ /**
+ * Sets the mimetypes
+ *
+ * @param string|array $mimetype The mimetypes to validate
+ * @return Zend_Validate_File_Extension Provides a fluent interface
+ */
+ public function setMimeType($mimetype)
+ {
+ $this->_mimetype = null;
+ $this->addMimeType($mimetype);
+ return $this;
+ }
+
+ /**
+ * Adds the mimetypes
+ *
+ * @param string|array $mimetype The mimetypes to add for validation
+ * @return Zend_Validate_File_Extension Provides a fluent interface
+ */
+ public function addMimeType($mimetype)
+ {
+ $mimetypes = $this->getMimeType(true);
+
+ if (is_string($mimetype)) {
+ $mimetype = explode(',', $mimetype);
+ } elseif (!is_array($mimetype)) {
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception("Invalid options to validator provided");
+ }
+
+ if (isset($mimetype['magicfile'])) {
+ unset($mimetype['magicfile']);
+ }
+
+ foreach ($mimetype as $content) {
+ if (empty($content) || !is_string($content)) {
+ continue;
+ }
+ $mimetypes[] = trim($content);
+ }
+ $mimetypes = array_unique($mimetypes);
+
+ // Sanity check to ensure no empty values
+ foreach ($mimetypes as $key => $mt) {
+ if (empty($mt)) {
+ unset($mimetypes[$key]);
+ }
+ }
+
+ $this->_mimetype = implode(',', $mimetypes);
+
+ return $this;
+ }
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if the mimetype of the file matches the given ones. Also parts
+ * of mimetypes can be checked. If you give for example "image" all image
+ * mime types will be accepted like "image/gif", "image/jpeg" and so on.
+ *
+ * @param string $value Real file to check for mimetype
+ * @param array $file File data from Zend_File_Transfer
+ * @return boolean
+ */
+ public function isValid($value, $file = null)
+ {
+ // Is file readable ?
+ require_once 'Zend/Loader.php';
+ if (!Zend_Loader::isReadable($value)) {
+ return $this->_throw($file, self::NOT_READABLE);
+ }
+
+ if ($file !== null) {
+ $mimefile = $this->getMagicFile();
+ if (class_exists('finfo', false) && ((!empty($mimefile)) or (defined('MAGIC')))) {
+ if (!empty($mimefile)) {
+ $mime = new finfo(FILEINFO_MIME, $mimefile);
+ } else {
+ $mime = new finfo(FILEINFO_MIME);
+ }
+
+ $this->_type = $mime->file($value);
+ unset($mime);
+ } elseif (function_exists('mime_content_type') && ini_get('mime_magic.magicfile')) {
+ $this->_type = mime_content_type($value);
+ } else {
+ $this->_type = $file['type'];
+ }
+ }
+
+ if (empty($this->_type)) {
+ return $this->_throw($file, self::NOT_DETECTED);
+ }
+
+ $mimetype = $this->getMimeType(true);
+ if (in_array($this->_type, $mimetype)) {
+ return true;
+ }
+
+ $types = explode('/', $this->_type);
+ $types = array_merge($types, explode('-', $this->_type));
+ foreach($mimetype as $mime) {
+ if (in_array($mime, $types)) {
+ return true;
+ }
+ }
+
+ return $this->_throw($file, self::FALSE_TYPE);
+ }
+
+ /**
+ * Throws an error of the given type
+ *
+ * @param string $file
+ * @param string $errorType
+ * @return false
+ */
+ protected function _throw($file, $errorType)
+ {
+ if ($file !== null) {
+ $this->_value = $file['name'];
+ }
+
+ $this->_error($errorType);
+ return false;
+ }
+}
diff --git a/lib/Zend/Validate/File/NotExists.php b/lib/Zend/Validate/File/NotExists.php
new file mode 100644
index 0000000..8aeef9f
--- /dev/null
+++ b/lib/Zend/Validate/File/NotExists.php
@@ -0,0 +1,84 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: $
+ */
+
+/**
+ * @see Zend_Validate_File_Exists
+ */
+require_once 'Zend/Validate/File/Exists.php';
+
+/**
+ * Validator which checks if the destination file does not exist
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_File_NotExists extends Zend_Validate_File_Exists
+{
+ /**
+ * @const string Error constants
+ */
+ const DOES_EXIST = 'fileNotExistsDoesExist';
+
+ /**
+ * @var array Error message templates
+ */
+ protected $_messageTemplates = array(
+ self::DOES_EXIST => "The file '%value%' does exist"
+ );
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if and only if the file does not exist in the set destinations
+ *
+ * @param string $value Real file to check for
+ * @param array $file File data from Zend_File_Transfer
+ * @return boolean
+ */
+ public function isValid($value, $file = null)
+ {
+ $directories = $this->getDirectory(true);
+ if (($file !== null) and (!empty($file['destination']))) {
+ $directories[] = $file['destination'];
+ } else if (!isset($file['name'])) {
+ $file['name'] = $value;
+ }
+
+ foreach ($directories as $directory) {
+ if (empty($directory)) {
+ continue;
+ }
+
+ $check = true;
+ if (file_exists($directory . DIRECTORY_SEPARATOR . $file['name'])) {
+ return $this->_throw($file, self::DOES_EXIST);
+ }
+ }
+
+ if (!isset($check)) {
+ return $this->_throw($file, self::DOES_EXIST);
+ }
+
+ return true;
+ }
+}
diff --git a/lib/Zend/Validate/File/Sha1.php b/lib/Zend/Validate/File/Sha1.php
new file mode 100644
index 0000000..b02a837
--- /dev/null
+++ b/lib/Zend/Validate/File/Sha1.php
@@ -0,0 +1,181 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: $
+ */
+
+/**
+ * @see Zend_Validate_File_Hash
+ */
+require_once 'Zend/Validate/File/Hash.php';
+
+/**
+ * Validator for the sha1 hash of given files
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_File_Sha1 extends Zend_Validate_File_Hash
+{
+ /**
+ * @const string Error constants
+ */
+ const DOES_NOT_MATCH = 'fileSha1DoesNotMatch';
+ const NOT_DETECTED = 'fileSha1NotDetected';
+ const NOT_FOUND = 'fileSha1NotFound';
+
+ /**
+ * @var array Error message templates
+ */
+ protected $_messageTemplates = array(
+ self::DOES_NOT_MATCH => "The file '%value%' does not match the given sha1 hashes",
+ self::NOT_DETECTED => "There was no sha1 hash detected for the given file",
+ self::NOT_FOUND => "The file '%value%' could not be found"
+ );
+
+ /**
+ * Hash of the file
+ *
+ * @var string
+ */
+ protected $_hash;
+
+ /**
+ * Sets validator options
+ *
+ * $hash is the hash we accept for the file $file
+ *
+ * @param string|array $options
+ * @return void
+ */
+ public function __construct($options)
+ {
+ if ($options instanceof Zend_Config) {
+ $options = $options->toArray();
+ } elseif (is_scalar($options)) {
+ $options = array('hash1' => $options);
+ } elseif (!is_array($options)) {
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception('Invalid options to validator provided');
+ }
+
+ $this->setHash($options);
+ }
+
+ /**
+ * Returns all set sha1 hashes
+ *
+ * @return array
+ */
+ public function getSha1()
+ {
+ return $this->getHash();
+ }
+
+ /**
+ * Sets the sha1 hash for one or multiple files
+ *
+ * @param string|array $options
+ * @return Zend_Validate_File_Hash Provides a fluent interface
+ */
+ public function setHash($options)
+ {
+ if (!is_array($options)) {
+ $options = (array) $options;
+ }
+
+ $options['algorithm'] = 'sha1';
+ parent::setHash($options);
+ return $this;
+ }
+
+ /**
+ * Sets the sha1 hash for one or multiple files
+ *
+ * @param string|array $options
+ * @return Zend_Validate_File_Hash Provides a fluent interface
+ */
+ public function setSha1($options)
+ {
+ $this->setHash($options);
+ return $this;
+ }
+
+ /**
+ * Adds the sha1 hash for one or multiple files
+ *
+ * @param string|array $options
+ * @return Zend_Validate_File_Hash Provides a fluent interface
+ */
+ public function addHash($options)
+ {
+ if (!is_array($options)) {
+ $options = (array) $options;
+ }
+
+ $options['algorithm'] = 'sha1';
+ parent::addHash($options);
+ return $this;
+ }
+
+ /**
+ * Adds the sha1 hash for one or multiple files
+ *
+ * @param string|array $options
+ * @return Zend_Validate_File_Hash Provides a fluent interface
+ */
+ public function addSha1($options)
+ {
+ $this->addHash($options);
+ return $this;
+ }
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if and only if the given file confirms the set hash
+ *
+ * @param string $value Filename to check for hash
+ * @param array $file File data from Zend_File_Transfer
+ * @return boolean
+ */
+ public function isValid($value, $file = null)
+ {
+ // Is file readable ?
+ require_once 'Zend/Loader.php';
+ if (!Zend_Loader::isReadable($value)) {
+ return $this->_throw($file, self::NOT_FOUND);
+ }
+
+ $hashes = array_unique(array_keys($this->_hash));
+ $filehash = hash_file('sha1', $value);
+ if ($filehash === false) {
+ return $this->_throw($file, self::NOT_DETECTED);
+ }
+
+ foreach ($hashes as $hash) {
+ if ($filehash === $hash) {
+ return true;
+ }
+ }
+
+ return $this->_throw($file, self::DOES_NOT_MATCH);
+ }
+}
diff --git a/lib/Zend/Validate/File/Size.php b/lib/Zend/Validate/File/Size.php
new file mode 100644
index 0000000..a52d499
--- /dev/null
+++ b/lib/Zend/Validate/File/Size.php
@@ -0,0 +1,404 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * Validator for the maximum size of a file up to a max of 2GB
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_File_Size extends Zend_Validate_Abstract
+{
+ /**#@+
+ * @const string Error constants
+ */
+ const TOO_BIG = 'fileSizeTooBig';
+ const TOO_SMALL = 'fileSizeTooSmall';
+ const NOT_FOUND = 'fileSizeNotFound';
+ /**#@-*/
+
+ /**
+ * @var array Error message templates
+ */
+ protected $_messageTemplates = array(
+ self::TOO_BIG => "Maximum allowed size for file '%value%' is '%max%' but '%size%' detected",
+ self::TOO_SMALL => "Minimum expected size for file '%value%' is '%min%' but '%size%' detected",
+ self::NOT_FOUND => "The file '%value%' could not be found"
+ );
+
+ /**
+ * @var array Error message template variables
+ */
+ protected $_messageVariables = array(
+ 'min' => '_min',
+ 'max' => '_max',
+ 'size' => '_size',
+ );
+
+ /**
+ * Minimum filesize
+ * @var integer
+ */
+ protected $_min;
+
+ /**
+ * Maximum filesize
+ *
+ * If null, there is no maximum filesize
+ *
+ * @var integer|null
+ */
+ protected $_max;
+
+ /**
+ * Detected size
+ *
+ * @var integer
+ */
+ protected $_size;
+
+ /**
+ * Use bytestring ?
+ *
+ * @var boolean
+ */
+ protected $_useByteString = true;
+
+ /**
+ * Sets validator options
+ *
+ * If $options is a integer, it will be used as maximum filesize
+ * As Array is accepts the following keys:
+ * 'min': Minimum filesize
+ * 'max': Maximum filesize
+ * 'bytestring': Use bytestring or real size for messages
+ *
+ * @param integer|array $options Options for the adapter
+ */
+ public function __construct($options)
+ {
+ if ($options instanceof Zend_Config) {
+ $options = $options->toArray();
+ } elseif (is_string($options) || is_numeric($options)) {
+ $options = array('max' => $options);
+ } elseif (!is_array($options)) {
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception ('Invalid options to validator provided');
+ }
+
+ if (1 < func_num_args()) {
+ trigger_error('Multiple constructor options are deprecated in favor of a single options array', E_USER_NOTICE);
+ $argv = func_get_args();
+ array_shift($argv);
+ $options['max'] = array_shift($argv);
+ if (!empty($argv)) {
+ $options['bytestring'] = array_shift($argv);
+ }
+ }
+
+ if (isset($options['bytestring'])) {
+ $this->setUseByteString($options['bytestring']);
+ }
+
+ if (isset($options['min'])) {
+ $this->setMin($options['min']);
+ }
+
+ if (isset($options['max'])) {
+ $this->setMax($options['max']);
+ }
+ }
+
+ /**
+ * Returns the minimum filesize
+ *
+ * @param boolean $byteString Use bytestring ?
+ * @return integer
+ */
+ public function setUseByteString($byteString = true)
+ {
+ $this->_useByteString = (bool) $byteString;
+ return $this;
+ }
+
+ /**
+ * Will bytestring be used?
+ *
+ * @return boolean
+ */
+ public function useByteString()
+ {
+ return $this->_useByteString;
+ }
+
+ /**
+ * Returns the minimum filesize
+ *
+ * @param bool $raw Whether or not to force return of the raw value (defaults off)
+ * @return integer|string
+ */
+ public function getMin($raw = false)
+ {
+ $min = $this->_min;
+ if (!$raw && $this->useByteString()) {
+ $min = $this->_toByteString($min);
+ }
+
+ return $min;
+ }
+
+ /**
+ * Sets the minimum filesize
+ *
+ * @param integer $min The minimum filesize
+ * @throws Zend_Validate_Exception When min is greater than max
+ * @return Zend_Validate_File_Size Provides a fluent interface
+ */
+ public function setMin($min)
+ {
+ if (!is_string($min) and !is_numeric($min)) {
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception ('Invalid options to validator provided');
+ }
+
+ $min = (integer) $this->_fromByteString($min);
+ $max = $this->getMax(true);
+ if (($max !== null) && ($min > $max)) {
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception("The minimum must be less than or equal to the maximum filesize, but $min >"
+ . " $max");
+ }
+
+ $this->_min = $min;
+ return $this;
+ }
+
+ /**
+ * Returns the maximum filesize
+ *
+ * @param bool $raw Whether or not to force return of the raw value (defaults off)
+ * @return integer|string
+ */
+ public function getMax($raw = false)
+ {
+ $max = $this->_max;
+ if (!$raw && $this->useByteString()) {
+ $max = $this->_toByteString($max);
+ }
+
+ return $max;
+ }
+
+ /**
+ * Sets the maximum filesize
+ *
+ * @param integer $max The maximum filesize
+ * @throws Zend_Validate_Exception When max is smaller than min
+ * @return Zend_Validate_StringLength Provides a fluent interface
+ */
+ public function setMax($max)
+ {
+ if (!is_string($max) && !is_numeric($max)) {
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception ('Invalid options to validator provided');
+ }
+
+ $max = (integer) $this->_fromByteString($max);
+ $min = $this->getMin(true);
+ if (($min !== null) && ($max < $min)) {
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception("The maximum must be greater than or equal to the minimum filesize, but "
+ . "$max < $min");
+ }
+
+ $this->_max = $max;
+ return $this;
+ }
+
+ /**
+ * Retrieve current detected file size
+ *
+ * @return int
+ */
+ protected function _getSize()
+ {
+ return $this->_size;
+ }
+
+ /**
+ * Set current size
+ *
+ * @param int $size
+ * @return Zend_Validate_File_Size
+ */
+ protected function _setSize($size)
+ {
+ $this->_size = $size;
+ return $this;
+ }
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if and only if the filesize of $value is at least min and
+ * not bigger than max (when max is not null).
+ *
+ * @param string $value Real file to check for size
+ * @param array $file File data from Zend_File_Transfer
+ * @return boolean
+ */
+ public function isValid($value, $file = null)
+ {
+ // Is file readable ?
+ require_once 'Zend/Loader.php';
+ if (!Zend_Loader::isReadable($value)) {
+ return $this->_throw($file, self::NOT_FOUND);
+ }
+
+ // limited to 4GB files
+ $size = sprintf("%u", @filesize($value));
+
+ // Check to see if it's smaller than min size
+ $min = $this->getMin(true);
+ $max = $this->getMax(true);
+ if (($min !== null) && ($size < $min)) {
+ if ($this->useByteString()) {
+ $this->_min = $this->_toByteString($min);
+ $this->_size = $this->_toByteString($size);
+ $this->_throw($file, self::TOO_SMALL);
+ $this->_min = $min;
+ $this->_size = $size;
+ } else {
+ $this->_throw($file, self::TOO_SMALL);
+ }
+ }
+
+ // Check to see if it's larger than max size
+ if (($max !== null) && ($max < $size)) {
+ if ($this->useByteString()) {
+ $this->_max = $this->_toByteString($max);
+ $this->_size = $this->_toByteString($size);
+ $this->_throw($file, self::TOO_BIG);
+ $this->_max = $max;
+ $this->_size = $size;
+ } else {
+ $this->_throw($file, self::TOO_BIG);
+ }
+ }
+
+ if (count($this->_messages) > 0) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns the formatted size
+ *
+ * @param integer $size
+ * @return string
+ */
+ protected function _toByteString($size)
+ {
+ $sizes = array('B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB');
+ for ($i=0; $size >= 1024 && $i < 9; $i++) {
+ $size /= 1024;
+ }
+
+ return round($size, 2) . $sizes[$i];
+ }
+
+ /**
+ * Returns the unformatted size
+ *
+ * @param string $size
+ * @return integer
+ */
+ protected function _fromByteString($size)
+ {
+ if (is_numeric($size)) {
+ return (integer) $size;
+ }
+
+ $type = trim(substr($size, -2, 1));
+
+ $value = substr($size, 0, -1);
+ if (!is_numeric($value)) {
+ $value = substr($value, 0, -1);
+ }
+
+ switch (strtoupper($type)) {
+ case 'Y':
+ $value *= (1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024);
+ break;
+ case 'Z':
+ $value *= (1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024);
+ break;
+ case 'E':
+ $value *= (1024 * 1024 * 1024 * 1024 * 1024 * 1024);
+ break;
+ case 'P':
+ $value *= (1024 * 1024 * 1024 * 1024 * 1024);
+ break;
+ case 'T':
+ $value *= (1024 * 1024 * 1024 * 1024);
+ break;
+ case 'G':
+ $value *= (1024 * 1024 * 1024);
+ break;
+ case 'M':
+ $value *= (1024 * 1024);
+ break;
+ case 'K':
+ $value *= 1024;
+ break;
+ default:
+ break;
+ }
+
+ return $value;
+ }
+
+ /**
+ * Throws an error of the given type
+ *
+ * @param string $file
+ * @param string $errorType
+ * @return false
+ */
+ protected function _throw($file, $errorType)
+ {
+ if ($file !== null) {
+ $this->_value = $file['name'];
+ }
+
+ $this->_error($errorType);
+ return false;
+ }
+}
diff --git a/lib/Zend/Validate/File/Upload.php b/lib/Zend/Validate/File/Upload.php
new file mode 100644
index 0000000..ddeb328
--- /dev/null
+++ b/lib/Zend/Validate/File/Upload.php
@@ -0,0 +1,234 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * Validator for the maximum size of a file up to a max of 2GB
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_File_Upload extends Zend_Validate_Abstract
+{
+ /**@#+
+ * @const string Error constants
+ */
+ const INI_SIZE = 'fileUploadErrorIniSize';
+ const FORM_SIZE = 'fileUploadErrorFormSize';
+ const PARTIAL = 'fileUploadErrorPartial';
+ const NO_FILE = 'fileUploadErrorNoFile';
+ const NO_TMP_DIR = 'fileUploadErrorNoTmpDir';
+ const CANT_WRITE = 'fileUploadErrorCantWrite';
+ const EXTENSION = 'fileUploadErrorExtension';
+ const ATTACK = 'fileUploadErrorAttack';
+ const FILE_NOT_FOUND = 'fileUploadErrorFileNotFound';
+ const UNKNOWN = 'fileUploadErrorUnknown';
+ /**@#-*/
+
+ /**
+ * @var array Error message templates
+ */
+ protected $_messageTemplates = array(
+ self::INI_SIZE => "The file '%value%' exceeds the defined ini size",
+ self::FORM_SIZE => "The file '%value%' exceeds the defined form size",
+ self::PARTIAL => "The file '%value%' was only partially uploaded",
+ self::NO_FILE => "The file '%value%' was not uploaded",
+ self::NO_TMP_DIR => "No temporary directory was found for the file '%value%'",
+ self::CANT_WRITE => "The file '%value%' can't be written",
+ self::EXTENSION => "The extension returned an error while uploading the file '%value%'",
+ self::ATTACK => "The file '%value%' was illegal uploaded, possible attack",
+ self::FILE_NOT_FOUND => "The file '%value%' was not found",
+ self::UNKNOWN => "Unknown error while uploading the file '%value%'"
+ );
+
+ /**
+ * Internal array of files
+ * @var array
+ */
+ protected $_files = array();
+
+ /**
+ * Sets validator options
+ *
+ * The array $files must be given in syntax of Zend_File_Transfer to be checked
+ * If no files are given the $_FILES array will be used automatically.
+ * NOTE: This validator will only work with HTTP POST uploads!
+ *
+ * @param array $files Array of files in syntax of Zend_File_Transfer
+ * @return void
+ */
+ public function __construct($files = array())
+ {
+ $this->setFiles($files);
+ }
+
+ /**
+ * Returns the array of set files
+ *
+ * @param string $files (Optional) The file to return in detail
+ * @return array
+ * @throws Zend_Validate_Exception If file is not found
+ */
+ public function getFiles($file = null)
+ {
+ if ($file !== null) {
+ $return = array();
+ foreach ($this->_files as $name => $content) {
+ if ($name === $file) {
+ $return[$file] = $this->_files[$name];
+ }
+
+ if ($content['name'] === $file) {
+ $return[$name] = $this->_files[$name];
+ }
+ }
+
+ if (count($return) === 0) {
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception("The file '$file' was not found");
+ }
+
+ return $return;
+ }
+
+ return $this->_files;
+ }
+
+ /**
+ * Sets the minimum filesize
+ *
+ * @param array $files The files to check in syntax of Zend_File_Transfer
+ * @return Zend_Validate_File_Upload Provides a fluent interface
+ */
+ public function setFiles($files = array())
+ {
+ if (count($files) === 0) {
+ $this->_files = $_FILES;
+ } else {
+ $this->_files = $files;
+ }
+ return $this;
+ }
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if and only if the file was uploaded without errors
+ *
+ * @param string $value Single file to check for upload errors, when giving null the $_FILES array
+ * from initialization will be used
+ * @return boolean
+ */
+ public function isValid($value, $file = null)
+ {
+ if (array_key_exists($value, $this->_files)) {
+ $files[$value] = $this->_files[$value];
+ } else {
+ foreach ($this->_files as $file => $content) {
+ if ($content['name'] === $value) {
+ $files[$file] = $this->_files[$file];
+ }
+
+ if ($content['tmp_name'] === $value) {
+ $files[$file] = $this->_files[$file];
+ }
+ }
+ }
+
+ if (empty($files)) {
+ return $this->_throw($file, self::FILE_NOT_FOUND);
+ }
+
+ foreach ($files as $file => $content) {
+ $this->_value = $file;
+ switch($content['error']) {
+ case 0:
+ if (!is_uploaded_file($content['tmp_name'])) {
+ $this->_throw($file, self::ATTACK);
+ }
+ break;
+
+ case 1:
+ $this->_throw($file, self::INI_SIZE);
+ break;
+
+ case 2:
+ $this->_throw($file, self::FORM_SIZE);
+ break;
+
+ case 3:
+ $this->_throw($file, self::PARTIAL);
+ break;
+
+ case 4:
+ $this->_throw($file, self::NO_FILE);
+ break;
+
+ case 6:
+ $this->_throw($file, self::NO_TMP_DIR);
+ break;
+
+ case 7:
+ $this->_throw($file, self::CANT_WRITE);
+ break;
+
+ case 8:
+ $this->_throw($file, self::EXTENSION);
+ break;
+
+ default:
+ $this->_throw($file, self::UNKNOWN);
+ break;
+ }
+ }
+
+ if (count($this->_messages) > 0) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Throws an error of the given type
+ *
+ * @param string $file
+ * @param string $errorType
+ * @return false
+ */
+ protected function _throw($file, $errorType)
+ {
+ if ($file !== null) {
+ if (is_array($file) and !empty($file['name'])) {
+ $this->_value = $file['name'];
+ }
+ }
+
+ $this->_error($errorType);
+ return false;
+ }
+}
diff --git a/lib/Zend/Validate/Float.php b/lib/Zend/Validate/Float.php
new file mode 100644
index 0000000..0405161
--- /dev/null
+++ b/lib/Zend/Validate/Float.php
@@ -0,0 +1,75 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Float.php 8714 2008-03-09 20:03:45Z thomas $
+ */
+
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_Float extends Zend_Validate_Abstract
+{
+
+ const NOT_FLOAT = 'notFloat';
+
+ /**
+ * @var array
+ */
+ protected $_messageTemplates = array(
+ self::NOT_FLOAT => "'%value%' does not appear to be a float"
+ );
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if and only if $value is a floating-point value
+ *
+ * @param string $value
+ * @return boolean
+ */
+ public function isValid($value)
+ {
+ $valueString = (string) $value;
+
+ $this->_setValue($valueString);
+
+ $locale = localeconv();
+
+ $valueFiltered = str_replace($locale['thousands_sep'], '', $valueString);
+ $valueFiltered = str_replace($locale['decimal_point'], '.', $valueFiltered);
+
+ if (strval(floatval($valueFiltered)) != $valueFiltered) {
+ $this->_error();
+ return false;
+ }
+
+ return true;
+ }
+
+}
diff --git a/lib/Zend/Validate/GreaterThan.php b/lib/Zend/Validate/GreaterThan.php
new file mode 100644
index 0000000..35e658c
--- /dev/null
+++ b/lib/Zend/Validate/GreaterThan.php
@@ -0,0 +1,114 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: GreaterThan.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_GreaterThan extends Zend_Validate_Abstract
+{
+
+ const NOT_GREATER = 'notGreaterThan';
+
+ /**
+ * @var array
+ */
+ protected $_messageTemplates = array(
+ self::NOT_GREATER => "'%value%' is not greater than '%min%'"
+ );
+
+ /**
+ * @var array
+ */
+ protected $_messageVariables = array(
+ 'min' => '_min'
+ );
+
+ /**
+ * Minimum value
+ *
+ * @var mixed
+ */
+ protected $_min;
+
+ /**
+ * Sets validator options
+ *
+ * @param mixed $min
+ * @return void
+ */
+ public function __construct($min)
+ {
+ $this->setMin($min);
+ }
+
+ /**
+ * Returns the min option
+ *
+ * @return mixed
+ */
+ public function getMin()
+ {
+ return $this->_min;
+ }
+
+ /**
+ * Sets the min option
+ *
+ * @param mixed $min
+ * @return Zend_Validate_GreaterThan Provides a fluent interface
+ */
+ public function setMin($min)
+ {
+ $this->_min = $min;
+ return $this;
+ }
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if and only if $value is greater than min option
+ *
+ * @param mixed $value
+ * @return boolean
+ */
+ public function isValid($value)
+ {
+ $this->_setValue($value);
+
+ if ($this->_min >= $value) {
+ $this->_error();
+ return false;
+ }
+ return true;
+ }
+
+}
diff --git a/lib/Zend/Validate/Hex.php b/lib/Zend/Validate/Hex.php
new file mode 100644
index 0000000..9512eda
--- /dev/null
+++ b/lib/Zend/Validate/Hex.php
@@ -0,0 +1,74 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Hex.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_Hex extends Zend_Validate_Abstract
+{
+ /**
+ * Validation failure message key for when the value contains characters other than hexadecimal digits
+ */
+ const NOT_HEX = 'notHex';
+
+ /**
+ * Validation failure message template definitions
+ *
+ * @var array
+ */
+ protected $_messageTemplates = array(
+ self::NOT_HEX => "'%value%' has not only hexadecimal digit characters"
+ );
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if and only if $value contains only hexadecimal digit characters
+ *
+ * @param string $value
+ * @return boolean
+ */
+ public function isValid($value)
+ {
+ $valueString = (string) $value;
+
+ $this->_setValue($valueString);
+
+ if (!ctype_xdigit($valueString)) {
+ $this->_error();
+ return false;
+ }
+
+ return true;
+ }
+
+}
diff --git a/lib/Zend/Validate/Hostname.php b/lib/Zend/Validate/Hostname.php
new file mode 100644
index 0000000..61b0b23
--- /dev/null
+++ b/lib/Zend/Validate/Hostname.php
@@ -0,0 +1,444 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Hostname.php 12274 2008-11-03 12:50:50Z yoshida@zend.co.jp $
+ */
+
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * @see Zend_Loader
+ */
+require_once 'Zend/Loader.php';
+
+/**
+ * @see Zend_Validate_Ip
+ */
+require_once 'Zend/Validate/Ip.php';
+
+/**
+ * Please note there are two standalone test scripts for testing IDN characters due to problems
+ * with file encoding.
+ *
+ * The first is tests/Zend/Validate/HostnameTestStandalone.php which is designed to be run on
+ * the command line.
+ *
+ * The second is tests/Zend/Validate/HostnameTestForm.php which is designed to be run via HTML
+ * to allow users to test entering UTF-8 characters in a form.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_Hostname extends Zend_Validate_Abstract
+{
+
+ const IP_ADDRESS_NOT_ALLOWED = 'hostnameIpAddressNotAllowed';
+ const UNKNOWN_TLD = 'hostnameUnknownTld';
+ const INVALID_DASH = 'hostnameDashCharacter';
+ const INVALID_HOSTNAME_SCHEMA = 'hostnameInvalidHostnameSchema';
+ const UNDECIPHERABLE_TLD = 'hostnameUndecipherableTld';
+ const INVALID_HOSTNAME = 'hostnameInvalidHostname';
+ const INVALID_LOCAL_NAME = 'hostnameInvalidLocalName';
+ const LOCAL_NAME_NOT_ALLOWED = 'hostnameLocalNameNotAllowed';
+
+ /**
+ * @var array
+ */
+ protected $_messageTemplates = array(
+ self::IP_ADDRESS_NOT_ALLOWED => "'%value%' appears to be an IP address, but IP addresses are not allowed",
+ self::UNKNOWN_TLD => "'%value%' appears to be a DNS hostname but cannot match TLD against known list",
+ self::INVALID_DASH => "'%value%' appears to be a DNS hostname but contains a dash (-) in an invalid position",
+ self::INVALID_HOSTNAME_SCHEMA => "'%value%' appears to be a DNS hostname but cannot match against hostname schema for TLD '%tld%'",
+ self::UNDECIPHERABLE_TLD => "'%value%' appears to be a DNS hostname but cannot extract TLD part",
+ self::INVALID_HOSTNAME => "'%value%' does not match the expected structure for a DNS hostname",
+ self::INVALID_LOCAL_NAME => "'%value%' does not appear to be a valid local network name",
+ self::LOCAL_NAME_NOT_ALLOWED => "'%value%' appears to be a local network name but local network names are not allowed"
+ );
+
+ /**
+ * @var array
+ */
+ protected $_messageVariables = array(
+ 'tld' => '_tld'
+ );
+
+ /**
+ * Allows Internet domain names (e.g., example.com)
+ */
+ const ALLOW_DNS = 1;
+
+ /**
+ * Allows IP addresses
+ */
+ const ALLOW_IP = 2;
+
+ /**
+ * Allows local network names (e.g., localhost, www.localdomain)
+ */
+ const ALLOW_LOCAL = 4;
+
+ /**
+ * Allows all types of hostnames
+ */
+ const ALLOW_ALL = 7;
+
+ /**
+ * Whether IDN domains are validated
+ *
+ * @var boolean
+ */
+ private $_validateIdn = true;
+
+ /**
+ * Whether TLDs are validated against a known list
+ *
+ * @var boolean
+ */
+ private $_validateTld = true;
+
+ /**
+ * Bit field of ALLOW constants; determines which types of hostnames are allowed
+ *
+ * @var integer
+ */
+ protected $_allow;
+
+ /**
+ * Bit field of CHECK constants; determines what additional hostname checks to make
+ *
+ * @var unknown_type
+ */
+ // protected $_check;
+
+ /**
+ * Array of valid top-level-domains
+ *
+ * @var array
+ * @see ftp://data.iana.org/TLD/tlds-alpha-by-domain.txt List of all TLDs by domain
+ */
+ protected $_validTlds = array(
+ 'ac', 'ad', 'ae', 'aero', 'af', 'ag', 'ai', 'al', 'am', 'an', 'ao',
+ 'aq', 'ar', 'arpa', 'as', 'asia', 'at', 'au', 'aw', 'ax', 'az', 'ba', 'bb',
+ 'bd', 'be', 'bf', 'bg', 'bh', 'bi', 'biz', 'bj', 'bm', 'bn', 'bo',
+ 'br', 'bs', 'bt', 'bv', 'bw', 'by', 'bz', 'ca', 'cat', 'cc', 'cd',
+ 'cf', 'cg', 'ch', 'ci', 'ck', 'cl', 'cm', 'cn', 'co', 'com', 'coop',
+ 'cr', 'cu', 'cv', 'cx', 'cy', 'cz', 'de', 'dj', 'dk', 'dm', 'do',
+ 'dz', 'ec', 'edu', 'ee', 'eg', 'er', 'es', 'et', 'eu', 'fi', 'fj',
+ 'fk', 'fm', 'fo', 'fr', 'ga', 'gb', 'gd', 'ge', 'gf', 'gg', 'gh',
+ 'gi', 'gl', 'gm', 'gn', 'gov', 'gp', 'gq', 'gr', 'gs', 'gt', 'gu',
+ 'gw', 'gy', 'hk', 'hm', 'hn', 'hr', 'ht', 'hu', 'id', 'ie', 'il',
+ 'im', 'in', 'info', 'int', 'io', 'iq', 'ir', 'is', 'it', 'je', 'jm',
+ 'jo', 'jobs', 'jp', 'ke', 'kg', 'kh', 'ki', 'km', 'kn', 'kp', 'kr', 'kw',
+ 'ky', 'kz', 'la', 'lb', 'lc', 'li', 'lk', 'lr', 'ls', 'lt', 'lu',
+ 'lv', 'ly', 'ma', 'mc', 'md', 'me', 'mg', 'mh', 'mil', 'mk', 'ml', 'mm',
+ 'mn', 'mo', 'mobi', 'mp', 'mq', 'mr', 'ms', 'mt', 'mu', 'museum', 'mv',
+ 'mw', 'mx', 'my', 'mz', 'na', 'name', 'nc', 'ne', 'net', 'nf', 'ng',
+ 'ni', 'nl', 'no', 'np', 'nr', 'nu', 'nz', 'om', 'org', 'pa', 'pe',
+ 'pf', 'pg', 'ph', 'pk', 'pl', 'pm', 'pn', 'pr', 'pro', 'ps', 'pt',
+ 'pw', 'py', 'qa', 're', 'ro', 'rs', 'ru', 'rw', 'sa', 'sb', 'sc', 'sd',
+ 'se', 'sg', 'sh', 'si', 'sj', 'sk', 'sl', 'sm', 'sn', 'so', 'sr',
+ 'st', 'su', 'sv', 'sy', 'sz', 'tc', 'td', 'tel', 'tf', 'tg', 'th', 'tj',
+ 'tk', 'tl', 'tm', 'tn', 'to', 'tp', 'tr', 'travel', 'tt', 'tv', 'tw',
+ 'tz', 'ua', 'ug', 'uk', 'um', 'us', 'uy', 'uz', 'va', 'vc', 've',
+ 'vg', 'vi', 'vn', 'vu', 'wf', 'ws', 'ye', 'yt', 'yu', 'za', 'zm',
+ 'zw'
+ );
+
+ /**
+ * @var string
+ */
+ protected $_tld;
+
+ /**
+ * Sets validator options
+ *
+ * @param integer $allow OPTIONAL Set what types of hostname to allow (default ALLOW_DNS)
+ * @param boolean $validateIdn OPTIONAL Set whether IDN domains are validated (default true)
+ * @param boolean $validateTld OPTIONAL Set whether the TLD element of a hostname is validated (default true)
+ * @param Zend_Validate_Ip $ipValidator OPTIONAL
+ * @return void
+ * @see http://www.iana.org/cctld/specifications-policies-cctlds-01apr02.htm Technical Specifications for ccTLDs
+ */
+ public function __construct($allow = self::ALLOW_DNS, $validateIdn = true, $validateTld = true, Zend_Validate_Ip $ipValidator = null)
+ {
+ // Set allow options
+ $this->setAllow($allow);
+
+ // Set validation options
+ $this->_validateIdn = $validateIdn;
+ $this->_validateTld = $validateTld;
+
+ $this->setIpValidator($ipValidator);
+ }
+
+ /**
+ * @param Zend_Validate_Ip $ipValidator OPTIONAL
+ * @return void;
+ */
+ public function setIpValidator(Zend_Validate_Ip $ipValidator = null)
+ {
+ if ($ipValidator === null) {
+ $ipValidator = new Zend_Validate_Ip();
+ }
+ $this->_ipValidator = $ipValidator;
+ }
+
+ /**
+ * Returns the allow option
+ *
+ * @return integer
+ */
+ public function getAllow()
+ {
+ return $this->_allow;
+ }
+
+ /**
+ * Sets the allow option
+ *
+ * @param integer $allow
+ * @return Zend_Validate_Hostname Provides a fluent interface
+ */
+ public function setAllow($allow)
+ {
+ $this->_allow = $allow;
+ return $this;
+ }
+
+ /**
+ * Set whether IDN domains are validated
+ *
+ * This only applies when DNS hostnames are validated
+ *
+ * @param boolean $allowed Set allowed to true to validate IDNs, and false to not validate them
+ */
+ public function setValidateIdn ($allowed)
+ {
+ $this->_validateIdn = (bool) $allowed;
+ }
+
+ /**
+ * Set whether the TLD element of a hostname is validated
+ *
+ * This only applies when DNS hostnames are validated
+ *
+ * @param boolean $allowed Set allowed to true to validate TLDs, and false to not validate them
+ */
+ public function setValidateTld ($allowed)
+ {
+ $this->_validateTld = (bool) $allowed;
+ }
+
+ /**
+ * Sets the check option
+ *
+ * @param integer $check
+ * @return Zend_Validate_Hostname Provides a fluent interface
+ */
+ /*
+ public function setCheck($check)
+ {
+ $this->_check = $check;
+ return $this;
+ }
+ */
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if and only if the $value is a valid hostname with respect to the current allow option
+ *
+ * @param string $value
+ * @throws Zend_Validate_Exception if a fatal error occurs for validation process
+ * @return boolean
+ */
+ public function isValid($value)
+ {
+ $valueString = (string) $value;
+
+ $this->_setValue($valueString);
+
+ // Check input against IP address schema
+ if ($this->_ipValidator->setTranslator($this->getTranslator())->isValid($valueString)) {
+ if (!($this->_allow & self::ALLOW_IP)) {
+ $this->_error(self::IP_ADDRESS_NOT_ALLOWED);
+ return false;
+ } else{
+ return true;
+ }
+ }
+
+ // Check input against DNS hostname schema
+ $domainParts = explode('.', $valueString);
+ if ((count($domainParts) > 1) && (strlen($valueString) >= 4) && (strlen($valueString) <= 254)) {
+ $status = false;
+
+ do {
+ // First check TLD
+ if (preg_match('/([a-z]{2,10})$/i', end($domainParts), $matches)) {
+
+ reset($domainParts);
+
+ // Hostname characters are: *(label dot)(label dot label); max 254 chars
+ // label: id-prefix [*ldh{61} id-prefix]; max 63 chars
+ // id-prefix: alpha / digit
+ // ldh: alpha / digit / dash
+
+ // Match TLD against known list
+ $this->_tld = strtolower($matches[1]);
+ if ($this->_validateTld) {
+ if (!in_array($this->_tld, $this->_validTlds)) {
+ $this->_error(self::UNKNOWN_TLD);
+ $status = false;
+ break;
+ }
+ }
+
+ /**
+ * Match against IDN hostnames
+ * @see Zend_Validate_Hostname_Interface
+ */
+ $labelChars = 'a-z0-9';
+ $utf8 = false;
+ $classFile = 'Zend/Validate/Hostname/' . ucfirst($this->_tld) . '.php';
+ if ($this->_validateIdn) {
+ if (Zend_Loader::isReadable($classFile)) {
+
+ // Load additional characters
+ $className = 'Zend_Validate_Hostname_' . ucfirst($this->_tld);
+ Zend_Loader::loadClass($className);
+ $labelChars .= call_user_func(array($className, 'getCharacters'));
+ $utf8 = true;
+ }
+ }
+
+ // Keep label regex short to avoid issues with long patterns when matching IDN hostnames
+ $regexLabel = '/^[' . $labelChars . '\x2d]{1,63}$/i';
+ if ($utf8) {
+ $regexLabel .= 'u';
+ }
+
+ // Check each hostname part
+ $valid = true;
+ foreach ($domainParts as $domainPart) {
+
+ // Check dash (-) does not start, end or appear in 3rd and 4th positions
+ if (strpos($domainPart, '-') === 0 ||
+ (strlen($domainPart) > 2 && strpos($domainPart, '-', 2) == 2 && strpos($domainPart, '-', 3) == 3) ||
+ strrpos($domainPart, '-') === strlen($domainPart) - 1) {
+
+ $this->_error(self::INVALID_DASH);
+ $status = false;
+ break 2;
+ }
+
+ // Check each domain part
+ $status = @preg_match($regexLabel, $domainPart);
+ if ($status === false) {
+ /**
+ * Regex error
+ * @see Zend_Validate_Exception
+ */
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception('Internal error: DNS validation failed');
+ } elseif ($status === 0) {
+ $valid = false;
+ }
+ }
+
+ // If all labels didn't match, the hostname is invalid
+ if (!$valid) {
+ $this->_error(self::INVALID_HOSTNAME_SCHEMA);
+ $status = false;
+ }
+
+ } else {
+ // Hostname not long enough
+ $this->_error(self::UNDECIPHERABLE_TLD);
+ $status = false;
+ }
+ } while (false);
+
+ // If the input passes as an Internet domain name, and domain names are allowed, then the hostname
+ // passes validation
+ if ($status && ($this->_allow & self::ALLOW_DNS)) {
+ return true;
+ }
+ } else {
+ $this->_error(self::INVALID_HOSTNAME);
+ }
+
+ // Check input against local network name schema; last chance to pass validation
+ $regexLocal = '/^(([a-zA-Z0-9\x2d]{1,63}\x2e)*[a-zA-Z0-9\x2d]{1,63}){1,254}$/';
+ $status = @preg_match($regexLocal, $valueString);
+ if (false === $status) {
+ /**
+ * Regex error
+ * @see Zend_Validate_Exception
+ */
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception('Internal error: local network name validation failed');
+ }
+
+ // If the input passes as a local network name, and local network names are allowed, then the
+ // hostname passes validation
+ $allowLocal = $this->_allow & self::ALLOW_LOCAL;
+ if ($status && $allowLocal) {
+ return true;
+ }
+
+ // If the input does not pass as a local network name, add a message
+ if (!$status) {
+ $this->_error(self::INVALID_LOCAL_NAME);
+ }
+
+ // If local network names are not allowed, add a message
+ if ($status && !$allowLocal) {
+ $this->_error(self::LOCAL_NAME_NOT_ALLOWED);
+ }
+
+ return false;
+ }
+
+ /**
+ * Throws an exception if a regex for $type does not exist
+ *
+ * @param string $type
+ * @throws Zend_Validate_Exception
+ * @return Zend_Validate_Hostname Provides a fluent interface
+ */
+ /*
+ protected function _checkRegexType($type)
+ {
+ if (!isset($this->_regex[$type])) {
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception("'$type' must be one of ('" . implode(', ', array_keys($this->_regex))
+ . "')");
+ }
+ return $this;
+ }
+ */
+
+}
diff --git a/lib/Zend/Validate/Hostname/At.php b/lib/Zend/Validate/Hostname/At.php
new file mode 100644
index 0000000..fff6bf2
--- /dev/null
+++ b/lib/Zend/Validate/Hostname/At.php
@@ -0,0 +1,50 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: At.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Validate_Hostname_Interface
+ */
+require_once 'Zend/Validate/Hostname/Interface.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_Hostname_At implements Zend_Validate_Hostname_Interface
+{
+
+ /**
+ * Returns UTF-8 characters allowed in DNS hostnames for the specified Top-Level-Domain
+ *
+ * @see http://www.nic.at/en/service/technical_information/idn/charset_converter/ Austria (.AT)
+ * @return string
+ */
+ static function getCharacters()
+ {
+ return '\x{00EO}-\x{00F6}\x{00F8}-\x{00FF}\x{0153}\x{0161}\x{017E}';
+ }
+
+} \ No newline at end of file
diff --git a/lib/Zend/Validate/Hostname/Ch.php b/lib/Zend/Validate/Hostname/Ch.php
new file mode 100644
index 0000000..72fae7b
--- /dev/null
+++ b/lib/Zend/Validate/Hostname/Ch.php
@@ -0,0 +1,50 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Ch.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Validate_Hostname_Interface
+ */
+require_once 'Zend/Validate/Hostname/Interface.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_Hostname_Ch implements Zend_Validate_Hostname_Interface
+{
+
+ /**
+ * Returns UTF-8 characters allowed in DNS hostnames for the specified Top-Level-Domain
+ *
+ * @see https://nic.switch.ch/reg/ocView.action?res=EF6GW2JBPVTG67DLNIQXU234MN6SC33JNQQGI7L6#anhang1 Switzerland (.CH)
+ * @return string
+ */
+ static function getCharacters()
+ {
+ return '\x{00EO}-\x{00F6}\x{00F8}-\x{00FF}\x{0153}';
+ }
+
+} \ No newline at end of file
diff --git a/lib/Zend/Validate/Hostname/De.php b/lib/Zend/Validate/Hostname/De.php
new file mode 100644
index 0000000..cfea068
--- /dev/null
+++ b/lib/Zend/Validate/Hostname/De.php
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: De.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Validate_Hostname_Interface
+ */
+require_once 'Zend/Validate/Hostname/Interface.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_Hostname_De implements Zend_Validate_Hostname_Interface
+{
+
+ /**
+ * Returns UTF-8 characters allowed in DNS hostnames for the specified Top-Level-Domain
+ *
+ * @see http://www.denic.de/en/domains/idns/liste.html Germany (.DE) alllowed characters
+ * @return string
+ */
+ static function getCharacters()
+ {
+ return '\x{00E1}\x{00E0}\x{0103}\x{00E2}\x{00E5}\x{00E4}\x{00E3}\x{0105}\x{0101}\x{00E6}\x{0107}' .
+ '\x{0109}\x{010D}\x{010B}\x{00E7}\x{010F}\x{0111}\x{00E9}\x{00E8}\x{0115}\x{00EA}\x{011B}' .
+ '\x{00EB}\x{0117}\x{0119}\x{0113}\x{011F}\x{011D}\x{0121}\x{0123}\x{0125}\x{0127}\x{00ED}' .
+ '\x{00EC}\x{012D}\x{00EE}\x{00EF}\x{0129}\x{012F}\x{012B}\x{0131}\x{0135}\x{0137}\x{013A}' .
+ '\x{013E}\x{013C}\x{0142}\x{0144}\x{0148}\x{00F1}\x{0146}\x{014B}\x{00F3}\x{00F2}\x{014F}' .
+ '\x{00F4}\x{00F6}\x{0151}\x{00F5}\x{00F8}\x{014D}\x{0153}\x{0138}\x{0155}\x{0159}\x{0157}' .
+ '\x{015B}\x{015D}\x{0161}\x{015F}\x{0165}\x{0163}\x{0167}\x{00FA}\x{00F9}\x{016D}\x{00FB}' .
+ '\x{016F}\x{00FC}\x{0171}\x{0169}\x{0173}\x{016B}\x{0175}\x{00FD}\x{0177}\x{00FF}\x{017A}' .
+ '\x{017E}\x{017C}\x{00F0}\x{00FE}';
+ }
+
+} \ No newline at end of file
diff --git a/lib/Zend/Validate/Hostname/Fi.php b/lib/Zend/Validate/Hostname/Fi.php
new file mode 100644
index 0000000..a28ba56
--- /dev/null
+++ b/lib/Zend/Validate/Hostname/Fi.php
@@ -0,0 +1,50 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Fi.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Validate_Hostname_Interface
+ */
+require_once 'Zend/Validate/Hostname/Interface.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_Hostname_Fi implements Zend_Validate_Hostname_Interface
+{
+
+ /**
+ * Returns UTF-8 characters allowed in DNS hostnames for the specified Top-Level-Domain
+ *
+ * @see http://www.ficora.fi/en/index/palvelut/fiverkkotunnukset/aakkostenkaytto.html Finland (.FI)
+ * @return string
+ */
+ static function getCharacters()
+ {
+ return '\x{00E5}\x{00E4}\x{00F6}';
+ }
+
+} \ No newline at end of file
diff --git a/lib/Zend/Validate/Hostname/Hu.php b/lib/Zend/Validate/Hostname/Hu.php
new file mode 100644
index 0000000..e6f9795
--- /dev/null
+++ b/lib/Zend/Validate/Hostname/Hu.php
@@ -0,0 +1,50 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Hu.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Validate_Hostname_Interface
+ */
+require_once 'Zend/Validate/Hostname/Interface.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_Hostname_Hu implements Zend_Validate_Hostname_Interface
+{
+
+ /**
+ * Returns UTF-8 characters allowed in DNS hostnames for the specified Top-Level-Domain
+ *
+ * @see http://www.domain.hu/domain/English/szabalyzat.html Hungary (.HU)
+ * @return string
+ */
+ static function getCharacters()
+ {
+ return '\x{00E1}\x{00E9}\x{00ED}\x{00F3}\x{00F6}\x{0151}\x{00FA}\x{00FC}\x{0171}';
+ }
+
+} \ No newline at end of file
diff --git a/lib/Zend/Validate/Hostname/Interface.php b/lib/Zend/Validate/Hostname/Interface.php
new file mode 100644
index 0000000..4426d8b
--- /dev/null
+++ b/lib/Zend/Validate/Hostname/Interface.php
@@ -0,0 +1,52 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Interface.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+interface Zend_Validate_Hostname_Interface
+{
+
+ /**
+ * Returns UTF-8 characters allowed in DNS hostnames for the specified Top-Level-Domain
+ *
+ * UTF-8 characters should be written as four character hex codes \x{XXXX}
+ * For example é (lowercase e with acute) is represented by the hex code \x{00E9}
+ *
+ * You only need to include lower-case equivalents of characters since the hostname
+ * check is case-insensitive
+ *
+ * Please document the supported TLDs in the documentation file at:
+ * manual/en/module_specs/Zend_Validate-Hostname.xml
+ *
+ * @see http://en.wikipedia.org/wiki/Internationalized_domain_name
+ * @see http://www.iana.org/cctld/ Country-Code Top-Level Domains (TLDs)
+ * @see http://www.columbia.edu/kermit/utf8-t1.html UTF-8 characters
+ * @return string
+ */
+ static function getCharacters();
+
+} \ No newline at end of file
diff --git a/lib/Zend/Validate/Hostname/Li.php b/lib/Zend/Validate/Hostname/Li.php
new file mode 100644
index 0000000..0739c93
--- /dev/null
+++ b/lib/Zend/Validate/Hostname/Li.php
@@ -0,0 +1,50 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Li.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Validate_Hostname_Interface
+ */
+require_once 'Zend/Validate/Hostname/Interface.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_Hostname_Li implements Zend_Validate_Hostname_Interface
+{
+
+ /**
+ * Returns UTF-8 characters allowed in DNS hostnames for the specified Top-Level-Domain
+ *
+ * @see https://nic.switch.ch/reg/ocView.action?res=EF6GW2JBPVTG67DLNIQXU234MN6SC33JNQQGI7L6#anhang1 Liechtenstein (.LI)
+ * @return string
+ */
+ static function getCharacters()
+ {
+ return '\x{00EO}-\x{00F6}\x{00F8}-\x{00FF}\x{0153}';
+ }
+
+} \ No newline at end of file
diff --git a/lib/Zend/Validate/Hostname/No.php b/lib/Zend/Validate/Hostname/No.php
new file mode 100644
index 0000000..d0d4c67
--- /dev/null
+++ b/lib/Zend/Validate/Hostname/No.php
@@ -0,0 +1,52 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: No.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Validate_Hostname_Interface
+ */
+require_once 'Zend/Validate/Hostname/Interface.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_Hostname_No implements Zend_Validate_Hostname_Interface
+{
+
+ /**
+ * Returns UTF-8 characters allowed in DNS hostnames for the specified Top-Level-Domain
+ *
+ * @see http://www.norid.no/domeneregistrering/idn/idn_nyetegn.en.html Norway (.NO)
+ * @return string
+ */
+ static function getCharacters()
+ {
+ return '\x00E1\x00E0\x00E4\x010D\x00E7\x0111\x00E9\x00E8\x00EA\x\x014B' .
+ '\x0144\x00F1\x00F3\x00F2\x00F4\x00F6\x0161\x0167\x00FC\x017E\x00E6' .
+ '\x00F8\x00E5';
+ }
+
+}
diff --git a/lib/Zend/Validate/Hostname/Se.php b/lib/Zend/Validate/Hostname/Se.php
new file mode 100644
index 0000000..6f181cb
--- /dev/null
+++ b/lib/Zend/Validate/Hostname/Se.php
@@ -0,0 +1,50 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Se.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Validate_Hostname_Interface
+ */
+require_once 'Zend/Validate/Hostname/Interface.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_Hostname_Se implements Zend_Validate_Hostname_Interface
+{
+
+ /**
+ * Returns UTF-8 characters allowed in DNS hostnames for the specified Top-Level-Domain
+ *
+ * @see http://www.iis.se/english/IDN_campaignsite.shtml?lang=en Sweden (.SE)
+ * @return string
+ */
+ static function getCharacters()
+ {
+ return '\x{00E5}\x{00E4}\x{00F6}\x{00FC}\x{00E9}';
+ }
+
+} \ No newline at end of file
diff --git a/lib/Zend/Validate/Identical.php b/lib/Zend/Validate/Identical.php
new file mode 100644
index 0000000..02a5366
--- /dev/null
+++ b/lib/Zend/Validate/Identical.php
@@ -0,0 +1,117 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Identical.php 8118 2008-02-18 16:10:32Z matthew $
+ */
+
+/** Zend_Validate_Abstract */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_Identical extends Zend_Validate_Abstract
+{
+ /**#@+
+ * Error codes
+ * @const string
+ */
+ const NOT_SAME = 'notSame';
+ const MISSING_TOKEN = 'missingToken';
+ /**#@-*/
+
+ /**
+ * Error messages
+ * @var array
+ */
+ protected $_messageTemplates = array(
+ self::NOT_SAME => 'Tokens do not match',
+ self::MISSING_TOKEN => 'No token was provided to match against',
+ );
+
+ /**
+ * Original token against which to validate
+ * @var string
+ */
+ protected $_token;
+
+ /**
+ * Sets validator options
+ *
+ * @param string $token
+ * @return void
+ */
+ public function __construct($token = null)
+ {
+ if (null !== $token) {
+ $this->setToken($token);
+ }
+ }
+
+ /**
+ * Set token against which to compare
+ *
+ * @param string $token
+ * @return Zend_Validate_Identical
+ */
+ public function setToken($token)
+ {
+ $this->_token = (string) $token;
+ return $this;
+ }
+
+ /**
+ * Retrieve token
+ *
+ * @return string
+ */
+ public function getToken()
+ {
+ return $this->_token;
+ }
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if and only if a token has been set and the provided value
+ * matches that token.
+ *
+ * @param string $value
+ * @return boolean
+ */
+ public function isValid($value)
+ {
+ $this->_setValue($value);
+ $token = $this->getToken();
+
+ if (empty($token)) {
+ $this->_error(self::MISSING_TOKEN);
+ return false;
+ }
+
+ if ($value !== $token) {
+ $this->_error(self::NOT_SAME);
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/lib/Zend/Validate/InArray.php b/lib/Zend/Validate/InArray.php
new file mode 100644
index 0000000..1c7725a
--- /dev/null
+++ b/lib/Zend/Validate/InArray.php
@@ -0,0 +1,138 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: InArray.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_InArray extends Zend_Validate_Abstract
+{
+
+ const NOT_IN_ARRAY = 'notInArray';
+
+ /**
+ * @var array
+ */
+ protected $_messageTemplates = array(
+ self::NOT_IN_ARRAY => "'%value%' was not found in the haystack"
+ );
+
+ /**
+ * Haystack of possible values
+ *
+ * @var array
+ */
+ protected $_haystack;
+
+ /**
+ * Whether a strict in_array() invocation is used
+ *
+ * @var boolean
+ */
+ protected $_strict;
+
+ /**
+ * Sets validator options
+ *
+ * @param array $haystack
+ * @param boolean $strict
+ * @return void
+ */
+ public function __construct(array $haystack, $strict = false)
+ {
+ $this->setHaystack($haystack)
+ ->setStrict($strict);
+ }
+
+ /**
+ * Returns the haystack option
+ *
+ * @return mixed
+ */
+ public function getHaystack()
+ {
+ return $this->_haystack;
+ }
+
+ /**
+ * Sets the haystack option
+ *
+ * @param mixed $haystack
+ * @return Zend_Validate_InArray Provides a fluent interface
+ */
+ public function setHaystack(array $haystack)
+ {
+ $this->_haystack = $haystack;
+ return $this;
+ }
+
+ /**
+ * Returns the strict option
+ *
+ * @return boolean
+ */
+ public function getStrict()
+ {
+ return $this->_strict;
+ }
+
+ /**
+ * Sets the strict option
+ *
+ * @param boolean $strict
+ * @return Zend_Validate_InArray Provides a fluent interface
+ */
+ public function setStrict($strict)
+ {
+ $this->_strict = $strict;
+ return $this;
+ }
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if and only if $value is contained in the haystack option. If the strict
+ * option is true, then the type of $value is also checked.
+ *
+ * @param mixed $value
+ * @return boolean
+ */
+ public function isValid($value)
+ {
+ $this->_setValue($value);
+ if (!in_array($value, $this->_haystack, $this->_strict)) {
+ $this->_error();
+ return false;
+ }
+ return true;
+ }
+
+}
diff --git a/lib/Zend/Validate/Int.php b/lib/Zend/Validate/Int.php
new file mode 100644
index 0000000..1fa0744
--- /dev/null
+++ b/lib/Zend/Validate/Int.php
@@ -0,0 +1,73 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Int.php 12336 2008-11-06 19:11:46Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_Int extends Zend_Validate_Abstract
+{
+ const NOT_INT = 'notInt';
+
+ /**
+ * @var array
+ */
+ protected $_messageTemplates = array(
+ self::NOT_INT => "'%value%' does not appear to be an integer"
+ );
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if and only if $value is a valid integer
+ *
+ * @param string $value
+ * @return boolean
+ */
+ public function isValid($value)
+ {
+ $valueString = (string) $value;
+ $this->_setValue($valueString);
+ if (is_bool($value)) {
+ $this->_error();
+ return false;
+ }
+
+ $locale = localeconv();
+ $valueFiltered = str_replace($locale['decimal_point'], '.', $valueString);
+ $valueFiltered = str_replace($locale['thousands_sep'], '', $valueFiltered);
+
+ if (strval(intval($valueFiltered)) != $valueFiltered) {
+ $this->_error();
+ return false;
+ }
+
+ return true;
+ }
+
+}
diff --git a/lib/Zend/Validate/Interface.php b/lib/Zend/Validate/Interface.php
new file mode 100644
index 0000000..4fcd525
--- /dev/null
+++ b/lib/Zend/Validate/Interface.php
@@ -0,0 +1,71 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Interface.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+interface Zend_Validate_Interface
+{
+ /**
+ * Returns true if and only if $value meets the validation requirements
+ *
+ * If $value fails validation, then this method returns false, and
+ * getMessages() will return an array of messages that explain why the
+ * validation failed.
+ *
+ * @param mixed $value
+ * @return boolean
+ * @throws Zend_Valid_Exception If validation of $value is impossible
+ */
+ public function isValid($value);
+
+ /**
+ * Returns an array of messages that explain why the most recent isValid()
+ * call returned false. The array keys are validation failure message identifiers,
+ * and the array values are the corresponding human-readable message strings.
+ *
+ * If isValid() was never called or if the most recent isValid() call
+ * returned true, then this method returns an empty array.
+ *
+ * @return array
+ */
+ public function getMessages();
+
+ /**
+ * Returns an array of message codes that explain why a previous isValid() call
+ * returned false.
+ *
+ * If isValid() was never called or if the most recent isValid() call
+ * returned true, then this method returns an empty array.
+ *
+ * This is now the same as calling array_keys() on the return value from getMessages().
+ *
+ * @return array
+ * @deprecated Since 1.5.0
+ */
+ public function getErrors();
+
+}
diff --git a/lib/Zend/Validate/Ip.php b/lib/Zend/Validate/Ip.php
new file mode 100644
index 0000000..67dc95f
--- /dev/null
+++ b/lib/Zend/Validate/Ip.php
@@ -0,0 +1,71 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Ip.php 13289 2008-12-15 23:18:58Z tjohns $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_Ip extends Zend_Validate_Abstract
+{
+ const NOT_IP_ADDRESS = 'notIpAddress';
+
+ /**
+ * @var array
+ */
+ protected $_messageTemplates = array(
+ self::NOT_IP_ADDRESS => "'%value%' does not appear to be a valid IP address"
+ );
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if and only if $value is a valid IP address
+ *
+ * @param mixed $value
+ * @return boolean
+ */
+ public function isValid($value)
+ {
+ $valueString = (string) $value;
+
+ $this->_setValue($valueString);
+
+ if ((ip2long($valueString) === false) || (long2ip(ip2long($valueString)) !== $valueString)) {
+ if (!function_exists('inet_pton')) {
+ $this->_error();
+ return false;
+ } else if ((@inet_pton($value) === false) ||(inet_ntop(@inet_pton($value)) !== $valueString)) {
+ $this->_error();
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+}
diff --git a/lib/Zend/Validate/LessThan.php b/lib/Zend/Validate/LessThan.php
new file mode 100644
index 0000000..9f7b72c
--- /dev/null
+++ b/lib/Zend/Validate/LessThan.php
@@ -0,0 +1,113 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: LessThan.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_LessThan extends Zend_Validate_Abstract
+{
+
+ const NOT_LESS = 'notLessThan';
+
+ /**
+ * @var array
+ */
+ protected $_messageTemplates = array(
+ self::NOT_LESS => "'%value%' is not less than '%max%'"
+ );
+
+ /**
+ * @var array
+ */
+ protected $_messageVariables = array(
+ 'max' => '_max'
+ );
+
+ /**
+ * Maximum value
+ *
+ * @var mixed
+ */
+ protected $_max;
+
+ /**
+ * Sets validator options
+ *
+ * @param mixed $max
+ * @return void
+ */
+ public function __construct($max)
+ {
+ $this->setMax($max);
+ }
+
+ /**
+ * Returns the max option
+ *
+ * @return mixed
+ */
+ public function getMax()
+ {
+ return $this->_max;
+ }
+
+ /**
+ * Sets the max option
+ *
+ * @param mixed $max
+ * @return Zend_Validate_LessThan Provides a fluent interface
+ */
+ public function setMax($max)
+ {
+ $this->_max = $max;
+ return $this;
+ }
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if and only if $value is less than max option
+ *
+ * @param mixed $value
+ * @return boolean
+ */
+ public function isValid($value)
+ {
+ $this->_setValue($value);
+ if ($this->_max <= $value) {
+ $this->_error();
+ return false;
+ }
+ return true;
+ }
+
+}
diff --git a/lib/Zend/Validate/NotEmpty.php b/lib/Zend/Validate/NotEmpty.php
new file mode 100644
index 0000000..ce5b1d3
--- /dev/null
+++ b/lib/Zend/Validate/NotEmpty.php
@@ -0,0 +1,70 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: NotEmpty.php 13249 2008-12-14 19:29:40Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_NotEmpty extends Zend_Validate_Abstract
+{
+ const IS_EMPTY = 'isEmpty';
+
+ /**
+ * @var array
+ */
+ protected $_messageTemplates = array(
+ self::IS_EMPTY => "Value is required and can't be empty"
+ );
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if and only if $value is not an empty value.
+ *
+ * @param string $value
+ * @return boolean
+ */
+ public function isValid($value)
+ {
+ $this->_setValue((string) $value);
+
+ if (is_string($value)
+ && (('' === $value)
+ || preg_match('/^\s+$/s', $value))
+ ) {
+ $this->_error();
+ return false;
+ } elseif (!is_string($value) && empty($value)) {
+ $this->_error();
+ return false;
+ }
+
+ return true;
+ }
+
+}
diff --git a/lib/Zend/Validate/Regex.php b/lib/Zend/Validate/Regex.php
new file mode 100644
index 0000000..1566f07
--- /dev/null
+++ b/lib/Zend/Validate/Regex.php
@@ -0,0 +1,125 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Regex.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+
+/**
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_Regex extends Zend_Validate_Abstract
+{
+
+ const NOT_MATCH = 'regexNotMatch';
+
+ /**
+ * @var array
+ */
+ protected $_messageTemplates = array(
+ self::NOT_MATCH => "'%value%' does not match against pattern '%pattern%'"
+ );
+
+ /**
+ * @var array
+ */
+ protected $_messageVariables = array(
+ 'pattern' => '_pattern'
+ );
+
+ /**
+ * Regular expression pattern
+ *
+ * @var string
+ */
+ protected $_pattern;
+
+ /**
+ * Sets validator options
+ *
+ * @param string $pattern
+ * @return void
+ */
+ public function __construct($pattern)
+ {
+ $this->setPattern($pattern);
+ }
+
+ /**
+ * Returns the pattern option
+ *
+ * @return string
+ */
+ public function getPattern()
+ {
+ return $this->_pattern;
+ }
+
+ /**
+ * Sets the pattern option
+ *
+ * @param string $pattern
+ * @return Zend_Validate_Regex Provides a fluent interface
+ */
+ public function setPattern($pattern)
+ {
+ $this->_pattern = (string) $pattern;
+ return $this;
+ }
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if and only if $value matches against the pattern option
+ *
+ * @param string $value
+ * @throws Zend_Validate_Exception if there is a fatal error in pattern matching
+ * @return boolean
+ */
+ public function isValid($value)
+ {
+ $valueString = (string) $value;
+
+ $this->_setValue($valueString);
+
+ $status = @preg_match($this->_pattern, $valueString);
+ if (false === $status) {
+ /**
+ * @see Zend_Validate_Exception
+ */
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception("Internal error matching pattern '$this->_pattern' against value '$valueString'");
+ }
+ if (!$status) {
+ $this->_error();
+ return false;
+ }
+ return true;
+ }
+
+}
diff --git a/lib/Zend/Validate/StringLength.php b/lib/Zend/Validate/StringLength.php
new file mode 100644
index 0000000..caf3d4d
--- /dev/null
+++ b/lib/Zend/Validate/StringLength.php
@@ -0,0 +1,223 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: StringLength.php 13278 2008-12-15 19:55:17Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * @category Zend
+ * @package Zend_Validate
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Validate_StringLength extends Zend_Validate_Abstract
+{
+ const TOO_SHORT = 'stringLengthTooShort';
+ const TOO_LONG = 'stringLengthTooLong';
+
+ /**
+ * @var array
+ */
+ protected $_messageTemplates = array(
+ self::TOO_SHORT => "'%value%' is less than %min% characters long",
+ self::TOO_LONG => "'%value%' is greater than %max% characters long"
+ );
+
+ /**
+ * @var array
+ */
+ protected $_messageVariables = array(
+ 'min' => '_min',
+ 'max' => '_max'
+ );
+
+ /**
+ * Minimum length
+ *
+ * @var integer
+ */
+ protected $_min;
+
+ /**
+ * Maximum length
+ *
+ * If null, there is no maximum length
+ *
+ * @var integer|null
+ */
+ protected $_max;
+
+ /**
+ * Encoding to use
+ *
+ * @var string|null
+ */
+ protected $_encoding;
+
+ /**
+ * Sets validator options
+ *
+ * @param integer $min
+ * @param integer $max
+ * @return void
+ */
+ public function __construct($min = 0, $max = null, $encoding = null)
+ {
+ $this->setMin($min);
+ $this->setMax($max);
+ $this->setEncoding($encoding);
+ }
+
+ /**
+ * Returns the min option
+ *
+ * @return integer
+ */
+ public function getMin()
+ {
+ return $this->_min;
+ }
+
+ /**
+ * Sets the min option
+ *
+ * @param integer $min
+ * @throws Zend_Validate_Exception
+ * @return Zend_Validate_StringLength Provides a fluent interface
+ */
+ public function setMin($min)
+ {
+ if (null !== $this->_max && $min > $this->_max) {
+ /**
+ * @see Zend_Validate_Exception
+ */
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception("The minimum must be less than or equal to the maximum length, but $min >"
+ . " $this->_max");
+ }
+ $this->_min = max(0, (integer) $min);
+ return $this;
+ }
+
+ /**
+ * Returns the max option
+ *
+ * @return integer|null
+ */
+ public function getMax()
+ {
+ return $this->_max;
+ }
+
+ /**
+ * Sets the max option
+ *
+ * @param integer|null $max
+ * @throws Zend_Validate_Exception
+ * @return Zend_Validate_StringLength Provides a fluent interface
+ */
+ public function setMax($max)
+ {
+ if (null === $max) {
+ $this->_max = null;
+ } else if ($max < $this->_min) {
+ /**
+ * @see Zend_Validate_Exception
+ */
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception("The maximum must be greater than or equal to the minimum length, but "
+ . "$max < $this->_min");
+ } else {
+ $this->_max = (integer) $max;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns the actual encoding
+ *
+ * @return string
+ */
+ public function getEncoding()
+ {
+ return $this->_encoding;
+ }
+
+ /**
+ * Sets a new encoding to use
+ *
+ * @param string $encoding
+ * @return Zend_Validate_StringLength
+ */
+ public function setEncoding($encoding = null)
+ {
+ if ($encoding !== null) {
+ $orig = iconv_get_encoding('internal_encoding');
+ $result = iconv_set_encoding('internal_encoding', $encoding);
+ if (!$result) {
+ require_once 'Zend/Validate/Exception.php';
+ throw new Zend_Validate_Exception('Given encoding not supported on this OS!');
+ }
+
+ iconv_set_encoding('internal_encoding', $orig);
+ }
+
+ $this->_encoding = $encoding;
+ return $this;
+ }
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if and only if the string length of $value is at least the min option and
+ * no greater than the max option (when the max option is not null).
+ *
+ * @param string $value
+ * @return boolean
+ */
+ public function isValid($value)
+ {
+ $valueString = (string) $value;
+ $this->_setValue($valueString);
+ if ($this->_encoding !== null) {
+ $length = iconv_strlen($valueString, $this->_encoding);
+ } else {
+ $length = iconv_strlen($valueString);
+ }
+
+ if ($length < $this->_min) {
+ $this->_error(self::TOO_SHORT);
+ }
+
+ if (null !== $this->_max && $this->_max < $length) {
+ $this->_error(self::TOO_LONG);
+ }
+
+ if (count($this->_messages)) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+}
diff --git a/log.txt b/log.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/log.txt