]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Added configurable options for attachments: supported mimetypes and quotas for uploads.
authorRobin Millette <millette@controlyourself.ca>
Sun, 31 May 2009 20:42:29 +0000 (16:42 -0400)
committerRobin Millette <millette@controlyourself.ca>
Sun, 31 May 2009 20:42:29 +0000 (16:42 -0400)
actions/newnotice.php
config.php.sample
extlib/MIME/Type.php [new file with mode: 0644]
extlib/MIME/Type/Extension.php [new file with mode: 0644]
extlib/MIME/Type/Parameter.php [new file with mode: 0644]
lib/common.php

index aebdab3cca62b091f1e39f176715493c08ed7d6e..29b748dd1ac509cd2e9ca84cbc340217196653a0 100644 (file)
@@ -84,20 +84,24 @@ class NewnoticeAction extends Action
 
     function handle($args)
     {
-        parent::handle($args);
-
         if (!common_logged_in()) {
             $this->clientError(_('Not logged in.'));
         } else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
+            // check for this before token since all POST and FILES data
+            // is losts when size is exceeded
+            if (empty($_POST) && $_SERVER['CONTENT_LENGTH']) {
+                $this->clientError(sprintf(_('The server was unable to handle ' .
+                    'that much POST data (%s bytes) due to its current configuration.'),
+                    $_SERVER['CONTENT_LENGTH']));
+            }
+            parent::handle($args);
 
             // CSRF protection
             $token = $this->trimmed('token');
             if (!$token || $token != common_session_token()) {
                 $this->clientError(_('There was a problem with your session token. '.
                                      'Try again, please.'));
-                return;
             }
-
             try {
                 $this->saveNewNotice();
             } catch (Exception $e) {
@@ -109,8 +113,55 @@ class NewnoticeAction extends Action
         }
     }
 
