diff options
-rw-r--r-- | actions/newnotice.php | 2 | ||||
-rw-r--r-- | classes/File.php | 10 | ||||
-rw-r--r-- | js/util.js | 2 | ||||
-rw-r--r-- | lib/jabber.php | 10 | ||||
-rw-r--r-- | lib/queuehandler.php | 5 | ||||
-rw-r--r-- | lib/stompqueuemanager.php | 81 | ||||
-rw-r--r-- | lib/xmppqueuehandler.php | 16 | ||||
-rwxr-xr-x | scripts/maildaemon.php | 154 | ||||
-rw-r--r-- | theme/base/css/display.css | 2 |
9 files changed, 242 insertions, 40 deletions
diff --git a/actions/newnotice.php b/actions/newnotice.php index 5f44a32a9..e254eac49 100644 --- a/actions/newnotice.php +++ b/actions/newnotice.php @@ -135,7 +135,7 @@ class NewnoticeAction extends Action function isRespectsQuota($user) { $file = new File; - $ret = $file->isRespectsQuota($user); + $ret = $file->isRespectsQuota($user,$_FILES['attach']['size']); if (true === $ret) return true; $this->clientError($ret); } diff --git a/classes/File.php b/classes/File.php index 5dd7cd865..533cc6e71 100644 --- a/classes/File.php +++ b/classes/File.php @@ -122,17 +122,17 @@ class File extends Memcached_DataObject return $x; } - function isRespectsQuota($user) { - if ($_FILES['attach']['size'] > common_config('attachments', 'file_quota')) { + function isRespectsQuota($user,$fileSize) { + if ($fileSize > common_config('attachments', 'file_quota')) { return sprintf(_('No file may be larger than %d bytes ' . 'and the file you sent was %d bytes. Try to upload a smaller version.'), - common_config('attachments', 'file_quota'), $_FILES['attach']['size']); + common_config('attachments', 'file_quota'), $fileSize); } $query = "select sum(size) as total from file join file_to_post on file_to_post.file_id = file.id join notice on file_to_post.post_id = notice.id where profile_id = {$user->id} and file.url like '%/notice/%/file'"; $this->query($query); $this->fetch(); - $total = $this->total + $_FILES['attach']['size']; + $total = $this->total + $fileSize; if ($total > common_config('attachments', 'user_quota')) { return sprintf(_('A file this large would exceed your user quota of %d bytes.'), common_config('attachments', 'user_quota')); } @@ -140,7 +140,7 @@ class File extends Memcached_DataObject $query .= ' month(modified) = month(now()) and year(modified) = year(now())'; $this->query($query); $this->fetch(); - $total = $this->total + $_FILES['attach']['size']; + $total = $this->total + $fileSize; if ($total > common_config('attachments', 'monthly_quota')) { return sprintf(_('A file this large would exceed your monthly quota of %d bytes.'), common_config('attachments', 'monthly_quota')); } diff --git a/js/util.js b/js/util.js index 7a7a5e30b..bbcbc0bbb 100644 --- a/js/util.js +++ b/js/util.js @@ -283,7 +283,7 @@ function NoticeAttachments() { }, timeout : 0, autoHide : true, - css : {'max-width':'502px', 'top':'22.5%', 'left':'32.5%'} + css : {'max-width':'542px', 'top':'22.5%', 'left':'32.5%'} }; $('#content .notice a.attachment').click(function() { diff --git a/lib/jabber.php b/lib/jabber.php index 7d584ad01..e15076160 100644 --- a/lib/jabber.php +++ b/lib/jabber.php @@ -77,6 +77,14 @@ function jabber_daemon_address() return common_config('xmpp', 'user') . '@' . common_config('xmpp', 'server'); } +class Sharing_XMPP extends XMPPHP_XMPP +{ + function getSocket() + { + return $this->socket; + } +} + /** * connect the configured Jabber account to the configured server * @@ -89,7 +97,7 @@ function jabber_connect($resource=null) { static $conn = null; if (!$conn) { - $conn = new XMPPHP_XMPP(common_config('xmpp', 'host') ? + $conn = new Sharing_XMPP(common_config('xmpp', 'host') ? common_config('xmpp', 'host') : common_config('xmpp', 'server'), common_config('xmpp', 'port'), diff --git a/lib/queuehandler.php b/lib/queuehandler.php index c0f38f4e3..c2ff10f32 100644 --- a/lib/queuehandler.php +++ b/lib/queuehandler.php @@ -115,5 +115,10 @@ class QueueHandler extends Daemon { common_log($level, $this->class_name() . ' ('. $this->get_id() .'): '.$msg); } + + function getSockets() + { + return array(); + } } diff --git a/lib/stompqueuemanager.php b/lib/stompqueuemanager.php index 4cefba113..d13af3fa5 100644 --- a/lib/stompqueuemanager.php +++ b/lib/stompqueuemanager.php @@ -30,6 +30,14 @@ require_once 'Stomp.php'; +class LiberalStomp extends Stomp +{ + function getSocket() + { + return $this->_socket; + } +} + class StompQueueManager { var $server = null; @@ -50,7 +58,7 @@ class StompQueueManager { if (empty($this->con)) { $this->_log(LOG_INFO, "Connecting to '$this->server' as '$this->username'..."); - $this->con = new Stomp($this->server); + $this->con = new LiberalStomp($this->server); if ($this->con->connect($this->username, $this->password)) { $this->_log(LOG_INFO, "Connected."); @@ -94,37 +102,66 @@ class StompQueueManager while (true) { - $frame = $this->con->readFrame(); + // Wait for something on one of our sockets - if (!empty($frame)) { - $notice = Notice::staticGet('id', $frame->body); + $stompsock = $this->con->getSocket(); - if (empty($notice)) { - $this->_log(LOG_WARNING, 'Got ID '. $frame->body .' for non-existent notice in queue '. $queue); - $this->con->ack($frame); - } else { - if ($handler->handle_notice($notice)) { - $this->_log(LOG_INFO, 'Successfully handled notice '. $notice->id .' posted at ' . $frame->headers['created'] . ' in queue '. $queue); - $this->con->ack($frame); - } else { - $this->_log(LOG_WARNING, 'Failed handling notice '. $notice->id .' posted at ' . $frame->headers['created'] . ' in queue '. $queue); - // FIXME we probably shouldn't have to do - // this kind of queue management ourselves - $this->con->ack($frame); - $this->enqueue($notice, $queue); + $handsocks = $handler->getSockets(); + + $socks = array_merge(array($stompsock), $handsocks); + + $read = $socks; + $write = array(); + $except = array(); + + $ready = stream_select($read, $write, $except, $handler->timeout(), 0); + + if ($ready === false) { + $this->_log(LOG_ERR, "Error selecting on sockets"); + } else if ($ready > 0) { + if (in_array($stompsock, $read)) { + $this->_handleNotice($queue, $handler); + } + foreach ($handsocks as $sock) { + if (in_array($sock, $read)) { + $handler->idle(QUEUE_HANDLER_HIT_IDLE); + break; } - unset($notice); } - - unset($frame); } - - $handler->idle(0); } $this->con->unsubscribe($this->_queueName($queue)); } + function _handleNotice($queue, $handler) + { + $frame = $this->con->readFrame(); + + if (!empty($frame)) { + $notice = Notice::staticGet('id', $frame->body); + + if (empty($notice)) { + $this->_log(LOG_WARNING, 'Got ID '. $frame->body .' for non-existent notice in queue '. $queue); + $this->con->ack($frame); + } else { + if ($handler->handle_notice($notice)) { + $this->_log(LOG_INFO, 'Successfully handled notice '. $notice->id .' posted at ' . $frame->headers['created'] . ' in queue '. $queue); + $this->con->ack($frame); + } else { + $this->_log(LOG_WARNING, 'Failed handling notice '. $notice->id .' posted at ' . $frame->headers['created'] . ' in queue '. $queue); + // FIXME we probably shouldn't have to do + // this kind of queue management ourselves + $this->con->ack($frame); + $this->enqueue($notice, $queue); + } + unset($notice); + } + + unset($frame); + } + } + function _queueName($queue) { return common_config('queue', 'queue_basename') . $queue; diff --git a/lib/xmppqueuehandler.php b/lib/xmppqueuehandler.php index 7d14422c6..77d476c30 100644 --- a/lib/xmppqueuehandler.php +++ b/lib/xmppqueuehandler.php @@ -21,6 +21,8 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/queuehandler.php'); +define('PING_INTERVAL', 120); + /** * Common superclass for all XMPP-using queue handlers. They all need to * service their message queues on idle, and forward any incoming messages @@ -31,6 +33,7 @@ require_once(INSTALLDIR.'/lib/queuehandler.php'); class XmppQueueHandler extends QueueHandler { var $pingid = 0; + var $lastping = null; function start() { @@ -63,8 +66,12 @@ class XmppQueueHandler extends QueueHandler try { if ($this->conn) { $this->log(LOG_DEBUG, "Servicing the XMPP queue."); - $this->conn->processTime(max($timeout, 1)); - $this->sendPing(); + $this->conn->processTime($timeout); + $now = time(); + if (empty($this->lastping) || $now - $this->lastping > PING_INTERVAL) { + $this->sendPing(); + $this->lastping = $now; + } } } catch (XMPPHP_Exception $e) { $this->log(LOG_ERR, "Got an XMPPHP_Exception: " . $e->getMessage()); @@ -119,4 +126,9 @@ class XmppQueueHandler extends QueueHandler return jabber_daemon_address() . '/' . common_config('xmpp','resource') . 'daemon'; } } + + function getSockets() + { + return array($this->conn->getSocket()); + } } diff --git a/scripts/maildaemon.php b/scripts/maildaemon.php index cfb11a36f..11ddf06b7 100755 --- a/scripts/maildaemon.php +++ b/scripts/maildaemon.php @@ -42,11 +42,11 @@ class MailerDaemon function handle_message($fname='php://stdin') { - list($from, $to, $msg) = $this->parse_message($fname); + list($from, $to, $msg, $attachments) = $this->parse_message($fname); if (!$from || !$to || !$msg) { $this->error(null, _('Could not parse message.')); } - common_log(LOG_INFO, "Mail from $from to $to: " .substr($msg, 0, 20)); + common_log(LOG_INFO, "Mail from $from to $to with ".count($attachments) .' attachment(s): ' .substr($msg, 0, 20)); $user = $this->user_from($from); if (!$user) { $this->error($from, _('Not a registered user.')); @@ -65,7 +65,47 @@ class MailerDaemon return true; } $msg = $this->cleanup_msg($msg); - $err = $this->add_notice($user, $msg); + $msg = common_shorten_links($msg); + if (mb_strlen($msg) > 140) { + $this->error($from,_('That\'s too long. '. + 'Max notice size is 140 chars.')); + } + $fileRecords = array(); + foreach($attachments as $attachment){ + $mimetype = $this->getUploadedFileType($attachment); + $stream = stream_get_meta_data($attachment); + if (!$this->isRespectsQuota($user,filesize($stream['uri']))) { + die('error() should trigger an exception before reaching here.'); + } + $filename = $this->saveFile($user, $attachment,$mimetype); + + fclose($attachment); + + if (empty($filename)) { + $this->error($from,_('Couldn\'t save file.')); + } + + $fileRecord = $this->storeFile($filename, $mimetype); + $fileRecords[] = $fileRecord; + $fileurl = common_local_url('attachment', + array('attachment' => $fileRecord->id)); + + // not sure this is necessary -- Zach + $this->maybeAddRedir($fileRecord->id, $fileurl); + + $short_fileurl = common_shorten_url($fileurl); + $msg .= ' ' . $short_fileurl; + + if (mb_strlen($msg) > 140) { + $this->deleteFile($filename); + $this->error($from,_('Max notice size is 140 chars, including attachment URL.')); + } + + // Also, not sure this is necessary -- Zach + $this->maybeAddRedir($fileRecord->id, $short_fileurl); + } + + $err = $this->add_notice($user, $msg, $fileRecords); if (is_string($err)) { $this->error($from, $err); return false; @@ -74,6 +114,89 @@ class MailerDaemon } } + function saveFile($user, $attachment, $mimetype) { + + $filename = File::filename($user->getProfile(), "email", $mimetype); + + $filepath = File::path($filename); + + $stream = stream_get_meta_data($attachment); + if (copy($stream['uri'], $filepath) && chmod($filepath,0664)) { + return $filename; + } else { + $this->error(null,_('File could not be moved to destination directory.' . $stream['uri'] . ' ' . $filepath)); + } + } + + function storeFile($filename, $mimetype) { + + $file = new File; + $file->filename = $filename; + + $file->url = File::url($filename); + + $filepath = File::path($filename); + + $file->size = filesize($filepath); + $file->date = time(); + $file->mimetype = $mimetype; + + $file_id = $file->insert(); + + if (!$file_id) { + common_log_db_error($file, "INSERT", __FILE__); + $this->error(null,_('There was a database error while saving your file. Please try again.')); + } + + return $file; + } + + function maybeAddRedir($file_id, $url) + { + $file_redir = File_redirection::staticGet('url', $url); + + if (empty($file_redir)) { + $file_redir = new File_redirection; + $file_redir->url = $url; + $file_redir->file_id = $file_id; + + $result = $file_redir->insert(); + + if (!$result) { + common_log_db_error($file_redir, "INSERT", __FILE__); + $this->error(null,_('There was a database error while saving your file. Please try again.')); + } + } + } + + function getUploadedFileType($fileHandle) { + require_once 'MIME/Type.php'; + + $cmd = &PEAR::getStaticProperty('MIME_Type', 'fileCmd'); + $cmd = common_config('attachments', 'filecommand'); + + $stream = stream_get_meta_data($fileHandle); + $filetype = MIME_Type::autoDetect($stream['uri']); + if (in_array($filetype, common_config('attachments', 'supported'))) { + return $filetype; + } + $media = MIME_Type::getMedia($filetype); + if ('application' !== $media) { + $hint = sprintf(_(' Try using another %s format.'), $media); + } else { + $hint = ''; + } + $this->error(null,sprintf( + _('%s is not a supported filetype on this server.'), $filetype) . $hint); + } + + function isRespectsQuota($user,$fileSize) { + $file = new File; + $ret = $file->isRespectsQuota($user,$fileSize); + if (true === $ret) return true; + $this->error(null,$ret); + } + function error($from, $msg) { file_put_contents("php://stderr", $msg . "\n"); @@ -133,19 +256,30 @@ class MailerDaemon common_log($level, 'MailDaemon: '.$msg); } - function add_notice($user, $msg) + function add_notice($user, $msg, $fileRecords) { $notice = Notice::saveNew($user->id, $msg, 'mail'); if (is_string($notice)) { $this->log(LOG_ERR, $notice); return $notice; } + foreach($fileRecords as $fileRecord){ + $this->attachFile($notice, $fileRecord); + } common_broadcast_notice($notice); $this->log(LOG_INFO, 'Added notice ' . $notice->id . ' from user ' . $user->nickname); return true; } + function attachFile($notice, $filerec) + { + File_to_post::processNew($filerec->id, $notice->id); + + $this->maybeAddRedir($filerec->id, + common_local_url('file', array('notice' => $notice->id))); + } + function parse_message($fname) { $contents = file_get_contents($fname); @@ -163,12 +297,19 @@ class MailerDaemon $type = $parsed->ctype_primary . '/' . $parsed->ctype_secondary; + $attachments = array(); + if ($parsed->ctype_primary == 'multipart') { foreach ($parsed->parts as $part) { if ($part->ctype_primary == 'text' && $part->ctype_secondary == 'plain') { $msg = $part->body; - break; + }else{ + if ($part->body) { + $attachment = tmpfile(); + fwrite($attachment, $part->body); + $attachments[] = $attachment; + } } } } else if ($type == 'text/plain') { @@ -176,8 +317,7 @@ class MailerDaemon } else { $this->unsupported_type($type); } - - return array($from, $to, $msg); + return array($from, $to, $msg, $attachments); } function unsupported_type($type) diff --git a/theme/base/css/display.css b/theme/base/css/display.css index e9f4beaae..3426e71c0 100644 --- a/theme/base/css/display.css +++ b/theme/base/css/display.css @@ -1020,7 +1020,7 @@ font-weight:bold; padding:0; } #jOverlayContent h1 { -max-width:475px; +max-width:425px; } #jOverlayContent #content { border-radius:7px; |