diff options
author | Luke Shumaker <LukeShu@sbcglobal.net> | 2011-08-01 01:22:36 -0400 |
---|---|---|
committer | Luke Shumaker <LukeShu@sbcglobal.net> | 2011-08-01 01:22:36 -0400 |
commit | 09dfe32eb6b538225686fd6ed0220240010bc574 (patch) | |
tree | 29c1afc5e79519ba8689a3d5d170c312d3cf5033 /src/ext |
initial commit.
Partway through a rewrite. I have some old files I didn't want to entirely delete.
Diffstat (limited to 'src/ext')
-rw-r--r-- | src/ext/GoogleVoice.class.php | 84 | ||||
-rw-r--r-- | src/ext/HTTP_Accept.class.php | 659 | ||||
-rw-r--r-- | src/ext/Identica.class.php | 491 | ||||
-rw-r--r-- | src/ext/MimeMailParser.class.php | 447 | ||||
-rw-r--r-- | src/ext/MimeMailParser_attachment.class.php | 136 | ||||
-rw-r--r-- | src/ext/PasswordHash.class.php | 253 | ||||
-rw-r--r-- | src/ext/README.txt | 16 |
7 files changed, 2086 insertions, 0 deletions
diff --git a/src/ext/GoogleVoice.class.php b/src/ext/GoogleVoice.class.php new file mode 100644 index 0000000..9638416 --- /dev/null +++ b/src/ext/GoogleVoice.class.php @@ -0,0 +1,84 @@ +<?PHP
+/*
+Version 0.2
+License This code is released under the MIT Open Source License. Feel free to do whatever you want with it.
+Author lostleon@gmail.com, http://www.lostleon.com/
+LastUpdate 05/28/2010
+*/
+class GoogleVoice
+{
+ public $username;
+ public $password;
+ public $status;
+ private $lastURL;
+ private $login_auth;
+ private $inboxURL = 'https://www.google.com/voice/m/';
+ private $loginURL = 'https://www.google.com/accounts/ClientLogin';
+ private $smsURL = 'https://www.google.com/voice/m/sendsms';
+
+ public function __construct($username, $password)
+ {
+ $this->username = $username;
+ $this->password = $password;
+ }
+
+ public function getLoginAuth()
+ {
+ $login_param = "accountType=GOOGLE&Email={$this->username}&Passwd={$this->password}&service=grandcentral&source=com.lostleon.GoogleVoiceTool";
+ $ch = curl_init($this->loginURL);
+ curl_setopt($ch, CURLOPT_HEADER, 0);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (iPhone; U; CPU iPhone OS 2_2_1 like Mac OS X; en-us) AppleWebKit/525.18.1 (KHTML, like Gecko) Version/3.1.1 Mobile/5H11 Safari/525.20");
+ curl_setopt($ch, CURLOPT_REFERER, $this->lastURL);
+ curl_setopt($ch, CURLOPT_POST, "application/x-www-form-urlencoded");
+ curl_setopt($ch, CURLOPT_POST, true);
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $login_param);
+ $html = curl_exec($ch);
+ $this->lastURL = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
+ curl_close($ch);
+ $this->login_auth = $this->match('/Auth=([A-z0-9_-]+)/', $html, 1);
+ return $this->login_auth;
+ }
+
+ public function get_rnr_se()
+ {
+ $this->getLoginAuth();
+ $ch = curl_init($this->inboxURL);
+ curl_setopt($ch, CURLOPT_HEADER, 0);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+ $headers = array("Authorization: GoogleLogin auth=".$this->login_auth, 'User-Agent: Mozilla/5.0 (iPhone; U; CPU iPhone OS 2_2_1 like Mac OS X; en-us) AppleWebKit/525.18.1 (KHTML, like Gecko) Version/3.1.1 Mobile/5H11 Safari/525.20');
+ curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
+ $html = curl_exec($ch);
+ $this->lastURL = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
+ curl_close($ch);
+ $_rnr_se = $this->match('!<input.*?name="_rnr_se".*?value="(.*?)"!ms', $html, 1);
+ return $_rnr_se;
+ }
+
+ public function sms($to_phonenumber, $smstxt)
+ {
+ $_rnr_se = $this->get_rnr_se();
+ $sms_param = "id=&c=&number=".urlencode($to_phonenumber)."&smstext=".urlencode($smstxt)."&_rnr_se=".urlencode($_rnr_se);
+ $ch = curl_init($this->smsURL);
+ curl_setopt($ch, CURLOPT_HEADER, 0);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+ $headers = array("Authorization: GoogleLogin auth=".$this->login_auth, 'User-Agent: Mozilla/5.0 (iPhone; U; CPU iPhone OS 2_2_1 like Mac OS X; en-us) AppleWebKit/525.18.1 (KHTML, like Gecko) Version/3.1.1 Mobile/5H11 Safari/525.20');
+ curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
+ curl_setopt($ch, CURLOPT_REFERER, $this->lastURL);
+ curl_setopt($ch, CURLOPT_POST, true);
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $sms_param);
+ $this->status = curl_exec($ch);
+ $this->lastURL = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
+ curl_close($ch);
+ return $this->status;
+ }
+
+ private function match($regex, $str, $out_ary = 0)
+ {
+ return preg_match($regex, $str, $match) == 1 ? $match[$out_ary] : false;
+ }
+}
+?>
diff --git a/src/ext/HTTP_Accept.class.php b/src/ext/HTTP_Accept.class.php new file mode 100644 index 0000000..5efaa5d --- /dev/null +++ b/src/ext/HTTP_Accept.class.php @@ -0,0 +1,659 @@ +<?php + +/** + * HTTP_Accept class for dealing with the HTTP 'Accept' header + * + * PHP versions 4 and 5 + * + * LICENSE: This source file is subject to the MIT License. + * The full text of this license is available at the following URL: + * http://www.opensource.org/licenses/mit-license.php + * + * @category HTTP + * @package HTTP_Accept + * @author Kevin Locke <kwl7@cornell.edu> + * @copyright 2007 Kevin Locke + * @license http://www.opensource.org/licenses/mit-license.php MIT License + * @version SVN: $Id: HTTP_Accept.php 22 2007-10-06 18:46:45Z kevin $ + * @link http://pear.php.net/package/HTTP_Accept + */ + +/** + * HTTP_Accept class for dealing with the HTTP 'Accept' header + * + * This class is intended to be used to parse the HTTP Accept header into + * usable information and provide a simple API for dealing with that + * information. + * + * The parsing of this class is designed to follow RFC 2616 to the letter, + * any deviations from that standard are bugs and should be reported to the + * maintainer. + * + * Often the class will be used very simply as + * <code> + * <?php + * $accept = new HTTP_Accept($_SERVER['HTTP_ACCEPT']); + * if ($accept->getQuality("image/png") > $accept->getQuality("image/jpeg")) + * // Send PNG image + * else + * // Send JPEG image + * ?> + * </code> + * + * However, for browsers which do not accurately describe their preferences, + * it may be necessary to check if a MIME Type is explicitly listed in their + * Accept header, in addition to being preferred to another type + * + * <code> + * <?php + * if ($accept->isMatchExact("application/xhtml+xml")) + * // Client specifically asked for this type at some quality level + * ?> + * </code> + * + * + * @category HTTP + * @package HTTP_Accept + * @access public + * @link http://pear.php.net/package/HTTP_Accept + */ +class HTTP_Accept +{ + /** + * Array of types and their associated parameters, extensions, and quality + * factors, as represented in the Accept: header. + * Indexed by [type][subtype][index], + * and contains 'PARAMS', 'QUALITY', and 'EXTENSIONS' keys for the + * parameter set, quality factor, and extensions set respectively. + * Note: Since type, subtype, and parameters are case-insensitive + * (RFC 2045 5.1) they are stored as lower-case. + * + * @var array + * @access private + */ + var $acceptedtypes = array(); + + /** + * Regular expression to match a token, as defined in RFC 2045 + * + * @var string + * @access private + */ + var $_matchtoken = '(?:[^[:cntrl:]()<>@,;:\\\\"\/\[\]?={} \t]+)'; + + /** + * Regular expression to match a quoted string, as defined in RFC 2045 + * + * @var string + * @access private + */ + var $_matchqstring = '(?:"[^\\\\"]*(?:\\\\.[^\\\\"]*)*")'; + + /** + * Constructs a new HTTP_Accept object + * + * Initializes the HTTP_Accept class with a given accept string + * or creates a new (empty) HTTP_Accept object if no string is given + * + * Note: The behavior is a little strange here to accomodate + * missing headers (to be interpreted as accept all) as well as + * new empty objects which should accept nothing. This means that + * HTTP_Accept("") will be different than HTTP_Accept() + * + * @access public + * @return object HTTP_Accept + * @param string $acceptstring The value of an Accept: header + * Will often be $_SERVER['HTTP_ACCEPT'] + * Note: If get_magic_quotes_gpc is on, + * run stripslashes() on the string first + */ + function HTTP_Accept() + { + if (func_num_args() == 0) { + // User wishes to create empty HTTP_Accept object + $this->acceptedtypes = array( + '*' => array( + '*' => array ( + 0 => array( + 'PARAMS' => array(), + 'QUALITY' => 0, + 'EXTENSIONS' => array() + ) + ) + ) + ); + return; + } + + $acceptstring = trim(func_get_arg(0)); + if (empty($acceptstring)) { + // Accept header empty or not sent, interpret as "*/*" + $this->acceptedtypes = array( + '*' => array( + '*' => array ( + 0 => array( + 'PARAMS' => array(), + 'QUALITY' => 1, + 'EXTENSIONS' => array() + ) + ) + ) + ); + return; + } + + $matches = preg_match_all( + '/\s*('.$this->_matchtoken.')\/' . // typegroup/ + '('.$this->_matchtoken.')' . // subtype + '((?:\s*;\s*'.$this->_matchtoken.'\s*' . // parameter + '(?:=\s*' . // optional =value + '(?:'.$this->_matchqstring.'|'.$this->_matchtoken.'))?)*)/', // value + $acceptstring, $acceptedtypes, + PREG_SET_ORDER); + + if ($matches == 0) { + // Malformed Accept header + $this->acceptedtypes = array( + '*' => array( + '*' => array ( + 0 => array( + 'PARAMS' => array(), + 'QUALITY' => 1, + 'EXTENSIONS' => array() + ) + ) + ) + ); + return; + } + + foreach ($acceptedtypes as $accepted) { + $typefamily = strtolower($accepted[1]); + $subtype = strtolower($accepted[2]); + + // */subtype is invalid according to grammar in section 14.1 + // so we ignore it + if ($typefamily == '*' && $subtype != '*') + continue; + + // Parse all arguments of the form "key=value" + $matches = preg_match_all('/;\s*('.$this->_matchtoken.')\s*' . + '(?:=\s*' . + '('.$this->_matchqstring.'|'. + $this->_matchtoken.'))?/', + $accepted[3], $args, + PREG_SET_ORDER); + + $params = array(); + $quality = -1; + $extensions = array(); + foreach ($args as $arg) { + array_shift($arg); + if (!empty($arg[1])) { + // Strip quotes (Note: Can't use trim() in case "text\"") + $len = strlen($arg[1]); + if ($arg[1][0] == '"' && $arg[1][$len-1] == '"' + && $len > 1) { + $arg[1] = substr($arg[1], 1, $len-2); + $arg[1] = stripslashes($arg[1]); + } + } else if (!isset($arg[1])) { + $arg[1] = null; + } + + // Everything before q=# is a parameter, after is an extension + if ($quality >= 0) { + $extensions[$arg[0]] = $arg[1]; + } else if ($arg[0] == 'q') { + $quality = (float)$arg[1]; + + if ($quality < 0) + $quality = 0; + else if ($quality > 1) + $quality = 1; + } else { + $arg[0] = strtolower($arg[0]); + // Values required for parameters, + // assume empty-string for missing values + if (isset($arg[1])) + $params[$arg[0]] = $arg[1]; + else + $params[$arg[0]] = ""; + } + } + + if ($quality < 0) + $quality = 1; + else if ($quality == 0) + continue; + + if (!isset($this->acceptedtypes[$typefamily])) + $this->acceptedtypes[$typefamily] = array(); + if (!isset($this->acceptedtypes[$typefamily][$subtype])) + $this->acceptedtypes[$typefamily][$subtype] = array(); + + $this->acceptedtypes[$typefamily][$subtype][] = + array('PARAMS' => $params, + 'QUALITY' => $quality, + 'EXTENSIONS' => $extensions); + } + + if (!isset($this->acceptedtypes['*'])) + $this->acceptedtypes['*'] = array(); + if (!isset($this->acceptedtypes['*']['*'])) + $this->acceptedtypes['*']['*'] = array( + 0 => array( + 'PARAMS' => array(), + 'QUALITY' => 0, + 'EXTENSIONS' => array() + ) + ); + } + + /** + * Gets the accepted quality factor for a given MIME Type + * + * Note: If there are multiple best matches + * (e.g. "text/html;level=4;charset=utf-8" matching both "text/html;level=4" + * and "text/html;charset=utf-8"), it returns the lowest quality factor as + * a conservative estimate. Further, if the ambiguity is between parameters + * and extensions (e.g. "text/html;level=4;q=1;ext=foo" matching both + * "text/html;level=4" and "text/html;q=1;ext=foo") the parameters take + * precidence. + * + * Usage Note: If the quality factor for all supported media types is 0, + * RFC 2616 specifies that applications SHOULD send an HTTP 406 (not + * acceptable) response. + * + * @access public + * @return double the quality value for the given MIME Type + * Quality values are in the range [0,1] where 0 means + * "not accepted" and 1 is "perfect quality". + * @param string $mimetype The MIME Type to query ("text/html") + * @param array $params Parameters of Type to query ([level => 4]) + * @param array $extensions Extension parameters to query + */ + function getQuality($mimetype, $params = array(), $extensions = array()) + { + $type = explode("/", $mimetype); + $supertype = strtolower($type[0]); + $subtype = strtolower($type[1]); + + if ($params == null) + $params = array(); + if ($extensions == null) + $extensions = array(); + + if (empty($this->acceptedtypes[$supertype])) { + if ($supertype == '*') + return 0; + else + return $this->getQuality("*/*", $params, $extensions); + } + + if (empty($this->acceptedtypes[$supertype][$subtype])) { + if ($subtype == '*') + return $this->getQuality("*/*", $params, $extensions); + else + return $this->getQuality("$supertype/*", $params, $extensions); + } + + $params = array_change_key_case($params, CASE_LOWER); + + $matches = $this->_findBestMatchIndices($supertype, $subtype, + $params, $extensions); + + if (count($matches) == 0) { + if ($subtype != '*') + return $this->getQuality("$supertype/*", $params, $extensions); + else if ($supertype != '*') + return $this->getQuality("*/*", $params, $extensions); + else + return 0; + } + + $minquality = 1; + foreach ($matches as $match) + if ($this->acceptedtypes[$supertype][$subtype][$match]['QUALITY'] < $minquality) + $minquality = $this->acceptedtypes[$supertype][$subtype][$match]['QUALITY']; + + return $minquality; + } + + /** + * Determines if there is an exact match for the specified MIME Type + * + * @access public + * @return boolean true if there is an exact match to the given + * values, false otherwise. + * @param string $mimetype The MIME Type to query (e.g. "text/html") + * @param array $params Parameters of Type to query (e.g. [level => 4]) + * @param array $extensions Extension parameters to query + */ + function isMatchExact($mimetype, $params = array(), $extensions = array()) + { + $type = explode("/", $mimetype); + $supertype = strtolower($type[0]); + $subtype = strtolower($type[1]); + + if ($params == null) + $params = array(); + if ($extensions == null) + $extensions = array(); + + return $this->_findExactMatchIndex($supertype, $subtype, + $params, $extensions) >= 0; + } + + /** + * Gets a list of all MIME Types explicitly accepted, sorted by quality + * + * @access public + * @return array list of MIME Types explicitly accepted, sorted + * in decreasing order of quality factor + */ + function getTypes() + { + $qvalues = array(); + $types = array(); + foreach ($this->acceptedtypes as $typefamily => $subtypes) { + if ($typefamily == '*') + continue; + + foreach ($subtypes as $subtype => $variants) { + if ($subtype == '*') + continue; + + $maxquality = 0; + foreach ($variants as $variant) + if ($variant['QUALITY'] > $maxquality) + $maxquality = $variant['QUALITY']; + + if ($maxquality > 0) { + $qvalues[] = $maxquality; + $types[] = $typefamily.'/'.$subtype; + } + } + } + + array_multisort($qvalues, SORT_DESC, SORT_NUMERIC, + $types, SORT_DESC, SORT_STRING); + + return $types; + } + + /** + * Gets the parameter sets for a given mime type, sorted by quality. + * Only parameter sets where the extensions set is empty will be returned. + * + * @access public + * @return array list of sets of name=>value parameter pairs + * in decreasing order of quality factor + * @param string $mimetype The MIME Type to query ("text/html") + */ + function getParameterSets($mimetype) + { + $type = explode("/", $mimetype); + $supertype = strtolower($type[0]); + $subtype = strtolower($type[1]); + + if (!isset($this->acceptedtypes[$supertype]) + || !isset($this->acceptedtypes[$supertype][$subtype])) + return array(); + + $qvalues = array(); + $paramsets = array(); + foreach ($this->acceptedtypes[$supertype][$subtype] as $acceptedtype) { + if (count($acceptedtype['EXTENSIONS']) == 0) { + $qvalues[] = $acceptedtype['QUALITY']; + $paramsets[] = $acceptedtype['PARAMS']; + } + } + + array_multisort($qvalues, SORT_DESC, SORT_NUMERIC, + $paramsets, SORT_DESC, SORT_STRING); + + return $paramsets; + } + + /** + * Gets the extension sets for a given mime type, sorted by quality. + * Only extension sets where the parameter set is empty will be returned. + * + * @access public + * @return array list of sets of name=>value extension pairs + * in decreasing order of quality factor + * @param string $mimetype The MIME Type to query ("text/html") + */ + function getExtensionSets($mimetype) + { + $type = explode("/", $mimetype); + $supertype = strtolower($type[0]); + $subtype = strtolower($type[1]); + + if (!isset($this->acceptedtypes[$supertype]) + || !isset($this->acceptedtypes[$supertype][$subtype])) + return array(); + + $qvalues = array(); + $extensionsets = array(); + foreach ($this->acceptedtypes[$supertype][$subtype] as $acceptedtype) { + if (count($acceptedtype['PARAMS']) == 0) { + $qvalues[] = $acceptedtype['QUALITY']; + $extensionsets[] = $acceptedtype['EXTENSIONS']; + } + } + + array_multisort($qvalues, SORT_DESC, SORT_NUMERIC, + $extensionsets, SORT_DESC, SORT_STRING); + + return $extensionsets; + } + + /** + * Adds a type to the set of accepted types + * + * @access public + * @param string $mimetype The MIME Type to add (e.g. "text/html") + * @param double $quality The quality value for the given MIME Type + * Quality values are in the range [0,1] where + * 0 means "not accepted" and 1 is + * "perfect quality". + * @param array $params Parameters of the type to add (e.g. [level => 4]) + * @param array $extensions Extension parameters of the type to add + */ + function addType($mimetype, $quality = 1, + $params = array(), $extensions = array()) + { + $type = explode("/", $mimetype); + $supertype = strtolower($type[0]); + $subtype = strtolower($type[1]); + + if ($params == null) + $params = array(); + if ($extensions == null) + $extensions = array(); + + $index = $this->_findExactMatchIndex($supertype, $subtype, $params, $extensions); + + if ($index >= 0) { + $this->acceptedtypes[$supertype][$subtype][$index]['QUALITY'] = $quality; + } else { + if (!isset($this->acceptedtypes[$supertype])) + $this->acceptedtypes[$supertype] = array(); + if (!isset($this->acceptedtypes[$supertype][$subtype])) + $this->acceptedtypes[$supertype][$subtype] = array(); + + $this->acceptedtypes[$supertype][$subtype][] = + array('PARAMS' => $params, + 'QUALITY' => $quality, + 'EXTENSIONS' => $extensions); + } + } + + /** + * Removes a type from the set of accepted types + * + * @access public + * @param string $mimetype The MIME Type to remove (e.g. "text/html") + * @param array $params Parameters of the type to remove (e.g. [level => 4]) + * @param array $extensions Extension parameters of the type to remove + */ + function removeType($mimetype, $params = array(), $extensions = array()) + { + $type = explode("/", $mimetype); + $supertype = strtolower($type[0]); + $subtype = strtolower($type[1]); + + if ($params == null) + $params = array(); + if ($extensions == null) + $extensions = array(); + + $index = $this->_findExactMatchIndex($supertype, $subtype, $params, $extensions); + + if ($index >= 0) { + $this->acceptedtypes[$supertype][$subtype] = + array_merge(array_slice($this->acceptedtypes[$supertype][$subtype], + 0, $index), + array_slice($this->acceptedtypes[$supertype][$subtype], + $index+1)); + } + } + + /** + * Gets a string representation suitable for use in an HTTP Accept header + * + * @access public + * @return string a string representation of this object + */ + function __toString() + { + $accepted = array(); + $qvalues = array(); + foreach ($this->acceptedtypes as $supertype => $subtypes) { + foreach ($subtypes as $subtype => $entries) { + foreach ($entries as $entry) { + $accepted[] = array('TYPE' => "$supertype/$subtype", + 'QUALITY' => $entry['QUALITY'], + 'PARAMS' => $entry['PARAMS'], + 'EXTENSIONS' => $entry['EXTENSIONS']); + $qvalues[] = $entry['QUALITY']; + } + } + } + + array_multisort($qvalues, SORT_DESC, SORT_NUMERIC, + $accepted); + + $str = ""; + foreach ($accepted as $accept) { + // Skip the catchall value if it is 0, since this is implied + if ($accept['TYPE'] == '*/*' && + $accept['QUALITY'] == 0 && + count($accept['PARAMS']) == 0 && + count($accept['EXTENSIONS'] == 0)) + continue; + + $str = $str.$accept['TYPE'].';'; + + foreach ($accept['PARAMS'] as $param => $value) { + if (preg_match('/^'.$this->_matchtoken.'$/', $value)) + $str = $str.$param.'='.$value.';'; + else + $str = $str.$param.'="'.addcslashes($value,'"\\').'";'; + } + + if ($accept['QUALITY'] < 1 || !empty($accept['EXTENSIONS'])) + $str = $str.'q='.$accept['QUALITY'].';'; + + foreach ($accept['EXTENSIONS'] as $extension => $value) { + if (preg_match('/^'.$this->_matchtoken.'$/', $value)) + $str = $str.$extension.'='.$value.';'; + else + $str = $str.$extension.'="'.addcslashes($value,'"\\').'";'; + } + + $str[strlen($str)-1] = ','; + } + + return rtrim($str, ','); + } + + /** + * Finds the index of an exact match for the specified MIME Type + * + * @access private + * @return int the index of an exact match if found, + * -1 otherwise + * @param string $supertype The general MIME Type to find (e.g. "text") + * @param string $subtype The MIME subtype to find (e.g. "html") + * @param array $params Parameters of Type to find ([level => 4]) + * @param array $extensions Extension parameters to find + */ + function _findExactMatchIndex($supertype, $subtype, $params, $extensions) + { + if (empty($this->acceptedtypes[$supertype]) + || empty($this->acceptedtypes[$supertype][$subtype])) + return -1; + + $params = array_change_key_case($params, CASE_LOWER); + + $parammatches = array(); + foreach ($this->acceptedtypes[$supertype][$subtype] as $index => $typematch) + if ($typematch['PARAMS'] == $params + && $typematch['EXTENSIONS'] == $extensions) + return $index; + + return -1; + } + + /** + * Finds the indices of the best matches for the specified MIME Type + * + * A "match" in this context is an exact type match and no extraneous + * matches for parameters or extensions (so the best match for + * "text/html;level=4" may be "text/html" but not the other way around). + * + * "Best" is interpreted as the entries that match the most + * parameters and extensions (the sum of the number of matches) + * + * @access private + * @return array an array of the indices of the best matches + * (empty if no matches) + * @param string $supertype The general MIME Type to find (e.g. "text") + * @param string $subtype The MIME subtype to find (e.g. "html") + * @param array $params Parameters of Type to find ([level => 4]) + * @param array $extensions Extension parameters to find + */ + function _findBestMatchIndices($supertype, $subtype, $params, $extensions) + { + $bestmatches = array(); + $bestlength = 0; + + if (empty($this->acceptedtypes[$supertype]) + || empty($this->acceptedtypes[$supertype][$subtype])) + return $bestmatches; + + foreach ($this->acceptedtypes[$supertype][$subtype] as $index => $typematch) { + if (count(array_diff_assoc($typematch['PARAMS'], $params)) == 0 + && count(array_diff_assoc($typematch['EXTENSIONS'], + $extensions)) == 0) { + $length = count($typematch['PARAMS']) + + count($typematch['EXTENSIONS']); + + if ($length > $bestlength) { + $bestmatches = array($index); + $bestlength = $length; + } else if ($length == $bestlength) { + $bestmatches[] = $index; + } + } + } + + return $bestmatches; + } +} + +// vim: set ts=4 sts=4 sw=4 et: +?> diff --git a/src/ext/Identica.class.php b/src/ext/Identica.class.php new file mode 100644 index 0000000..a3e62a9 --- /dev/null +++ b/src/ext/Identica.class.php @@ -0,0 +1,491 @@ +<?php +/** + * Copyright (c) <2009> Gianluca Urgese <g.urgese@jasone.it> + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +/** +* The main identica-php class. Create an object to use your Identi.ca account from php. +*/ +class Identica { + /** Username:password format string */ + private $credentials; + + /** Contains the last HTTP status code returned */ + private $http_status; + + /** Contains the last API call */ + private $last_api_call; + + /** Contains the application calling the API */ + private $application_source; + + /** + * Identi.ca class constructor. + * @param username is a alphanumeric string to perform login on Identi.ca. + * @param password is a alphanumeric string to perform login on Identi.ca. + * @param source is the name of your application. + * @return An Identica object to use to perform all the operation. + */ + function Identica($username, $password, $source=false) { + $this->credentials = sprintf("%s:%s", $username, $password); + $this->application_source = $source; + } + + /** + * Returns the 20 most recent statuses from non-protected users who have set a custom user icon. + * @param format is the extension for the result file (xml, json, rss, atom). + * @param since_id returns only statuses with an ID greater than (that is, more recent than) the specified ID. + * @return the public timeline in the specified format. + */ + function getPublicTimeline($format, $since_id = 0) { + $api_call = sprintf("http://identi.ca/api/statuses/public_timeline.%s", $format); + if ($since_id > 0) { + $api_call .= sprintf("?since_id=%d", $since_id); + } + return $this->APICall($api_call); + } + + /** + * Returns the 20 most recent statuses posted by the authenticating user and that user's friends. + * @param format is the extension for the result file (xml, json, rss, atom). + * @param id returns only statuses from specified ID. + * @param since returns only statuses with an ID greater than (that is, more recent than) the specified ID. + * @return the friends timeline in the specified format. + */ + function getFriendsTimeline($format, $id = NULL, $since = NULL) { + if ($id != NULL) { + $api_call = sprintf("http://identi.ca/api/statuses/friends_timeline/%s.%s", $id, $format); + } + else { + $api_call = sprintf("http://identi.ca/api/statuses/friends_timeline.%s", $format); + } + if ($since != NULL) { + $api_call .= sprintf("?since=%s", urlencode($since)); + } + return $this->APICall($api_call, true); + } + + /** + * Returns the 20 most recent statuses posted from the authenticating user. + * @param format is the extension for the result file (xml, json, rss, atom). + * @param id get only statuses from specified ID. + * @param count specifies the number of statuses to retrieve. May not be greater than 200. + * @param since get only statuses with an ID greater than (that is, more recent than) the specified ID. + * @return the 20 most recent statuses posted from the authenticating user. + */ + function getUserTimeline($format, $id = NULL, $count = 20, $since = NULL) { + if ($id != NULL) { + $api_call = sprintf("http://identi.ca/api/statuses/user_timeline/%s.%s", $id, $format); + } + else { + $api_call = sprintf("http://identi.ca/api/statuses/user_timeline.%s", $format); + } + if ($count != 20) { + $api_call .= sprintf("?count=%d", $count); + } + if ($since != NULL) { + $api_call .= sprintf("%ssince=%s", (strpos($api_call, "?count=") === false) ? "?" : "&", urlencode($since)); + } + return $this->APICall($api_call, true); + } + + /** + * Returns a single status, specified by the id parameter below. The status's author will be returned inline. + * @param format is the extension for the result file (xml, json, rss, atom). + * @param id get only statuses from specified ID. + * @return a single status, specified by the id parameter. + */ + function showStatus($format, $id) { + $api_call = sprintf("http://identi.ca/api/statuses/show/%d.%s", $id, $format); + return $this->APICall($api_call); + } + + /** + * Updates the authenticating user's status. Request must be a POST. Statuses over 140 characters will be forceably truncated. + * @param status is the text of your status update. + * @return the current update from authenticating user. + */ + function updateStatus($status) { + $status = urlencode(stripslashes(urldecode($status))); + $api_call = sprintf("http://identi.ca/api/statuses/update.xml?status=%s", $status); + return $this->APICall($api_call, true, true); + } + + /** + * Returns a list of replies from authenticating user. + * @param format is the extension for the result file (xml, json, rss, atom). + * @param page specifies the page of results to retrieve. + * @return list of replies from authenticating user. + */ + function getReplies($format, $page = 0) { + $api_call = sprintf("http://identi.ca/api/statuses/replies.%s", $format); + if ($page) { + $api_call .= sprintf("?page=%d", $page); + } + return $this->APICall($api_call, true); + } + + /** + * Destroys the status specified by the required ID parameter. The authenticating user must be the author of the specified status. + * @param format is the extension for the result file (xml, json, rss, atom). + * @param id the ID of the status to destroy. + * @return a destroyed status specified by the id parameter. + */ + function destroyStatus($format, $id) { + $api_call = sprintf("http://identi.ca/api/statuses/destroy/%d.%s", $id, $format); + return $this->APICall($api_call, true, true); + } + + /** + * Returns a user's friends, each with current status inline. They are ordered by the order in which + * they were added as friends, 100 at a time. Use the page option to access older friends. With no + * user specified, request defaults to the authenticated user's friends. + * @param format is the extension for the result file (xml, json, rss, atom). + * @param id is the ID of the user for whom to request a list of friends. + * @return a user's friends. + */ + function getFriends($format, $id = NULL) { + if ($id != NULL) { + $api_call = sprintf("http://identi.ca/api/statuses/friends/%s.%s", $id, $format); + } + else { + $api_call = sprintf("http://identi.ca/api/statuses/friends.%s", $format); + } + return $this->APICall($api_call, true); + } + + /** + * Returns a user's followers. They are ordered by the order in which they joined Identi.ca, 100 at a time. + * @param format is the extension for the result file (xml, json, rss, atom). + * @param lite specified if status must be show. + * @return a user's followers. + */ + function getFollowers($format, $lite = NULL) { + $api_call = sprintf("http://identi.ca/api/statuses/followers.%s%s", $format, ($lite) ? "?lite=true" : NULL); + return $this->APICall($api_call, true); + } + + /** + * Returns extended information of a given user, specified by ID or email as per the required id parameter. + * @param format is the extension for the result file (xml, json). + * @param id is the ID of specified user. + * @param email is the email of specified user. + * @return extended information of a given user. + */ + function showUser($format, $id, $email = NULL) { + if ($email == NULL) { + $api_call = sprintf("http://identi.ca/api/users/show/%s.%s", $id, $format); + } + else { + $api_call = sprintf("http://identi.ca/api/users/show.xml?email=%s", $email); + } + return $this->APICall($api_call, true); + } + + /** + * Returns a list of the 20 most recent direct messages sent to the authenticating user. The XML + * and JSON versions include detailed information about the sending and recipient users. + * @param format is the extension for the result file (xml, json, rss, atom). + * @param since get only messages from an ID greater than (that is, more recent than) the specified ID. + * @param since_id returns only statuses from the specified ID. + * @param page Specifies the page of direct messages to retrieve. + * @return a list of the 20 most recent direct messages. + */ + function getMessages($format, $since = NULL, $since_id = 0, $page = 1) { + $api_call = sprintf("http://identi.ca/api/direct_messages.%s", $format); + if ($since != NULL) { + $api_call .= sprintf("?since=%s", urlencode($since)); + } + if ($since_id > 0) { + $api_call .= sprintf("%ssince_id=%d", (strpos($api_call, "?since") === false) ? "?" : "&", $since_id); + } + if ($page > 1) { + $api_call .= sprintf("%spage=%d", (strpos($api_call, "?since") === false) ? "?" : "&", $page); + } + return $this->APICall($api_call, true); + } + + /** + * Returns a list of the 20 most recent direct messages sent by the authenticating user. The XML + * and JSON versions include detailed information about the sending and recipient users. + * @param format is the extension for the result file (xml, json, rss, atom). + * @param since get only messages from an ID greater than (that is, more recent than) the specified ID. + * @param since_id returns only statuses from the specified ID. + * @param page specifies the page of direct messages to retrieve. + * @return a list of the 20 most recent sent direct messages. + */ + function getSentMessages($format, $since = NULL, $since_id = 0, $page = 1) { + $api_call = sprintf("http://identi.ca/api/direct_messages/sent.%s", $format); + if ($since != NULL) { + $api_call .= sprintf("?since=%s", urlencode($since)); + } + if ($since_id > 0) { + $api_call .= sprintf("%ssince_id=%d", (strpos($api_call, "?since") === false) ? "?" : "&", $since_id); + } + if ($page > 1) { + $api_call .= sprintf("%spage=%d", (strpos($api_call, "?since") === false) ? "?" : "&", $page); + } + return $this->APICall($api_call, true); + } + + /** + * Sends a new direct message to the specified user from the authenticating user. Request must be a POST. + * @param format is the extension for the result file (xml, json). + * @param user is the ID of specified user to send the message. + * @param text is the text of your direct message. + * @return the sent message in the requested format when successful. + */ + function newMessage($format, $user, $text) { + $text = urlencode(stripslashes(urldecode($text))); + $api_call = sprintf("http://identi.ca/api/direct_messages/new.%s?user=%s&text=%s", $format, $user, $text); + return $this->APICall($api_call, true, true); + } + + /** + * Destroys the direct message specified in the required ID parameter. The authenticating user + * must be the recipient of the specified direct message. + * @param format is the extension for the result file (xml, json). + * @param id is the ID of specified direct message. + * @return the message destroyed. + */ + function destroyMessage($format, $id) { + $api_call = sprintf("http://identi.ca/api/direct_messages/destroy/%s.%s", $id, $format); + return $this->APICall($api_call, true, true); + } + + /** + * Allows the authenticating users to follow the user specified in the ID parameter. + * @param format is the extension for the result file (xml, json). + * @param id is the ID of specified user. + * @return the befriended user in the requested format when successful. Returns a string describing the + * failure condition when unsuccessful. If you are already friends with the user an HTTP 403 will be returned. + */ + function createFriendship($format, $id) { + $api_call = sprintf("http://identi.ca/api/friendships/create/%s.%s", $id, $format); + return $this->APICall($api_call, true, true); + } + + /** + * Allows the authenticating users to unfollow the user specified in the ID parameter. + * @param format is the extension for the result file (xml, json). + * @param id is the ID of specified user. + * @return the unfollowed user in the requested format when successful. Returns a string + * describing the failure condition when unsuccessful. + */ + function destroyFriendship($format, $id) { + $api_call = sprintf("http://identi.ca/api/friendships/destroy/%s.%s", $id, $format); + return $this->APICall($api_call, true, true); + } + + /** + * Tests for the existence of friendship between two users. + * @param format is the extension for the result file (xml, json). + * @param user_a is the ID of the first specified user. + * @param user_b is the ID of the second specified user. + * @return true if user_a follows user_b, otherwise will return false. + */ + function friendshipExists($format, $user_a, $user_b) { + $api_call = sprintf("http://identi.ca/api/friendships/exists.%s?user_a=%s&user_b=%s", $format, $user_a, $user_b); + return $this->APICall($api_call, true); + } + + /** + * Tests if supplied user credentials are valid. + * @param format is the extension for the result file (xml, json). + * @return an HTTP 200 OK response code and a representation of the requesting user if authentication + * was successful; returns a 401 status code and an error message if not. + */ + function verifyCredentials($format = NULL) { + $api_call = sprintf("http://identi.ca/api/account/verify_credentials%s", ($format != NULL) ? sprintf(".%s", $format) : NULL); + return $this->APICall($api_call, true); + } + + /** + * Ends the session of the authenticating user, returning a null cookie. + * @return NULL + */ + function endSession() { + $api_call = "http://identi.ca/api/account/end_session"; + return $this->APICall($api_call, true); + } + + /** + * Update user's location in the profile. + * @param location is the user's location . + * @return NULL. + */ + function updateLocation($format, $location) { + $api_call = sprintf("http://identi.ca/api/account/update_location.%s?location=%s", $format, $location); + return $this->APICall($api_call, true, true); + } + + /** + * Sets which device Identi.ca delivers updates to for the authenticating user. + * @param format is the extension for the result file (xml, json). + * @param device must be one of: sms, im, none. + * @return user's profile details in a selected format. + */ + function updateDeliveryDevice($format, $device) { + $api_call = sprintf("http://identi.ca/api/account/update_delivery_device.%s?device=%s", $format, $device); + return $this->APICall($api_call, true, true); + } + + /** + * Returns the remaining number of API requests available to the requesting user before the API + * limit is reached for the current hour. Calls to rateLimitStatus() do not count against the rate limit. + * @param format is the extension for the result file (xml, json). + * @return remaining number of API requests. + */ + function rateLimitStatus($format) { + $api_call = sprintf("http://identi.ca/api/account/rate_limit_status.%s", $format); + return $this->APICall($api_call, true); + } + + /** + * Returns the 20 most recent favorite statuses for the authenticating user or user + * specified by the ID parameter in the requested format. + * @param format is the extension for the result file (xml, json, rss, atom). + * @param id is the ID of specified user. + * @param page specifies the page of favorites to retrieve. + * @return a list of the 20 most recent favorite statuses. + */ + function getFavorites($format, $id = NULL, $page = 1) { + if ($id == NULL) { + $api_call = sprintf("http://identi.ca/api/favorites.%s", $format); + } + else { + $api_call = sprintf("http://identi.ca/api/favorites/%s.%s", $id, $format); + } + if ($page > 1) { + $api_call .= sprintf("?page=%d", $page); + } + return $this->APICall($api_call, true); + } + + /** + * Favorites the status specified in the ID parameter as the authenticating user. + * @param format is the extension for the result file (xml, json). + * @param id is the ID of specified user. + * @return the favorite status when successful. + */ + function createFavorite($format, $id) { + $api_call = sprintf("http://identi.ca/api/favorites/create/%d.%s", $id, $format); + return $this->APICall($api_call, true, true); + } + + /** + * Un-favorites the status specified in the ID parameter as the authenticating user. + * @param format is the extension for the result file (xml, json). + * @param id is the ID of specified user. + * @return the un-favorited status in the requested format when successful. + */ + function destroyFavorite($format, $id) { + $api_call = sprintf("http://identi.ca/api/favorites/destroy/%d.%s", $id, $format); + return $this->APICall($api_call, true, true); + } + + /** + * Enables device notifications for updates from the specified user. + * @param format is the extension for the result file (xml, json). + * @param id is the ID of specified user. + * @return the specified user when successful. + */ + function follow($format, $id) { + $api_call = sprintf("http://identi.ca/api/notifications/follow/%d.%s", $id, $format); + return $this->APICall($api_call, true, true); + } + + /** + * Disables notifications for updates from the specified user to the authenticating user. + * @param format is the extension for the result file (xml, json). + * @param id is the ID of specified user. + * @return the specified user when successful. + */ + function leave($format, $id) { + $api_call = sprintf("http://identi.ca/api/notifications/leave/%d.%s", $id, $format); + return $this->APICall($api_call, true, true); + } + + /** + * Blocks the user specified in the ID parameter as the authenticating user. Destroys a friendship to the blocked user if it exists. + * @param format is the extension for the result file (xml, json). + * @param id is the ID of specified user. + * @return the blocked user in the requested format when successful. + */ + function createBlock($format, $id) { + $api_call = sprintf("http://identi.ca/api/blocks/create/%d.%s", $id, $format); + return $this->APICall($api_call, true, true); + } + + /** + * Un-blocks the user specified in the ID parameter for the authenticating user. + * @param format is the extension for the result file (xml, json). + * @param id is the ID of specified user. + * @return the un-blocked user in the requested format when successful. + */ + function destroyBlock($format, $id) { + $api_call = sprintf("http://identi.ca/api/blocks/destroy/%d.%s", $id, $format); + return $this->APICall($api_call, true, true); + } + + /** + * Returns true or false in the requested format with a 200 OK HTTP status code. + * @param format is the extension for the result file (xml, json). + * @return test results. + */ + function test($format) { + $api_call = sprintf("http://identi.ca/api/help/test.%s", $format); + return $this->APICall($api_call, true); + } + + private function APICall($api_url, $require_credentials = false, $http_post = false) { + $curl_handle = curl_init(); + if($this->application_source){ + $api_url .= "&source=" . $this->application_source; + } + curl_setopt($curl_handle, CURLOPT_URL, $api_url); + if ($require_credentials) { + curl_setopt($curl_handle, CURLOPT_USERPWD, $this->credentials); + } + if ($http_post) { + curl_setopt($curl_handle, CURLOPT_POST, true); + } + curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, TRUE); + $identica_data = curl_exec($curl_handle); + $this->http_status = curl_getinfo($curl_handle, CURLINFO_HTTP_CODE); + $this->last_api_call = $api_url; + curl_close($curl_handle); + return $identica_data; + } + + function lastStatusCode() { + return $this->http_status; + } + + function lastAPICall() { + return $this->last_api_call; + } +} +?> diff --git a/src/ext/MimeMailParser.class.php b/src/ext/MimeMailParser.class.php new file mode 100644 index 0000000..0080199 --- /dev/null +++ b/src/ext/MimeMailParser.class.php @@ -0,0 +1,447 @@ +<?php
+
+require_once('MimeMailParser_attachment.class.php');
+
+/**
+ * Fast Mime Mail parser Class using PHP's MailParse Extension
+ * @author gabe@fijiwebdesign.com
+ * @url http://www.fijiwebdesign.com/
+ * @license http://creativecommons.org/licenses/by-sa/3.0/us/
+ * @version $Id$
+ */
+class MimeMailParser {
+
+ /**
+ * PHP MimeParser Resource ID
+ */
+ public $resource;
+
+ /**
+ * A file pointer to email
+ */
+ public $stream;
+
+ /**
+ * A text of an email
+ */
+ public $data;
+
+ /**
+ * Stream Resources for Attachments
+ */
+ public $attachment_streams;
+
+ /**
+ * Inialize some stuff
+ * @return
+ */
+ public function __construct() {
+ $this->attachment_streams = array();
+ }
+
+ /**
+ * Free the held resouces
+ * @return void
+ */
+ public function __destruct() {
+ // clear the email file resource
+ if (is_resource($this->stream)) {
+ fclose($this->stream);
+ }
+ // clear the MailParse resource
+ if (is_resource($this->resource)) {
+ mailparse_msg_free($this->resource);
+ }
+ // remove attachment resources
+ foreach($this->attachment_streams as $stream) {
+ fclose($stream);
+ }
+ }
+
+ /**
+ * Set the file path we use to get the email text
+ * @return Object MimeMailParser Instance
+ * @param $mail_path Object
+ */
+ public function setPath($path) {
+ // should parse message incrementally from file
+ $this->resource = mailparse_msg_parse_file($path);
+ $this->stream = fopen($path, 'r');
+ $this->parse();
+ return $this;
+ }
+
+ /**
+ * Set the Stream resource we use to get the email text
+ * @return Object MimeMailParser Instance
+ * @param $stream Resource
+ */
+ public function setStream($stream) {
+
+ // streams have to be cached to file first
+ if (get_resource_type($stream) == 'stream') {
+ $tmp_fp = tmpfile();
+ if ($tmp_fp) {
+ while(!feof($stream)) {
+ fwrite($tmp_fp, fread($stream, 2028));
+ }
+ fseek($tmp_fp, 0);
+ $this->stream =& $tmp_fp;
+ } else {
+ throw new Exception('Could not create temporary files for attachments. Your tmp directory may be unwritable by PHP.');
+ return false;
+ }
+ fclose($stream);
+ } else {
+ $this->stream = $stream;
+ }
+
+ $this->resource = mailparse_msg_create();
+ // parses the message incrementally low memory usage but slower
+ while(!feof($this->stream)) {
+ mailparse_msg_parse($this->resource, fread($this->stream, 2082));
+ }
+ $this->parse();
+ return $this;
+ }
+
+ /**
+ * Set the email text
+ * @return Object MimeMailParser Instance
+ * @param $data String
+ */
+ public function setText($data) {
+ $this->resource = mailparse_msg_create();
+ // does not parse incrementally, fast memory hog might explode
+ mailparse_msg_parse($this->resource, $data);
+ $this->data = $data;
+ $this->parse();
+ return $this;
+ }
+
+ /**
+ * Parse the Message into parts
+ * @return void
+ * @private
+ */
+ private function parse() {
+ $structure = mailparse_msg_get_structure($this->resource);
+ $this->parts = array();
+ foreach($structure as $part_id) {
+ $part = mailparse_msg_get_part($this->resource, $part_id);
+ $this->parts[$part_id] = mailparse_msg_get_part_data($part);
+ }
+ }
+
+ /**
+ * Retrieve the Email Headers
+ * @return Array
+ */
+ public function getHeaders() {
+ if (isset($this->parts[1])) {
+ return $this->getPartHeaders($this->parts[1]);
+ } else {
+ throw new Exception('MimeMailParser::setPath() or MimeMailParser::setText() must be called before retrieving email headers.');
+ }
+ return false;
+ }
+ /**
+ * Retrieve the raw Email Headers
+ * @return string
+ */
+ public function getHeadersRaw() {
+ if (isset($this->parts[1])) {
+ return $this->getPartHeaderRaw($this->parts[1]);
+ } else {
+ throw new Exception('MimeMailParser::setPath() or MimeMailParser::setText() must be called before retrieving email headers.');
+ }
+ return false;
+ }
+
+ /**
+ * Retrieve a specific Email Header
+ * @return String
+ * @param $name String Header name
+ */
+ public function getHeader($name) {
+ if (isset($this->parts[1])) {
+ $headers = $this->getPartHeaders($this->parts[1]);
+ if (isset($headers[$name])) {
+ return $headers[$name];
+ }
+ } else {
+ throw new Exception('MimeMailParser::setPath() or MimeMailParser::setText() must be called before retrieving email headers.');
+ }
+ return false;
+ }
+
+ /**
+ * Returns the part for the message body in the specified format
+ * @return Part or False if not found
+ * @param $type String[optional]
+ */
+ public function getMessageBodyPart($type = 'text') {
+ $mime_types = array(
+ 'text'=> 'text/plain',
+ 'html'=> 'text/html'
+ );
+ $attachment_dispositions = array("attachment","inline");
+ if (in_array($type, array_keys($mime_types))) {
+ foreach($this->parts as $part) {
+ $disposition = $this->getPartContentDisposition($part);
+ $mime_type = $this->getPartContentType($part);
+ if ( (!in_array($disposition, $attachment_dispositions)) &&
+ ($mime_type == $mime_types[$type]) ) {
+ return $part;
+ }
+ }
+ } else {
+ throw new Exception('Invalid type specified for MimeMailParser::getMessageBodyPart. "type" can either be text or html.');
+ }
+ return false;
+ }
+
+ /**
+ * Returns the email message body in the specified format
+ * @return Mixed String Body or False if not found
+ * @param $type Object[optional]
+ */
+ public function getMessageBody($type = 'text') {
+ $body = false;
+ $part = $this->getMessageBodyPart($type);
+ if ($part!==false) {
+ $headers = $this->getPartHeaders($part);
+ $body = $this->decode($this->getPartBody($part), array_key_exists('content-transfer-encoding', $headers) ? $headers['content-transfer-encoding'] : '');
+ }
+ return $body;
+ }
+
+ /**
+ * get the headers for the message body part.
+ * @return Array
+ * @param $type Object[optional]
+ */
+ public function getMessageBodyHeaders($type = 'text') {
+ $headers = false;
+ $part = $this->getMessageBodyPart($type);
+ if ($part!==false) {
+ $headers = $this->getPartHeaders($part);
+ }
+ return $headers;
+ }
+
+
+ /**
+ * Returns the attachments contents in order of appearance
+ * @return Array
+ * @param $type Object[optional]
+ */
+ public function getAttachments() {
+ $attachments = array();
+ $dispositions = array("attachment","inline");
+ foreach($this->parts as $part) {
+ $disposition = $this->getPartContentDisposition($part);
+ if (in_array($disposition, $dispositions)) {
+ $headers = $this->getPartHeaders($part);
+ $attachments[] = new MimeMailParser_attachment(
+ $part['disposition-filename'],
+ $this->getPartContentType($part),
+ $this->getAttachmentStream($part),
+ $disposition,
+ $headers
+ );
+ }
+ }
+ return $attachments;
+ }
+
+ /**
+ * Return the Headers for a MIME part
+ * @return Array
+ * @param $part Array
+ */
+ private function getPartHeaders($part) {
+ if (isset($part['headers'])) {
+ return $part['headers'];
+ }
+ return false;
+ }
+
+ /**
+ * Return a Specific Header for a MIME part
+ * @return Array
+ * @param $part Array
+ * @param $header String Header Name
+ */
+ private function getPartHeader($part, $header) {
+ if (isset($part['headers'][$header])) {
+ return $part['headers'][$header];
+ }
+ return false;
+ }
+
+ /**
+ * Return the ContentType of the MIME part
+ * @return String
+ * @param $part Array
+ */
+ private function getPartContentType($part) {
+ if (isset($part['content-type'])) {
+ return $part['content-type'];
+ }
+ return false;
+ }
+
+ /**
+ * Return the Content Disposition
+ * @return String
+ * @param $part Array
+ */
+ private function getPartContentDisposition($part) {
+ if (isset($part['content-disposition'])) {
+ return $part['content-disposition'];
+ }
+ return false;
+ }
+
+ /**
+ * Retrieve the raw Header of a MIME part
+ * @return String
+ * @param $part Object
+ */
+ private function getPartHeaderRaw(&$part) {
+ $header = '';
+ if ($this->stream) {
+ $header = $this->getPartHeaderFromFile($part);
+ } else if ($this->data) {
+ $header = $this->getPartHeaderFromText($part);
+ } else {
+ throw new Exception('MimeMailParser::setPath() or MimeMailParser::setText() must be called before retrieving email parts.');
+ }
+ return $header;
+ }
+ /**
+ * Retrieve the Body of a MIME part
+ * @return String
+ * @param $part Object
+ */
+ private function getPartBody(&$part) {
+ $body = '';
+ if ($this->stream) {
+ $body = $this->getPartBodyFromFile($part);
+ } else if ($this->data) {
+ $body = $this->getPartBodyFromText($part);
+ } else {
+ throw new Exception('MimeMailParser::setPath() or MimeMailParser::setText() must be called before retrieving email parts.');
+ }
+ return $body;
+ }
+
+ /**
+ * Retrieve the Header from a MIME part from file
+ * @return String Mime Header Part
+ * @param $part Array
+ */
+ private function getPartHeaderFromFile(&$part) {
+ $start = $part['starting-pos'];
+ $end = $part['starting-pos-body'];
+ fseek($this->stream, $start, SEEK_SET);
+ $header = fread($this->stream, $end-$start);
+ return $header;
+ }
+ /**
+ * Retrieve the Body from a MIME part from file
+ * @return String Mime Body Part
+ * @param $part Array
+ */
+ private function getPartBodyFromFile(&$part) {
+ $start = $part['starting-pos-body'];
+ $end = $part['ending-pos-body'];
+ fseek($this->stream, $start, SEEK_SET);
+ $body = fread($this->stream, $end-$start);
+ return $body;
+ }
+
+ /**
+ * Retrieve the Header from a MIME part from text
+ * @return String Mime Header Part
+ * @param $part Array
+ */
+ private function getPartHeaderFromText(&$part) {
+ $start = $part['starting-pos'];
+ $end = $part['starting-pos-body'];
+ $header = substr($this->data, $start, $end-$start);
+ return $header;
+ }
+ /**
+ * Retrieve the Body from a MIME part from text
+ * @return String Mime Body Part
+ * @param $part Array
+ */
+ private function getPartBodyFromText(&$part) {
+ $start = $part['starting-pos-body'];
+ $end = $part['ending-pos-body'];
+ $body = substr($this->data, $start, $end-$start);
+ return $body;
+ }
+
+ /**
+ * Read the attachment Body and save temporary file resource
+ * @return String Mime Body Part
+ * @param $part Array
+ */
+ private function getAttachmentStream(&$part) {
+ $temp_fp = tmpfile();
+
+ array_key_exists('content-transfer-encoding', $part['headers']) ? $encoding = $part['headers']['content-transfer-encoding'] : $encoding = '';
+
+ if ($temp_fp) {
+ if ($this->stream) {
+ $start = $part['starting-pos-body'];
+ $end = $part['ending-pos-body'];
+ fseek($this->stream, $start, SEEK_SET);
+ $len = $end-$start;
+ $written = 0;
+ $write = 2028;
+ $body = '';
+ while($written < $len) {
+ if (($written+$write < $len )) {
+ $write = $len - $written;
+ }
+ $part = fread($this->stream, $write);
+ fwrite($temp_fp, $this->decode($part, $encoding));
+ $written += $write;
+ }
+ } else if ($this->data) {
+ $attachment = $this->decode($this->getPartBodyFromText($part), $encoding);
+ fwrite($temp_fp, $attachment, strlen($attachment));
+ }
+ fseek($temp_fp, 0, SEEK_SET);
+ } else {
+ throw new Exception('Could not create temporary files for attachments. Your tmp directory may be unwritable by PHP.');
+ return false;
+ }
+ return $temp_fp;
+ }
+
+
+ /**
+ * Decode the string depending on encoding type.
+ * @return String the decoded string.
+ * @param $encodedString The string in its original encoded state.
+ * @param $encodingType The encoding type from the Content-Transfer-Encoding header of the part.
+ */
+ private function decode($encodedString, $encodingType) {
+ if (strtolower($encodingType) == 'base64') {
+ return base64_decode($encodedString);
+ } else if (strtolower($encodingType) == 'quoted-printable') {
+ return quoted_printable_decode($encodedString);
+ } else {
+ return $encodedString;
+ }
+ }
+
+}
+
+
+?>
diff --git a/src/ext/MimeMailParser_attachment.class.php b/src/ext/MimeMailParser_attachment.class.php new file mode 100644 index 0000000..6bd327c --- /dev/null +++ b/src/ext/MimeMailParser_attachment.class.php @@ -0,0 +1,136 @@ +<?php
+
+/**
+ * Model of an Attachment
+ */
+class MimeMailParser_attachment {
+
+ /**
+ * @var $filename Filename
+ */
+ public $filename;
+ /**
+ * @var $content_type Mime Type
+ */
+ public $content_type;
+ /**
+ * @var $content File Content
+ */
+ private $content;
+ /**
+ * @var $extension Filename extension
+ */
+ private $extension;
+ /**
+ * @var $content_disposition Content-Disposition (attachment or inline)
+ */
+ public $content_disposition;
+ /**
+ * @var $headers An Array of the attachment headers
+ */
+ public $headers;
+
+ private $stream;
+
+ public function __construct($filename, $content_type, $stream, $content_disposition, $headers) {
+ $this->filename = $filename;
+ $this->content_type = $content_type;
+ $this->stream = $stream;
+ $this->content = null;
+ $this->content_disposition = $content_disposition;
+ $this->headers = $headers;
+ }
+
+ /**
+ * retrieve the attachment filename
+ * @return String
+ */
+ public function getFilename() {
+ return $this->filename;
+ }
+
+ /**
+ * Retrieve the Attachment Content-Type
+ * @return String
+ */
+ public function getContentType() {
+ return $this->content_type;
+ }
+
+ /**
+ * Retrieve the Attachment Content-Disposition
+ * @return String
+ */
+ public function getContentDisposition() {
+ return $this->content_disposition;
+ }
+
+ /**
+ * Retrieve the Attachment Headers
+ * @return String
+ */
+ public function getHeaders() {
+ return $this->headers;
+ }
+
+ /**
+ * Retrieve the file extension
+ * @return String
+ */
+ public function getFileExtension() {
+ if (!$this->extension) {
+ $ext = substr(strrchr($this->filename, '.'), 1);
+ if ($ext == 'gz') {
+ // special case, tar.gz
+ // todo: other special cases?
+ $ext = preg_match("/\.tar\.gz$/i", $ext) ? 'tar.gz' : 'gz';
+ }
+ $this->extension = $ext;
+ }
+ return $this->extension;
+ }
+
+ /**
+ * Read the contents a few bytes at a time until completed
+ * Once read to completion, it always returns false
+ * @return String
+ * @param $bytes Int[optional]
+ */
+ public function read($bytes = 2082) {
+ return feof($this->stream) ? false : fread($this->stream, $bytes);
+ }
+
+ /**
+ * Retrieve the file content in one go
+ * Once you retreive the content you cannot use MimeMailParser_attachment::read()
+ * @return String
+ */
+ public function getContent() {
+ if ($this->content === null) {
+ fseek($this->stream, 0);
+ while(($buf = $this->read()) !== false) {
+ $this->content .= $buf;
+ }
+ }
+ return $this->content;
+ }
+
+ /**
+ * Allow the properties
+ * MimeMailParser_attachment::$name,
+ * MimeMailParser_attachment::$extension
+ * to be retrieved as public properties
+ * @param $name Object
+ */
+ public function __get($name) {
+ if ($name == 'content') {
+ return $this->getContent();
+ } else if ($name == 'extension') {
+ return $this->getFileExtension();
+ }
+ return null;
+ }
+
+}
+
+?>
\ No newline at end of file diff --git a/src/ext/PasswordHash.class.php b/src/ext/PasswordHash.class.php new file mode 100644 index 0000000..12958c7 --- /dev/null +++ b/src/ext/PasswordHash.class.php @@ -0,0 +1,253 @@ +<?php +# +# Portable PHP password hashing framework. +# +# Version 0.3 / genuine. +# +# Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in +# the public domain. Revised in subsequent years, still public domain. +# +# There's absolutely no warranty. +# +# The homepage URL for this framework is: +# +# http://www.openwall.com/phpass/ +# +# Please be sure to update the Version line if you edit this file in any way. +# It is suggested that you leave the main version number intact, but indicate +# your project name (after the slash) and add your own revision information. +# +# Please do not change the "private" password hashing method implemented in +# here, thereby making your hashes incompatible. However, if you must, please +# change the hash type identifier (the "$P$") to something different. +# +# Obviously, since this code is in the public domain, the above are not +# requirements (there can be none), but merely suggestions. +# +class PasswordHash { + var $itoa64; + var $iteration_count_log2; + var $portable_hashes; + var $random_state; + + function PasswordHash($iteration_count_log2, $portable_hashes) + { + $this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; + + if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31) + $iteration_count_log2 = 8; + $this->iteration_count_log2 = $iteration_count_log2; + + $this->portable_hashes = $portable_hashes; + + $this->random_state = microtime(); + if (function_exists('getmypid')) + $this->random_state .= getmypid(); + } + + function get_random_bytes($count) + { + $output = ''; + if (is_readable('/dev/urandom') && + ($fh = @fopen('/dev/urandom', 'rb'))) { + $output = fread($fh, $count); + fclose($fh); + } + + if (strlen($output) < $count) { + $output = ''; + for ($i = 0; $i < $count; $i += 16) { + $this->random_state = + md5(microtime() . $this->random_state); + $output .= + pack('H*', md5($this->random_state)); + } + $output = substr($output, 0, $count); + } + + return $output; + } + + function encode64($input, $count) + { + $output = ''; + $i = 0; + do { + $value = ord($input[$i++]); + $output .= $this->itoa64[$value & 0x3f]; + if ($i < $count) + $value |= ord($input[$i]) << 8; + $output .= $this->itoa64[($value >> 6) & 0x3f]; + if ($i++ >= $count) + break; + if ($i < $count) + $value |= ord($input[$i]) << 16; + $output .= $this->itoa64[($value >> 12) & 0x3f]; + if ($i++ >= $count) + break; + $output .= $this->itoa64[($value >> 18) & 0x3f]; + } while ($i < $count); + + return $output; + } + + function gensalt_private($input) + { + $output = '$P$'; + $output .= $this->itoa64[min($this->iteration_count_log2 + + ((PHP_VERSION >= '5') ? 5 : 3), 30)]; + $output .= $this->encode64($input, 6); + + return $output; + } + + function crypt_private($password, $setting) + { + $output = '*0'; + if (substr($setting, 0, 2) == $output) + $output = '*1'; + + $id = substr($setting, 0, 3); + # We use "$P$", phpBB3 uses "$H$" for the same thing + if ($id != '$P$' && $id != '$H$') + return $output; + + $count_log2 = strpos($this->itoa64, $setting[3]); + if ($count_log2 < 7 || $count_log2 > 30) + return $output; + + $count = 1 << $count_log2; + + $salt = substr($setting, 4, 8); + if (strlen($salt) != 8) + return $output; + + # We're kind of forced to use MD5 here since it's the only + # cryptographic primitive available in all versions of PHP + # currently in use. To implement our own low-level crypto + # in PHP would result in much worse performance and + # consequently in lower iteration counts and hashes that are + # quicker to crack (by non-PHP code). + if (PHP_VERSION >= '5') { + $hash = md5($salt . $password, TRUE); + do { + $hash = md5($hash . $password, TRUE); + } while (--$count); + } else { + $hash = pack('H*', md5($salt . $password)); + do { + $hash = pack('H*', md5($hash . $password)); + } while (--$count); + } + + $output = substr($setting, 0, 12); + $output .= $this->encode64($hash, 16); + + return $output; + } + + function gensalt_extended($input) + { + $count_log2 = min($this->iteration_count_log2 + 8, 24); + # This should be odd to not reveal weak DES keys, and the + # maximum valid value is (2**24 - 1) which is odd anyway. + $count = (1 << $count_log2) - 1; + + $output = '_'; + $output .= $this->itoa64[$count & 0x3f]; + $output .= $this->itoa64[($count >> 6) & 0x3f]; + $output .= $this->itoa64[($count >> 12) & 0x3f]; + $output .= $this->itoa64[($count >> 18) & 0x3f]; + + $output .= $this->encode64($input, 3); + + return $output; + } + + function gensalt_blowfish($input) + { + # This one needs to use a different order of characters and a + # different encoding scheme from the one in encode64() above. + # We care because the last character in our encoded string will + # only represent 2 bits. While two known implementations of + # bcrypt will happily accept and correct a salt string which + # has the 4 unused bits set to non-zero, we do not want to take + # chances and we also do not want to waste an additional byte + # of entropy. + $itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + + $output = '$2a$'; + $output .= chr(ord('0') + $this->iteration_count_log2 / 10); + $output .= chr(ord('0') + $this->iteration_count_log2 % 10); + $output .= '$'; + + $i = 0; + do { + $c1 = ord($input[$i++]); + $output .= $itoa64[$c1 >> 2]; + $c1 = ($c1 & 0x03) << 4; + if ($i >= 16) { + $output .= $itoa64[$c1]; + break; + } + + $c2 = ord($input[$i++]); + $c1 |= $c2 >> 4; + $output .= $itoa64[$c1]; + $c1 = ($c2 & 0x0f) << 2; + + $c2 = ord($input[$i++]); + $c1 |= $c2 >> 6; + $output .= $itoa64[$c1]; + $output .= $itoa64[$c2 & 0x3f]; + } while (1); + + return $output; + } + + function HashPassword($password) + { + $random = ''; + + if (CRYPT_BLOWFISH == 1 && !$this->portable_hashes) { + $random = $this->get_random_bytes(16); + $hash = + crypt($password, $this->gensalt_blowfish($random)); + if (strlen($hash) == 60) + return $hash; + } + + if (CRYPT_EXT_DES == 1 && !$this->portable_hashes) { + if (strlen($random) < 3) + $random = $this->get_random_bytes(3); + $hash = + crypt($password, $this->gensalt_extended($random)); + if (strlen($hash) == 20) + return $hash; + } + + if (strlen($random) < 6) + $random = $this->get_random_bytes(6); + $hash = + $this->crypt_private($password, + $this->gensalt_private($random)); + if (strlen($hash) == 34) + return $hash; + + # Returning '*' on error is safe here, but would _not_ be safe + # in a crypt(3)-like function used _both_ for generating new + # hashes and for validating passwords against existing hashes. + return '*'; + } + + function CheckPassword($password, $stored_hash) + { + $hash = $this->crypt_private($password, $stored_hash); + if ($hash[0] == '*') + $hash = crypt($password, $stored_hash); + + return $hash == $stored_hash; + } +} + +?> diff --git a/src/ext/README.txt b/src/ext/README.txt new file mode 100644 index 0000000..df28599 --- /dev/null +++ b/src/ext/README.txt @@ -0,0 +1,16 @@ +These are class files that I've gathered from around the internet. + +I've renamed the files to follow a standard scheme. +(Could we get a standard scheme, please?) + +This is where each file came from: + +My Name : Original Name : From +GoogleVoice.class.php : class.googlevoice.php : https://code.google.com/p/phpgooglevoice/ +Identica.class.php : identica.lib.php : https://code.google.com/p/identica-php/ +MimeMailParser.class.php : MimeMailParser.class.php : https://code.google.com/p/php-mime-mail-parser/ +MimeMailParser_attachment.class.php : attachment.php : https://code.google.com/p/php-mime-mail-parser/ +PasswordHash.class.php : PasswordHash.php : http://www.openwall.com/phpass/ + +~ Luke Shumaker <lukeshu.ath.cx> +Happy Hacking! |