-    function isFileAttached() {
-        return isset($_FILES['attach']['error']) && ($_FILES['attach']['error'] === UPLOAD_ERR_OK);
+    function isSupportedFileType() {
+        require_once 'MIME/Type.php';
+
+        $filetype = MIME_Type::autoDetect($_FILES['attach']['tmp_name']);
+        if (in_array($filetype, common_config('attachments', 'supported'))) {
+            return true;
+        }
+        $media = MIME_Type::getMedia($filetype);
+        if ('application' !== $media) {
+            $hint = sprintf(_(' Try using another %s format.'), $media);
+        } else {
+            $hint = '';
+        }
+        $this->clientError(sprintf(
+            _('%s is not a supported filetype on this server.'), $filetype) . $hint);
+    }
+
+    function isRespectsQuota($user) {
+        if ($_FILES['attach']['size'] > common_config('attachments', 'file_quota')) {
+            $this->clientError(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']));
+        }
+
+        $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'";
+        $file = new File;
+        $file->query($query);
+        $file->fetch();
+        $total = $file->total + $_FILES['attach']['size'];
+        if ($total > common_config('attachments', 'user_quota')) {
+            $this->clientError(sprintf(_('A file this large would exceed your user quota of %d bytes.'), common_config('attachments', 'user_quota')));
+        }
+
+        $query .= ' month(modified) = month(now()) and year(modified) = year(now())';
+        $file2 = new File;
+        $file2->query($query);
+        $file2->fetch();
+        $total2 = $file2->total + $_FILES['attach']['size'];
+        if ($total2 > common_config('attachments', 'monthly_quota')) {
+            $this->clientError(sprintf(_('A file this large would exceed your monthly quota of %d bytes.'), common_config('attachments', 'monthly_quota')));
+        }
+        return true;
+    }
+
+    function isValidFileAttached($user) {
+        return isset($_FILES['attach']['error'])
+            && ($_FILES['attach']['error'] === UPLOAD_ERR_OK)
+            && $this->isSupportedFileType()
+            && $this->isRespectsQuota($user);
     }
 
     /**
@@ -135,7 +186,6 @@ class NewnoticeAction extends Action
             $this->clientError(_('No content!'));
         } else {
             $content_shortened = common_shorten_links($content);
-
             if (mb_strlen($content_shortened) > 140) {
                 $this->clientError(_('That\'s too long. '.
                                      'Max notice size is 140 chars.'));
@@ -162,19 +212,54 @@ class NewnoticeAction extends Action
             $replyto = 'false';
         }
 
+        switch ($_FILES['attach']['error']) {
+            case UPLOAD_ERR_NO_FILE:
+                // no file uploaded
+                // nothing to do
+                break;
+
+             case UPLOAD_ERR_OK:
+                // file was uploaded alright
+                // lets check if we really support its format
+                // and it doesn't go over quotas
+
+
+                if (!$this->isValidFileAttached($user)) {
+                    die('clientError() should trigger an exception before reaching here.');
+                }
+                break;
+
+            case UPLOAD_ERR_INI_SIZE:
+                $this->clientError(_('The uploaded file exceeds the upload_max_filesize directive in php.ini.'));
+
+            case UPLOAD_ERR_FORM_SIZE:
+                $this->clientError(_('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.'));
+
+            case UPLOAD_ERR_PARTIAL:
+                $this->clientError(_('The uploaded file was only partially uploaded.'));
+
+            case  UPLOAD_ERR_NO_TMP_DIR:
+                $this->clientError(_('Missing a temporary folder.'));
+
+            case UPLOAD_ERR_CANT_WRITE:
+                $this->clientError(_('Failed to write file to disk.'));
+
+            case UPLOAD_ERR_EXTENSION:
+                $this->clientError(_('File upload stopped by extension.'));
+
+            default:
+                die('Should never reach here.');
+        }
+
         $notice = Notice::saveNew($user->id, $content_shortened, 'web', 1,
                                   ($replyto == 'false') ? null : $replyto);
 
         if (is_string($notice)) {
             $this->clientError($notice);
-            return;
         }
 
-        if ($this->isFileAttached()) {
-            $this->storeFile($notice);
-        }
+        $this->storeFile($notice);
         $this->saveUrls($notice);
-
         common_broadcast_notice($notice);
 
         if ($this->boolean('ajax')) {
@@ -201,12 +286,12 @@ class NewnoticeAction extends Action
     }
 
     function storeFile($notice) {
+        if (UPLOAD_ERR_NO_FILE === $_FILES['attach']['error']) return;
         $filename = basename($_FILES['attach']['name']);
         $destination = "file/{$notice->id}-$filename";
         if (move_uploaded_file($_FILES['attach']['tmp_name'], INSTALLDIR . "/$destination")) {
             $file = new File;
             $file->url = common_local_url('file', array('notice' => $notice->id));
-//            $file->url = common_path($destination);
             $file->size = filesize(INSTALLDIR . "/$destination");
             $file->date = time();
             $file->mimetype = $_FILES['attach']['type'];
@@ -221,14 +306,9 @@ class NewnoticeAction extends Action
                 $f2p->post_id = $notice->id; 
                 $f2p->insert();
             } else {
-                die('inserting file, dying');
+                $this->clientError(_('There was a database error while saving your file. Please try again.'));
             }
         }
-/*
-        $url = common_local_url('file', array('notice' => $notice->id));
-        echo "$destination<br />";
-        die($url);
-*/
     }
 
 
index 282826a7fb075957c32098c8753e0c277d88c911..636f4cf8e22255322c0452e154727f1ff3cb0f41 100644 (file)
@@ -215,3 +215,11 @@ $config['sphinx']['port'] = 3312;
 // $config['snapshot']['run'] = 'never';
 // If you want to report statistics in a cron job instead.
 // $config['snapshot']['run'] = 'cron';
+
+// Support for file uploads (attachments),
+// select supported mimetypes and quotas (in bytes)
+// $config['attachments']['supported'] = array('image/png', 'application/ogg');
+// $config['attachments']['file_quota'] = 5000000;
+// $config['attachments']['user_quota'] = 50000000;
+// $config['attachments']['monthly_quota'] = 15000000;
+
diff --git a/extlib/MIME/Type.php b/extlib/MIME/Type.php
new file mode 100644 (file)
index 0000000..c335f8d
--- /dev/null
@@ -0,0 +1,523 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2002, 2008 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license,       |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available at through the world-wide-web at                           |
+// | http://www.php.net/license/3_0.txt.                                  |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Authors: Ian Eure <ieure@php.net>                                    |
+// +----------------------------------------------------------------------+
+//
+// $Id: Type.php,v 1.6 2009/01/16 11:49:45 cweiske Exp $
+
+require_once 'PEAR.php';
+
+$_fileCmd = &PEAR::getStaticProperty('MIME_Type', 'fileCmd');
+$_fileCmd = 'file';
+
+/**
+ * Class for working with MIME types
+ *
+ * @category MIME
+ * @package  MIME_Type
+ * @license  PHP License 3.0
+ * @version  1.2.0
+ * @link     http://pear.php.net/package/MIME_Type
+ * @author   Ian Eure <ieure@php.net>
+ */
+class MIME_Type
+{
+    /**
+     * The MIME media type
+     *
+     * @var string
+     */
+    var $media = '';
+
+    /**
+     * The MIME media sub-type
+     *
+     * @var string
+     */
+    var $subType = '';
+
+    /**
+     * Optional MIME parameters
+     *
+     * @var array
+     */
+    var $parameters = array();
+
+    /**
+     * List of valid media types.
+     * A media type is the string in front of the slash.
+     * The media type of "text/xml" would be "text".
+     *
+     * @var array
+     */
+    var $validMediaTypes = array(
+        'text',
+        'image',
+        'audio',
+        'video',
+        'application',
+        'multipart',
+        'message'
+    );
+
+
+    /**
+     * Constructor.
+     *
+     * If $type is set, if will be parsed and the appropriate class vars set.
+     * If not, you get an empty class.
+     * This is useful, but not quite as useful as parsing a type.
+     *
+     * @param string $type MIME type
+     *
+     * @return void
+     */
+    function MIME_Type($type = false)
+    {
+        if ($type) {
+            $this->parse($type);
+        }
+    }
+
+
+    /**
+     * Parse a mime-type and set the class variables.
+     *
+     * @param string $type MIME type to parse
+     *
+     * @return void
+     */
+    function parse($type)
+    {
+        $this->media      = $this->getMedia($type);
+        $this->subType    = $this->getSubType($type);
+        $this->parameters = array();
+        if (MIME_Type::hasParameters($type)) {
+            require_once 'MIME/Type/Parameter.php';
+            foreach (MIME_Type::getParameters($type) as $param) {
+                $param = new MIME_Type_Parameter($param);
+                $this->parameters[$param->name] = $param;
+            }
+        }
+    }
+
+
+    /**
+     * Does this type have any parameters?
+     *
+     * @param string $type MIME type to check
+     *
+     * @return boolean true if $type has parameters, false otherwise
+     * @static
+     */
+    function hasParameters($type)
+    {
+        if (strstr($type, ';')) {
+            return true;
+        }
+        return false;
+    }
+
+
+    /**
+     * Get a MIME type's parameters
+     *
+     * @param string $type MIME type to get parameters of
+     *
+     * @return array $type's parameters
+     * @static
+     */
+    function getParameters($type)
+    {
+        $params = array();
+        $tmp    = explode(';', $type);
+        for ($i = 1; $i < count($tmp); $i++) {
+            $params[] = trim($tmp[$i]);
+        }
+        return $params;
+    }
+
+
+    /**
+     * Strip parameters from a MIME type string.
+     *
+     * @param string $type MIME type string
+     *
+     * @return string MIME type with parameters removed
+     * @static
+     */
+    function stripParameters($type)
+    {
+        if (strstr($type, ';')) {
+            return substr($type, 0, strpos($type, ';'));
+        }
+        return $type;
+    }
+
+
+    /**
+     * Removes comments from a media type, subtype or parameter.
+     *
+     * @param string $string   String to strip comments from
+     * @param string &$comment Comment is stored in there.
+     *
+     * @return string   String without comments
+     * @static
+     */
+    function stripComments($string, &$comment)
+    {
+        if (strpos($string, '(') === false) {
+            return $string;
+        }
+
+        $inquote   = false;
+        $quoting   = false;
+        $incomment = 0;
+        $newstring = '';
+
+        for ($n = 0; $n < strlen($string); $n++) {
+            if ($quoting) {
+                if ($incomment == 0) {
+                    $newstring .= $string[$n];
+                } else if ($comment !== null) {
+                    $comment .= $string[$n];
+                }
+                $quoting = false;
+            } else if ($string[$n] == '\\') {
+                $quoting = true;
+            } else if (!$inquote && $incomment > 0 && $string[$n] == ')') {
+                $incomment--;
+                if ($incomment == 0 && $comment !== null) {
+                    $comment .= ' ';
+                }
+            } else if (!$inquote && $string[$n] == '(') {
+                $incomment++;
+            } else if ($string[$n] == '"') {
+                if ($inquote) {
+                    $inquote = false;
+                } else {
+                    $inquote = true;
+                }
+            } else if ($incomment == 0) {
+                $newstring .= $string[$n];
+            } else if ($comment !== null) {
+                $comment .= $string[$n];
+            }
+        }
+
+        if ($comment !== null) {
+            $comment = trim($comment);
+        }
+
+        return $newstring;
+    }
+
+
+    /**
+     * Get a MIME type's media
+     *
+     * @note 'media' refers to the portion before the first slash
+     *
+     * @param string $type MIME type to get media of
+     *
+     * @return string $type's media
+     * @static
+     */
+    function getMedia($type)
+    {
+        $tmp = explode('/', $type);
+        return strtolower(trim(MIME_Type::stripComments($tmp[0], $null)));
+    }
+
+
+    /**
+     * Get a MIME type's subtype
+     *
+     * @param string $type MIME type to get subtype of
+     *
+     * @return string $type's subtype, null if invalid mime type
+     * @static
+     */
+    function getSubType($type)
+    {
+        $tmp = explode('/', $type);
+        if (!isset($tmp[1])) {
+            return null;
+        }
+        $tmp = explode(';', $tmp[1]);
+        return strtolower(trim(MIME_Type::stripComments($tmp[0], $null)));
+    }
+
+
+    /**
+     * Create a textual MIME type from object values
+     *
+     * This function performs the opposite function of parse().
+     *
+     * @return string MIME type string
+     */
+    function get()
+    {
+        $type = strtolower($this->media . '/' . $this->subType);
+        if (count($this->parameters)) {
+            foreach ($this->parameters as $key => $null) {
+                $type .= '; ' . $this->parameters[$key]->get();
+            }
+        }
+        return $type;
+    }
+
+
+    /**
+     * Is this type experimental?
+     *
+     * @note Experimental types are denoted by a leading 'x-' in the media or
+     *       subtype, e.g. text/x-vcard or x-world/x-vrml.
+     *
+     * @param string $type MIME type to check
+     *
+     * @return boolean true if $type is experimental, false otherwise
+     * @static
+     */
+    function isExperimental($type)
+    {
+        if (substr(MIME_Type::getMedia($type), 0, 2) == 'x-' ||
+            substr(MIME_Type::getSubType($type), 0, 2) == 'x-') {
+            return true;
+        }
+        return false;
+    }
+
+
+    /**
+     * Is this a vendor MIME type?
+     *
+     * @note Vendor types are denoted with a leading 'vnd. in the subtype.
+     *
+     * @param string $type MIME type to check
+     *
+     * @return boolean true if $type is a vendor type, false otherwise
+     * @static
+     */
+    function isVendor($type)
+    {
+        if (substr(MIME_Type::getSubType($type), 0, 4) == 'vnd.') {
+            return true;
+        }
+        return false;
+    }
+
+
+    /**
+     * Is this a wildcard type?
+     *
+     * @param string $type MIME type to check
+     *
+     * @return boolean true if $type is a wildcard, false otherwise
+     * @static
+     */
+    function isWildcard($type)
+    {
+        if ($type == '*/*' || MIME_Type::getSubtype($type) == '*') {
+            return true;
+        }
+        return false;
+    }
+
+
+    /**
+     * Perform a wildcard match on a MIME type
+     *
+     * Example:
+     * MIME_Type::wildcardMatch('image/*', 'image/png')
+     *
+     * @param string $card Wildcard to check against
+     * @param string $type MIME type to check
+     *
+     * @return boolean true if there was a match, false otherwise
+     * @static
+     */
+    function wildcardMatch($card, $type)
+    {
+        if (!MIME_Type::isWildcard($card)) {
+            return false;
+        }
+
+        if ($card == '*/*') {
+            return true;
+        }
+
+        if (MIME_Type::getMedia($card) == MIME_Type::getMedia($type)) {
+            return true;
+        }
+
+        return false;
+    }
+
+
+    /**
+     * Add a parameter to this type
+     *
+     * @param string $name    Attribute name
+     * @param string $value   Attribute value
+     * @param string $comment Comment for this parameter
+     *
+     * @return void
+     */
+    function addParameter($name, $value, $comment = false)
+    {
+        $tmp = new MIME_Type_Parameter();
+
+        $tmp->name               = $name;
+        $tmp->value              = $value;
+        $tmp->comment            = $comment;
+        $this->parameters[$name] = $tmp;
+    }
+
+
+    /**
+     * Remove a parameter from this type
+     *
+     * @param string $name Parameter name
+     *
+     * @return void
+     */
+    function removeParameter($name)
+    {
+        unset($this->parameters[$name]);
+    }
+
+
+    /**
+     * Autodetect a file's MIME-type
+     *
+     * This function may be called staticly.
+     *
+     * @internal Tries to use fileinfo extension at first. If that
+     *  does not work, mime_magic is used. If this is also not available
+     *  or does not succeed, "file" command is tried to be executed with
+     *  System_Command. When that fails, too, then we use our in-built
+     *  extension-to-mimetype-mapping list.
+     *
+     * @param string $file   Path to the file to get the type of
+     * @param bool   $params Append MIME parameters if true
+     *
+     * @return string $file's MIME-type on success, PEAR_Error otherwise
+     *
+     * @since 1.0.0beta1
+     * @static
+     */
+    function autoDetect($file, $params = false)
+    {
+        // Sanity checks
+        if (!file_exists($file)) {
+            return PEAR::raiseError("File \"$file\" doesn't exist");
+        }
+
+        if (!is_readable($file)) {
+            return PEAR::raiseError("File \"$file\" is not readable");
+        }
+
+        if (function_exists('finfo_file')) {
+            $finfo = finfo_open(FILEINFO_MIME);
+            $type  = finfo_file($finfo, $file);
+            finfo_close($finfo);
+            if ($type !== false && $type !== '') {
+                return MIME_Type::_handleDetection($type, $params);
+            }
+        }
+
+        if (function_exists('mime_content_type')) {
+            $type = mime_content_type($file);
+            if ($type !== false && $type !== '') {
+                return MIME_Type::_handleDetection($type, $params);
+            }
+        }
+
+        @include_once 'System/Command.php';
+        if (class_exists('System_Command')) {
+            return MIME_Type::_handleDetection(
+                MIME_Type::_fileAutoDetect($file),
+                $params
+            );
+        }
+
+        require_once 'MIME/Type/Extension.php';
+        $mte = new MIME_Type_Extension();
+        return $mte->getMIMEType($file);
+    }
+
+
+    /**
+     * Handles a detected MIME type and modifies it if necessary.
+     *
+     * @param string $type   MIME Type of a file
+     * @param bool   $params Append MIME parameters if true
+     *
+     * @return string $file's MIME-type on success, PEAR_Error otherwise
+     */
+    function _handleDetection($type, $params)
+    {
+        // _fileAutoDetect() may have returned an error.
+        if (PEAR::isError($type)) {
+            return $type;
+        }
+
+        // Don't return an empty string
+        if (!$type || !strlen($type)) {
+            return PEAR::raiseError("Sorry, couldn't determine file type.");
+        }
+
+        // Strip parameters if present & requested
+        if (MIME_Type::hasParameters($type) && !$params) {
+            $type = MIME_Type::stripParameters($type);
+        }
+
+        return $type;
+    }
+
+
+    /**
+     * Autodetect a file's MIME-type with 'file' and System_Command
+     *
+     * This function may be called staticly.
+     *
+     * @param string $file Path to the file to get the type of
+     *
+     * @return string $file's MIME-type
+     *
+     * @since 1.0.0beta1
+     * @static
+     */
+    function _fileAutoDetect($file)
+    {
+        $cmd = new System_Command();
+
+        // Make sure we have the 'file' command.
+        $fileCmd = PEAR::getStaticProperty('MIME_Type', 'fileCmd');
+        if (!$cmd->which($fileCmd)) {
+            unset($cmd);
+            return PEAR::raiseError("Can't find file command \"{$fileCmd}\"");
+        }
+
+        $cmd->pushCommand($fileCmd, "-bi " . escapeshellarg($file));
+        $res = $cmd->execute();
+        unset($cmd);
+
+        return $res;
+    }
+}
+
diff --git a/extlib/MIME/Type/Extension.php b/extlib/MIME/Type/Extension.php
new file mode 100644 (file)
index 0000000..1987e2a
--- /dev/null
@@ -0,0 +1,298 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP versions 4 and 5                                                 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2009 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license,       |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available at through the world-wide-web at                           |
+// | http://www.php.net/license/3_0.txt.                                  |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Authors: Christian Schmidt <schmidt@php.net>                         |
+// +----------------------------------------------------------------------+
+//
+// $Id: Extension.php,v 1.1 2009/01/16 11:49:45 cweiske Exp $
+
+require_once 'PEAR.php';
+
+/**
+ * Class for mapping file extensions to MIME types.
+ *
+ * @category MIME
+ * @package  MIME_Type
+ * @author   Christian Schmidt <schmidt@php.net>
+ * @license  PHP License 3.0
+ * @version  1.2.0
+ * @link     http://pear.php.net/package/MIME_Type
+ */
+class MIME_Type_Extension
+{
+    /**
+     * Mapping between file extension and MIME type.
+     *
+     * @internal The array is sorted alphabetically by value and with primary
+     *  extension first. Be careful about not adding duplicate keys - PHP
+     *  silently ignores duplicates. The following command can be used for
+     *  checking for duplicates:
+     *    grep "=> '" Extension.php | cut -d\' -f2 | sort | uniq -d
+     *  application/octet-stream is generally used as fallback when no other
+     *  MIME-type can be found, but the array does not contain a lot of such
+     *  unknown extension. One entry exists, though, to allow detection of
+     *  file extension for this MIME-type.
+     *
+     * @var array
+     */
+    var $extensionToType = array (
+        'ez'        => 'application/andrew-inset',
+        'atom'      => 'application/atom+xml',
+        'jar'       => 'application/java-archive',
+        'hqx'       => 'application/mac-binhex40',
+        'cpt'       => 'application/mac-compactpro',
+        'mathml'    => 'application/mathml+xml',
+        'doc'       => 'application/msword',
+        'dat'       => 'application/octet-stream',
+        'oda'       => 'application/oda',
+        'ogg'       => 'application/ogg',
+        'pdf'       => 'application/pdf',
+        'ai'        => 'application/postscript',
+        'eps'       => 'application/postscript',
+        'ps'        => 'application/postscript',
+        'rdf'       => 'application/rdf+xml',
+        'rss'       => 'application/rss+xml',
+        'smi'       => 'application/smil',
+        'smil'      => 'application/smil',
+        'gram'      => 'application/srgs',
+        'grxml'     => 'application/srgs+xml',
+        'kml'       => 'application/vnd.google-earth.kml+xml',
+        'kmz'       => 'application/vnd.google-earth.kmz',
+        'mif'       => 'application/vnd.mif',
+        'xul'       => 'application/vnd.mozilla.xul+xml',
+        'xls'       => 'application/vnd.ms-excel',
+        'xlb'       => 'application/vnd.ms-excel',
+        'xlt'       => 'application/vnd.ms-excel',
+        'xlam'      => 'application/vnd.ms-excel.addin.macroEnabled.12',
+        'xlsb'      => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
+        'xlsm'      => 'application/vnd.ms-excel.sheet.macroEnabled.12',
+        'xltm'      => 'application/vnd.ms-excel.template.macroEnabled.12',
+        'docm'      => 'application/vnd.ms-word.document.macroEnabled.12',
+        'dotm'      => 'application/vnd.ms-word.template.macroEnabled.12',
+        'ppam'      => 'application/vnd.ms-powerpoint.addin.macroEnabled.12',
+        'pptm'      => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
+        'ppsm'      => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12',
+        'potm'      => 'application/vnd.ms-powerpoint.template.macroEnabled.12',
+        'ppt'       => 'application/vnd.ms-powerpoint',
+        'pps'       => 'application/vnd.ms-powerpoint',
+        'odc'       => 'application/vnd.oasis.opendocument.chart',
+        'odb'       => 'application/vnd.oasis.opendocument.database',
+        'odf'       => 'application/vnd.oasis.opendocument.formula',
+        'odg'       => 'application/vnd.oasis.opendocument.graphics',
+        'otg'       => 'application/vnd.oasis.opendocument.graphics-template',
+        'odi'       => 'application/vnd.oasis.opendocument.image',
+        'odp'       => 'application/vnd.oasis.opendocument.presentation',
+        'otp'       => 'application/vnd.oasis.opendocument.presentation-template',
+        'ods'       => 'application/vnd.oasis.opendocument.spreadsheet',
+        'ots'       => 'application/vnd.oasis.opendocument.spreadsheet-template',
+        'odt'       => 'application/vnd.oasis.opendocument.text',
+        'odm'       => 'application/vnd.oasis.opendocument.text-master',
+        'ott'       => 'application/vnd.oasis.opendocument.text-template',
+        'oth'       => 'application/vnd.oasis.opendocument.text-web',
+        'potx'      => 'application/vnd.openxmlformats-officedocument.presentationml.template',
+        'ppsx'      => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
+        'pptx'      => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
+        'xlsx'      => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+        'xltx'      => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
+        'docx'      => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+        'dotx'      => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
+        'vsd'       => 'application/vnd.visio',
+        'wbxml'     => 'application/vnd.wap.wbxml',
+        'wmlc'      => 'application/vnd.wap.wmlc',
+        'wmlsc'     => 'application/vnd.wap.wmlscriptc',
+        'vxml'      => 'application/voicexml+xml',
+        'bcpio'     => 'application/x-bcpio',
+        'vcd'       => 'application/x-cdlink',
+        'pgn'       => 'application/x-chess-pgn',
+        'cpio'      => 'application/x-cpio',
+        'csh'       => 'application/x-csh',
+        'dcr'       => 'application/x-director',
+        'dir'       => 'application/x-director',
+        'dxr'       => 'application/x-director',
+        'dvi'       => 'application/x-dvi',
+        'spl'       => 'application/x-futuresplash',
+        'tgz'       => 'application/x-gtar',
+        'gtar'      => 'application/x-gtar',
+        'hdf'       => 'application/x-hdf',
+        'js'        => 'application/x-javascript',
+        'skp'       => 'application/x-koan',
+        'skd'       => 'application/x-koan',
+        'skt'       => 'application/x-koan',
+        'skm'       => 'application/x-koan',
+        'latex'     => 'application/x-latex',
+        'nc'        => 'application/x-netcdf',
+        'cdf'       => 'application/x-netcdf',
+        'sh'        => 'application/x-sh',
+        'shar'      => 'application/x-shar',
+        'swf'       => 'application/x-shockwave-flash',
+        'sit'       => 'application/x-stuffit',
+        'sv4cpio'   => 'application/x-sv4cpio',
+        'sv4crc'    => 'application/x-sv4crc',
+        'tar'       => 'application/x-tar',
+        'tcl'       => 'application/x-tcl',
+        'tex'       => 'application/x-tex',
+        'texinfo'   => 'application/x-texinfo',
+        'texi'      => 'application/x-texinfo',
+        't'         => 'application/x-troff',
+        'tr'        => 'application/x-troff',
+        'roff'      => 'application/x-troff',
+        'man'       => 'application/x-troff-man',
+        'me'        => 'application/x-troff-me',
+        'ms'        => 'application/x-troff-ms',
+        'ustar'     => 'application/x-ustar',
+        'src'       => 'application/x-wais-source',
+        'xhtml'     => 'application/xhtml+xml',
+        'xht'       => 'application/xhtml+xml',
+        'xslt'      => 'application/xslt+xml',
+        'xml'       => 'application/xml',
+        'xsl'       => 'application/xml',
+        'dtd'       => 'application/xml-dtd',
+        'zip'       => 'application/zip',
+        'au'        => 'audio/basic',
+        'snd'       => 'audio/basic',
+        'mid'       => 'audio/midi',
+        'midi'      => 'audio/midi',
+        'kar'       => 'audio/midi',
+        'mpga'      => 'audio/mpeg',
+        'mp2'       => 'audio/mpeg',
+        'mp3'       => 'audio/mpeg',
+        'aif'       => 'audio/x-aiff',
+        'aiff'      => 'audio/x-aiff',
+        'aifc'      => 'audio/x-aiff',
+        'm3u'       => 'audio/x-mpegurl',
+        'wma'       => 'audio/x-ms-wma',
+        'wax'       => 'audio/x-ms-wax',
+        'ram'       => 'audio/x-pn-realaudio',
+        'ra'        => 'audio/x-pn-realaudio',
+        'rm'        => 'application/vnd.rn-realmedia',
+        'wav'       => 'audio/x-wav',
+        'pdb'       => 'chemical/x-pdb',
+        'xyz'       => 'chemical/x-xyz',
+        'bmp'       => 'image/bmp',
+        'cgm'       => 'image/cgm',
+        'gif'       => 'image/gif',
+        'ief'       => 'image/ief',
+        'jpeg'      => 'image/jpeg',
+        'jpg'       => 'image/jpeg',
+        'jpe'       => 'image/jpeg',
+        'png'       => 'image/png',
+        'svg'       => 'image/svg+xml',
+        'tiff'      => 'image/tiff',
+        'tif'       => 'image/tiff',
+        'djvu'      => 'image/vnd.djvu',
+        'djv'       => 'image/vnd.djvu',
+        'wbmp'      => 'image/vnd.wap.wbmp',
+        'ras'       => 'image/x-cmu-raster',
+        'ico'       => 'image/x-icon',
+        'pnm'       => 'image/x-portable-anymap',
+        'pbm'       => 'image/x-portable-bitmap',
+        'pgm'       => 'image/x-portable-graymap',
+        'ppm'       => 'image/x-portable-pixmap',
+        'rgb'       => 'image/x-rgb',
+        'xbm'       => 'image/x-xbitmap',
+        'psd'       => 'image/x-photoshop',
+        'xpm'       => 'image/x-xpixmap',
+        'xwd'       => 'image/x-xwindowdump',
+        'eml'       => 'message/rfc822',
+        'igs'       => 'model/iges',
+        'iges'      => 'model/iges',
+        'msh'       => 'model/mesh',
+        'mesh'      => 'model/mesh',
+        'silo'      => 'model/mesh',
+        'wrl'       => 'model/vrml',
+        'vrml'      => 'model/vrml',
+        'ics'       => 'text/calendar',
+        'ifb'       => 'text/calendar',
+        'css'       => 'text/css',
+        'csv'       => 'text/csv',
+        'html'      => 'text/html',
+        'htm'       => 'text/html',
+        'txt'       => 'text/plain',
+        'asc'       => 'text/plain',
+        'rtx'       => 'text/richtext',
+        'rtf'       => 'text/rtf',
+        'sgml'      => 'text/sgml',
+        'sgm'       => 'text/sgml',
+        'tsv'       => 'text/tab-separated-values',
+        'wml'       => 'text/vnd.wap.wml',
+        'wmls'      => 'text/vnd.wap.wmlscript',
+        'etx'       => 'text/x-setext',
+        'mpeg'      => 'video/mpeg',
+        'mpg'       => 'video/mpeg',
+        'mpe'       => 'video/mpeg',
+        'qt'        => 'video/quicktime',
+        'mov'       => 'video/quicktime',
+        'mxu'       => 'video/vnd.mpegurl',
+        'm4u'       => 'video/vnd.mpegurl',
+        'flv'       => 'video/x-flv',
+        'asf'       => 'video/x-ms-asf',
+        'asx'       => 'video/x-ms-asf',
+        'wmv'       => 'video/x-ms-wmv',
+        'wm'        => 'video/x-ms-wm',
+        'wmx'       => 'video/x-ms-wmx',
+        'avi'       => 'video/x-msvideo',
+        'ogv'       => 'video/ogg',
+        'movie'     => 'video/x-sgi-movie',
+        'ice'       => 'x-conference/x-cooltalk',
+    );
+
+
+
+    /**
+     * Autodetect a file's MIME-type.
+     *
+     * @param string $file Path to the file to get the type of
+     *
+     * @return string $file's MIME-type on success, PEAR_Error otherwise
+     */
+    function getMIMEType($file)
+    {
+        $extension = substr(strrchr($file, '.'), 1);
+        if ($extension === false) {
+            return PEAR::raiseError("File has no extension.");
+        }
+
+        if (!isset($this->extensionToType[$extension])) {
+            return PEAR::raiseError("Sorry, couldn't determine file type.");
+        }
+
+        return $this->extensionToType[$extension];
+    }
+
+
+
+    /**
+     * Return default MIME-type for the specified extension.
+     *
+     * @param string $type MIME-type
+     *
+     * @return string A file extension without leading period.
+     */
+    function getExtension($type)
+    {
+        require_once 'MIME/Type.php';
+        // Strip parameters and comments.
+        $type = MIME_Type::getMedia($type) . '/' . MIME_Type::getSubType($type);
+
+        $extension = array_search($type, $this->extensionToType);
+        if ($extension === false) {
+            return PEAR::raiseError("Sorry, couldn't determine extension.");
+        }
+        return $extension;
+    }
+
+}
+
+?>
\ No newline at end of file
diff --git a/extlib/MIME/Type/Parameter.php b/extlib/MIME/Type/Parameter.php
new file mode 100644 (file)
index 0000000..399d3dd
--- /dev/null
@@ -0,0 +1,163 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2002 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license,       |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available at through the world-wide-web at                           |
+// | http://www.php.net/license/3_0.txt.                                  |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Authors: Ian Eure <ieure@php.net>                                    |
+// +----------------------------------------------------------------------+
+//
+// $Id: Parameter.php,v 1.1 2007/03/25 10:10:21 cweiske Exp $
+
+/**
+ * Class for working with MIME type parameters
+ *
+ * @version 1.2.0
+ * @package MIME_Type
+ * @author Ian Eure <ieure@php.net>
+ */
+class MIME_Type_Parameter {
+    /**
+     * Parameter name
+     *
+     * @var string
+     */
+    var $name;
+
+    /**
+     * Parameter value
+     *
+     * @var string
+     */
+    var $value;
+
+    /**
+     * Parameter comment
+     *
+     * @var string
+     */
+    var $comment;
+
+
+    /**
+     * Constructor.
+     *
+     * @param  string $param MIME parameter to parse, if set.
+     * @return void
+     */
+    function MIME_Type_Parameter($param = false)
+    {
+        if ($param) {
+            $this->parse($param);
+        }
+    }
+
+
+    /**
+     * Parse a MIME type parameter and set object fields
+     *
+     * @param  string $param MIME type parameter to parse
+     * @return void
+     */
+    function parse($param)
+    {
+        $comment = '';
+        $param   = MIME_Type::stripComments($param, $comment);
+        $this->name    = $this->getAttribute($param);
+        $this->value   = $this->getValue($param);
+        $this->comment = $comment;
+    }
+
+
+    /**
+     * Get a parameter attribute (e.g. name)
+     *
+     * @param  string MIME type parameter
+     * @return string Attribute name
+     * @static
+     */
+    function getAttribute($param)
+    {
+        $tmp = explode('=', $param);
+        return trim($tmp[0]);
+    }
+
+
+    /**
+     * Get a parameter value
+     *
+     * @param  string $param MIME type parameter
+     * @return string Value
+     * @static
+     */
+    function getValue($param)
+    {
+        $tmp = explode('=', $param, 2);
+        $value = $tmp[1];
+        $value = trim($value);
+        if ($value[0] == '"' && $value[strlen($value)-1] == '"') {
+            $value = substr($value, 1, -1);
+        }
+        $value = str_replace('\\"', '"', $value);
+        return $value;
+    }
+
+
+    /**
+     * Get a parameter comment
+     *
+     * @param  string $param MIME type parameter
+     * @return string Parameter comment
+     * @see getComment()
+     * @static
+     */
+    function getComment($param)
+    {
+        $cs = strpos($param, '(');
+        $comment = substr($param, $cs);
+        return trim($comment, '() ');
+    }
+
+
+    /**
+     * Does this parameter have a comment?
+     *
+     * @param  string  $param MIME type parameter
+     * @return boolean true if $param has a comment, false otherwise
+     * @static
+     */
+    function hasComment($param)
+    {
+        if (strstr($param, '(')) {
+            return true;
+        }
+        return false;
+    }
+
+
+    /**
+     * Get a string representation of this parameter
+     *
+     * This function performs the oppsite of parse()
+     *
+     * @return string String representation of parameter
+     */
+    function get()
+    {
+        $val = $this->name . '="' . str_replace('"', '\\"', $this->value) . '"';
+        if ($this->comment) {
+            $val .= ' (' . $this->comment . ')';
+        }
+        return $val;
+    }
+}
+?>
\ No newline at end of file
index 0ce46442deb99e3d928aed6bfa16c96b1a488401..ede8d6277f2ebab784ccf5c49cf365b37d7f2992 100644 (file)
@@ -163,6 +163,40 @@ $config =
         array('run' => 'web',
               'frequency' => 10000,
               'reporturl' => 'http://laconi.ca/stats/report'),
+        'attachments' =>
+        array('supported' => array('image/png',
+            'image/jpeg',
+            'image/gif',
+            'image/svg+xml',
+            'audio/mpeg',
+            'application/ogg',
+            'application/pdf',
+            'application/vnd.oasis.opendocument.text',
+            'application/vnd.oasis.opendocument.text-template',
+            'application/vnd.oasis.opendocument.graphics',
+            'application/vnd.oasis.opendocument.graphics-template',
+            'application/vnd.oasis.opendocument.presentation',
+            'application/vnd.oasis.opendocument.presentation-template',
+            'application/vnd.oasis.opendocument.spreadsheet',
+            'application/vnd.oasis.opendocument.spreadsheet-template',
+            'application/vnd.oasis.opendocument.chart',
+            'application/vnd.oasis.opendocument.chart-template',
+            'application/vnd.oasis.opendocument.image',
+            'application/vnd.oasis.opendocument.image-template',
+            'application/vnd.oasis.opendocument.formula',
+            'application/vnd.oasis.opendocument.formula-template',
+            'application/vnd.oasis.opendocument.text-master',
+            'application/vnd.oasis.opendocument.text-web',
+            'application/zip',
+            'text/plain',
+            'video/mpeg',
+            'video/mp4',
+            'video/quicktime',
+            'video/mpeg'),
+        'file_quota' => 5000000,
+        'user_quota' => 50000000,
+        'monthly_quota' => 15000000,
+        ),
         );
 
 $config['db'] = &PEAR::getStaticProperty('DB_DataObject','options');
