summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
Diffstat (limited to 'apps')
-rw-r--r--apps/mm/controllers/Messages.class.php98
-rw-r--r--apps/mm/ext/GoogleVoice.class.php84
-rw-r--r--apps/mm/ext/Identica.class.php491
-rw-r--r--apps/mm/ext/MimeMailParser.class.php447
-rw-r--r--apps/mm/ext/MimeMailParser_attachment.class.php136
-rw-r--r--apps/mm/ext/README.txt14
-rw-r--r--apps/mm/models/Message.class.php116
-rw-r--r--apps/mm/plugins/SenderGVSMS.class.php34
-rw-r--r--apps/mm/plugins/SenderIdentica.class.php35
-rw-r--r--apps/mm/scripts/newmail.php36
-rw-r--r--apps/um/lib/Singleton.class.php12
-rw-r--r--apps/um/models/Auth.class.php1
-rw-r--r--apps/um/views/pages/messages/401.html.php15
-rw-r--r--apps/um/views/pages/messages/frame.html.php58
-rw-r--r--apps/um/views/pages/messages/index.html.php25
15 files changed, 1589 insertions, 13 deletions
diff --git a/apps/mm/controllers/Messages.class.php b/apps/mm/controllers/Messages.class.php
new file mode 100644
index 0000000..d9decc9
--- /dev/null
+++ b/apps/mm/controllers/Messages.class.php
@@ -0,0 +1,98 @@
+<?php
+require_once('Login.class.php');
+require_once('Auth.class.php');
+require_once('Database.class.php');
+require_once('Message.class.php');
+require_once('MimeMailParser.class.php');
+
+Router::register('messages', 'Messages', 'index');
+Router::register('messages/index', 'Messages', 'index');
+Router::register('messages/*', 'Messages', 'message');
+
+class Messages extends Controller {
+ private $msgdir;
+
+ public function __construct() {
+ $db = Database::getInstance();
+ $this->msgdir = $db->getSysConf('msgdir');
+ }
+
+ public function index($routed, $remainder) {
+ $parser = new MimeMailParser();
+ $messages = array();
+ $dh = opendir($this->msgdir);
+ while (($file = readdir($dh)) !== false) {
+ $path = $this->msgdir."/$file";
+ if (is_file($path)) {
+ $parser->setPath($path);
+
+ $date_string = $parser->getHeader('date');
+ $date = strtotime($date_string);
+ if (!isset($messages[$date])) {
+ $messages[$date] = array();
+ }
+ $messages[$date][] =
+ array('id'=>$file,
+ 'subject'=>$parser->getHeader('subject'),
+ 'from'=>$parser->getHeader('from'));
+ }
+ }
+ closedir($dh);
+
+ $this->showView('messages/index', array('messages' => $messages));
+ exit();
+ }
+
+ public function message($routed, $remainder) {
+ $uid = Login::isLoggedIn();
+ if ($uid===false || !Auth::getInstance($uid)->isUser()) {
+ $this->http401($routed, $remainder);
+ return;
+ }
+
+ $msg_id = $remainder[0];// We can trust the router that this is set
+ $msg = new Message($msg_id);
+ if ($msg === false) {
+ $this->http404($routed, $remainder);
+ return;
+ }
+
+ @$part = $remainder[1];
+ @$subpart = $remainder[2];
+
+ switch ($part) {
+ case '':
+ $this->showView('messages/frame',
+ array('msg'=>$msg));
+ break;
+ case 'body':
+ require_once('Mime.class.php');
+ header('Content-type: '.Mime::ext2mime(PAGE_EXT));
+ $map = array('html'=>'html',
+ 'txt' =>'text');
+ echo $msg->getMessageBody($map[PAGE_EXT]);
+ break;
+ case 'attachment':
+ $attachment_id = $subpart;
+ $attachments = $msg->getAttachments();
+ $attachment = $attachments[$attachment_id];
+
+ $type = $attachment->getContentType();
+ $filename = $attachment->getFilename();
+
+ header('Content-Type: '.$type);
+ header('Content-Disposition: attachment; filename='.$filename );
+ while($bytes = $attachment->read()) {
+ echo $bytes;
+ }
+ break;
+ default:
+ array_push($routed, array_shift($remainder));
+ $this->http404($routed, $remainder);
+ }
+ }
+
+ public function http401($routed, $remainder) {
+ $this->showView('messages/401', array('uid'=>Login::isLoggedIn()));
+ }
+} \ No newline at end of file
diff --git a/apps/mm/ext/GoogleVoice.class.php b/apps/mm/ext/GoogleVoice.class.php
new file mode 100644
index 0000000..9638416
--- /dev/null
+++ b/apps/mm/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/apps/mm/ext/Identica.class.php b/apps/mm/ext/Identica.class.php
new file mode 100644
index 0000000..a3e62a9
--- /dev/null
+++ b/apps/mm/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/apps/mm/ext/MimeMailParser.class.php b/apps/mm/ext/MimeMailParser.class.php
new file mode 100644
index 0000000..0080199
--- /dev/null
+++ b/apps/mm/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/apps/mm/ext/MimeMailParser_attachment.class.php b/apps/mm/ext/MimeMailParser_attachment.class.php
new file mode 100644
index 0000000..6bd327c
--- /dev/null
+++ b/apps/mm/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/apps/mm/ext/README.txt b/apps/mm/ext/README.txt
new file mode 100644
index 0000000..6d31122
--- /dev/null
+++ b/apps/mm/ext/README.txt
@@ -0,0 +1,14 @@
+These are class files that I've gathered from around the internet.
+
+I've renamed the files to follow a standard scheme.
+
+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/
+
+~ Luke Shumaker <http://lukeshu.ath.cx>
+Happy Hacking!
diff --git a/apps/mm/models/Message.class.php b/apps/mm/models/Message.class.php
new file mode 100644
index 0000000..b2a2b7e
--- /dev/null
+++ b/apps/mm/models/Message.class.php
@@ -0,0 +1,116 @@
+<?php
+require_once('MimeMailParser.class.php');
+require_once('Database.class.php');
+
+class Message extends Model {
+ public static function add($infile) {
+ $parser = new MimeMailParser();
+ $parser->setPath($infile);
+ $id = preg_replace('/<(.*)>/', '$1',
+ $parser->getHeader('message-id'));
+ $id = str_replace('/', '', $id); // For security purposes
+
+ $db = Database::getInstance();
+ $msgdir = $db->getSysConf('msgdir');
+ $msg_file = "$msgdir/$id";
+
+ rename($infile, $msg_file);
+ return new Message($id);
+ }
+
+ private $_msgid;
+ private $_msgdir;
+ private $_parser;
+
+ public function __construct($msgid) {
+ $this->_msgid = str_replace('/', '', $msgid);
+ if (!file_exists($this->file())) {
+ return false;
+ }
+ }
+
+ public function msgid() {
+ return $this->_msgid;
+ }
+
+ private function msgdir() {
+ if (!isset($this->_msgdir)) {
+ $db = Database::getInstance();
+ $this->_msgdir = $db->getSysConf('msgdir');
+ }
+ return $this->_msgdir;
+ }
+
+ private function file() {
+ return $this->msgdir().'/'.$this->msgid();
+ }
+
+ private function parser() {
+ if (!isset($this->_parser)) {
+ $this->_parser = new MimeMailParser();
+ $this->_parser->setPath($this->file());
+ }
+ return $this->_parser;
+ }
+
+ /**
+ * Retrieve the Email Headers
+ * @return Array
+ */
+ public function getHeaders() {
+ return $this->parser()->getHeaders();
+ }
+
+ /**
+ * Retrieve the raw Email Headers
+ * @return string
+ */
+ public function getHeadersRaw() {
+ return $this->parser()->getHeadersRaw();
+ }
+
+ /**
+ * Retrieve a specific Email Header
+ * @return String
+ * @param $name String Header name
+ */
+ public function getHeader($name) {
+ return $this->parser()->getHeader($name);
+ }
+
+ /**
+ * 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') {
+ return $this->parser()->getMessageBodyPart($type);
+ }
+
+ /**
+ * 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') {
+ return $this->parser()->getMessageBody($type);
+ }
+
+ /**
+ * get the headers for the message body part.
+ * @return Array
+ * @param $type Object[optional]
+ */
+ public function getMessageBodyHeaders($type = 'text') {
+ return $this->parser()->getMessageBodyHeaders($type);
+ }
+
+ /**
+ * Returns the attachments contents in order of appearance
+ * @return Array
+ * @param $type Object[optional]
+ */
+ public function getAttachments() {
+ return $this->parser()->getAttachments();
+ }
+}
diff --git a/apps/mm/plugins/SenderGVSMS.class.php b/apps/mm/plugins/SenderGVSMS.class.php
new file mode 100644
index 0000000..76db208
--- /dev/null
+++ b/apps/mm/plugins/SenderGVSMS.class.php
@@ -0,0 +1,34 @@
+<?php
+require_once('Plugin.class.php');
+require_once('GoogleVoice.class.php');
+
+class SenderGVSMS extends Plugin {
+ protected $config = array('username'=>'',
+ 'password'=>'',
+ 'length'=>160);
+ private $obj;
+
+ public static function description() {
+ return 'Send messages over SMS via GoogleVoice.';
+ }
+
+ public static function configList() {
+ return array('username'=>'text',
+ 'password'=>'password');
+ }
+
+ public function init() {
+ $this->obj = new GoogleVoice($this->config['username'],
+ $this->config['password']);
+ }
+
+ public function sendPrivate($phoneNum, $id, $subject, $body) {
+ global $shorturl, $messenger;
+ $url = $shorturl->get($messenger->id2url($id));
+ $maxlen = $this->config['length']-(strlen($url)+1);
+ if($maxlen < strlen($subject)) {
+ $subject = substr($subject,0,$maxlen-3).'...';
+ }
+ $this->obj->sms($phoneNum, $subject.' '.$url);
+ }
+}
diff --git a/apps/mm/plugins/SenderIdentica.class.php b/apps/mm/plugins/SenderIdentica.class.php
new file mode 100644
index 0000000..ab55eb9
--- /dev/null
+++ b/apps/mm/plugins/SenderIdentica.class.php
@@ -0,0 +1,35 @@
+<?php
+require_once('Plugin.class.php');
+require_once('Identica.class.php');
+
+class SenderIdentica extends Plugin {
+ protected $config = array('username'=>'',
+ 'password'=>'',
+ 'length'=>140);
+ private $obj;
+
+ public static function description() {
+ return 'Dent messages to Identi.ca.';
+ }
+
+ public static function configList() {
+ return array('username'=>'text',
+ 'password'=>'password',
+ 'length'=>'int');
+ }
+
+ public function init() {
+ $this->obj = new Identica($this->config['username'],
+ $this->config['password']);
+ }
+
+ public function sendBroadcast($id, $subject, $body) {
+ global $shorturl, $messenger;
+ $url = $shorturl->get($messenger->id2url($id));
+ $maxlen = $this->config['length']-(strlen($url)+1);
+ if($maxlen < strlen($subject)) {
+ $subject = substr($subject,0,$maxlen-3).'...';
+ }
+ $this->obj->updateStatus($subject.' '.$url);
+ }
+}
diff --git a/apps/mm/scripts/newmail.php b/apps/mm/scripts/newmail.php
new file mode 100644
index 0000000..e4fc740
--- /dev/null
+++ b/apps/mm/scripts/newmail.php
@@ -0,0 +1,36 @@
+<?php
+// What directory are we in on the server? /////////////////////////////////////
+$_dir_scripts = dirname(__FILE__);
+$_dir_app = dirname($_dir_scripts);
+$_dir_apps = dirname($_dir_app);
+$_dir_base = dirname($_dir_apps);
+require_once($_dir_base.'/stub.php');
+
+$cmdline = isset($argv[0]); // called from the command line
+@$method = $_SERVER['REQUEST_METHOD']; // What HTTP method was used
+if ( ($method=='PUT') || $cmdline ) {
+ // We're going to be uploading a new message.
+
+ // uniqid() isn't 'secure', it doesn't need to be, it's to prevent
+ // random collisions.
+ $tmpfile = "$BASE/tmp/".uniqid(getmypid().'.');
+ $infile = ($cmdline?'php://stdin':'php://input');
+ $out = fopen($tmpfile, "w");
+ $in = fopen($infile, "r");
+ while ($data = fread($in, 1024))
+ fwrite($out, $data);
+ fclose($out);
+ fclose($in);
+
+ require_once('Message.class.php');
+ $msg = Message::add($tmpfile);
+ $id = $msg->msgid();
+
+ if ($cmdline) {
+ echo $id."\n";
+ } else {
+ $m->status('201 Created');
+ header("Location: ".$m->baseUrl().'messages/'.$id);
+ }
+ exit();
+} \ No newline at end of file
diff --git a/apps/um/lib/Singleton.class.php b/apps/um/lib/Singleton.class.php
deleted file mode 100644
index 2f8c74f..0000000
--- a/apps/um/lib/Singleton.class.php
+++ /dev/null
@@ -1,12 +0,0 @@
-<?php
-
-abstract class Singleton {
- private static $instances = array();
- public static function getInstance() {
- $class = get_called_class();
- if (!isset(self::$instances[$class])) {
- self::$instances[$class] = new $class;
- }
- return self::$instances[$class];
- }
-}
diff --git a/apps/um/models/Auth.class.php b/apps/um/models/Auth.class.php
index 39f627e..a7cc629 100644
--- a/apps/um/models/Auth.class.php
+++ b/apps/um/models/Auth.class.php
@@ -1,5 +1,4 @@
<?php
-require_once('Model.class.php');
require_once('Login.class.php');
require_once('Database.class.php');
diff --git a/apps/um/views/pages/messages/401.html.php b/apps/um/views/pages/messages/401.html.php
new file mode 100644
index 0000000..0b24f80
--- /dev/null
+++ b/apps/um/views/pages/messages/401.html.php
@@ -0,0 +1,15 @@
+<?php global $VARS;
+$t = $VARS['template'];
+
+$t->status('401 Unauthorized');
+$t->header('Unauthorized');
+$t->tag('h1', array(), "401: Unauthorized");
+if ($VARS['uid']===false) {
+ // Not logged in
+ $t->paragraph('You need to be logged in to view messages.');
+} else {
+ // Logged in, so the account must not activated
+ $t->paragraph('Your account needs to be activated by an administrator '.
+ 'to view messages.');
+}
+$t->footer();
diff --git a/apps/um/views/pages/messages/frame.html.php b/apps/um/views/pages/messages/frame.html.php
new file mode 100644
index 0000000..30fc1cc
--- /dev/null
+++ b/apps/um/views/pages/messages/frame.html.php
@@ -0,0 +1,58 @@
+<?php global $VARS;
+$t = $VARS['template'];
+$msg = $VARS['msg'];
+
+function messageLink($id) {
+ if (is_array($id)) { $id = $id[1]; }
+ global $VARS; $t = $VARS['template'];
+
+ $msg = new Message($id);
+ $exists = ($msg?true:false);
+
+ return sprintf('&lt;<a href="%1$s"%2$s>%3$s</a>&gt;',
+ $t->url("messages/$id/"),
+ ($exists?'':' class="http404"'),
+ htmlentities($id));
+}
+function parseMessageIDs($string) {
+ $base = $_SERVER['REQUEST_URL'];
+ $html = preg_replace_callback(
+ '/<([^>]*)>/',
+ 'messageLink',
+ $string);
+ return $html;
+}
+
+$t->header('View Message');
+$t->openTag('table');
+$t->row(array('To:' , htmlentities( $msg->getHeader('to' ))));
+$t->row(array('From:' , htmlentities( $msg->getHeader('from' ))));
+$t->row(array('Subject:' , htmlentities( $msg->getHeader('subject' ))));
+$t->row(array('In-Reply-to:', parseMessageIDs($msg->getHeader('in-reply-to'))));
+$t->row(array('References:' , parseMessageIDs($msg->getHeader('references' ))));
+$t->closeTag('table');
+
+$msg_id = htmlentities($msg->msgid());
+
+$t->openTag('div', array('class'=>'message-body'));
+if ($msg->getMessageBodyPart('html')!==false) {
+ $t->tag('h2', array(), 'HTML');
+ $t->tag('iframe', array('src'=>$t->url("messages/$msg_id/body.html")), '');
+}
+if ($msg->getMessageBodyPart('text')!==false) {
+ $t->tag('h2', array(), 'Plain Text');
+ $t->tag('iframe', array('src'=>$t->url("messages/$msg_id/body.txt")), '');
+}
+$t->closeTag('div');
+$t->tag('h2', array(), 'Attachments');
+$t->openTag('table');
+$attachments = $msg->getAttachments();
+foreach ($attachments as $id => $attachment) {
+ $t->row(array(
+ htmlentities($attachment->getContentType()),
+ $t->link($t->url("$msg_id/attachment/$id"),
+ htmlentities($attachment->getFilename())),
+ ));
+}
+$t->closeTag('table');
+$t->footer();
diff --git a/apps/um/views/pages/messages/index.html.php b/apps/um/views/pages/messages/index.html.php
new file mode 100644
index 0000000..111b6c6
--- /dev/null
+++ b/apps/um/views/pages/messages/index.html.php
@@ -0,0 +1,25 @@
+<?php global $VARS;
+$t = $VARS['template'];
+$messages = $VARS['messages'];
+
+$t->header('Message Index');
+$t->tag('h1', array(), "Message Index");
+
+$t->openTag('table');
+$t->row(array('From','Subject', 'Date'));
+foreach ($messages as $date => $message_array) {
+ foreach ($message_array as $message) {
+ $url = $t->url('messages/'.$message['id'].'/');
+ $subject = htmlentities($message['subject']);
+ $from = htmlentities($message['from']);
+ $date_str = str_replace(' ', '&nbsp;', date('Y-m-d H:i:s',$date));
+ $t->row(array(
+ $t->link($url, $from , true),
+ $t->link($url, $subject , true),
+ $t->link($url, $date_str, true),
+ ));
+ }
+}
+$t->closeTag('table');
+
+$t->footer();