@@ -223,19 +257,19 @@ if ($_db_name != 'laconica' && !array_key_exists('ini_'.$_db_name, $config['db']
 
 // XXX: how many of these could be auto-loaded on use?
 
-require_once('Validate.php');
-require_once('markdown.php');
+require_once 'Validate.php';
+require_once 'markdown.php';
 
-require_once(INSTALLDIR.'/lib/util.php');
-require_once(INSTALLDIR.'/lib/action.php');
-require_once(INSTALLDIR.'/lib/theme.php');
-require_once(INSTALLDIR.'/lib/mail.php');
-require_once(INSTALLDIR.'/lib/subs.php');
-require_once(INSTALLDIR.'/lib/Shorturl_api.php');
-require_once(INSTALLDIR.'/lib/twitter.php');
+require_once INSTALLDIR.'/lib/util.php';
+require_once INSTALLDIR.'/lib/action.php';
+require_once INSTALLDIR.'/lib/theme.php';
+require_once INSTALLDIR.'/lib/mail.php';
+require_once INSTALLDIR.'/lib/subs.php';
+require_once INSTALLDIR.'/lib/Shorturl_api.php';
+require_once INSTALLDIR.'/lib/twitter.php';
 
-require_once(INSTALLDIR.'/lib/clientexception.php');
-require_once(INSTALLDIR.'/lib/serverexception.php');
+require_once INSTALLDIR.'/lib/clientexception.php';
+require_once INSTALLDIR.'/lib/serverexception.php';
 
 // XXX: other formats here