]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Merge branch '0.8.x' into 0.9.x
authorSarven Capadisli <csarven@status.net>
Wed, 30 Sep 2009 10:54:17 +0000 (10:54 +0000)
committerSarven Capadisli <csarven@status.net>
Wed, 30 Sep 2009 10:54:17 +0000 (10:54 +0000)
145 files changed:
EVENTS.txt
README
actions/accesstoken.php
actions/all.php
actions/allrss.php
actions/api.php
actions/confirmaddress.php
actions/deletenotice.php
actions/doc.php
actions/editgroup.php
actions/favorited.php
actions/favoritesrss.php
actions/finishaddopenid.php [deleted file]
actions/finishopenidlogin.php [deleted file]
actions/finishremotesubscribe.php
actions/grouprss.php
actions/groupsearch.php
actions/invite.php
actions/login.php
actions/logout.php
actions/newgroup.php
actions/newmessage.php
actions/newnotice.php
actions/noticesearch.php
actions/openidlogin.php [deleted file]
actions/openidsettings.php [deleted file]
actions/othersettings.php
actions/postnotice.php
actions/profilesettings.php
actions/public.php
actions/publicrss.php
actions/publictagcloud.php
actions/publicxrds.php [deleted file]
actions/register.php
actions/remotesubscribe.php
actions/replies.php
actions/repliesrss.php
actions/requesttoken.php
actions/showfavorites.php
actions/showgroup.php
actions/shownotice.php
actions/showstream.php
actions/subscribers.php
actions/twitapidirect_messages.php
actions/twitapigroups.php
actions/twitapistatuses.php
actions/updateprofile.php
actions/userauthorization.php
actions/userrss.php
actions/xrds.php
classes/Config.php [new file with mode: 0644]
classes/Deleted_notice.php [new file with mode: 0644]
classes/File.php
classes/File_oembed.php
classes/File_redirection.php
classes/Message.php
classes/Notice.php
classes/Profile.php
classes/User.php
classes/User_group.php
classes/User_openid.php [deleted file]
classes/User_role.php [new file with mode: 0644]
classes/statusnet.ini
config.php.sample
db/08to09.sql [new file with mode: 0644]
db/08to09_pg.sql [new file with mode: 0644]
db/statusnet.sql
db/statusnet_pg.sql
doc-src/help
doc-src/openid [deleted file]
extlib/libomb/base_url_xrds_mapper.php [new file with mode: 0755]
extlib/libomb/constants.php [new file with mode: 0644]
extlib/libomb/datastore.php [new file with mode: 0755]
extlib/libomb/helper.php [new file with mode: 0644]
extlib/libomb/invalidparameterexception.php [new file with mode: 0755]
extlib/libomb/invalidyadisexception.php [new file with mode: 0755]
extlib/libomb/notice.php [new file with mode: 0755]
extlib/libomb/omb_yadis_xrds.php [new file with mode: 0755]
extlib/libomb/plain_xrds_writer.php [new file with mode: 0755]
extlib/libomb/profile.php [new file with mode: 0755]
extlib/libomb/remoteserviceexception.php [new file with mode: 0755]
extlib/libomb/service_consumer.php [new file with mode: 0755]
extlib/libomb/service_provider.php [new file with mode: 0755]
extlib/libomb/unsupportedserviceexception.php [new file with mode: 0755]
extlib/libomb/xrds_mapper.php [new file with mode: 0755]
extlib/libomb/xrds_writer.php [new file with mode: 0755]
index.php
install.php
js/util.js
lib/Shorturl_api.php
lib/accountsettingsaction.php
lib/action.php
lib/command.php
lib/commandinterpreter.php
lib/common.php
lib/curlclient.php [new file with mode: 0644]
lib/default.php [new file with mode: 0644]
lib/deleteaction.php [deleted file]
lib/facebookaction.php
lib/facebookutil.php
lib/groupeditform.php
lib/httpclient.php [new file with mode: 0644]
lib/logingroupnav.php
lib/messageform.php
lib/noticeform.php
lib/noticelist.php
lib/oauthstore.php
lib/omb.php
lib/openid.php [deleted file]
lib/plugin.php
lib/right.php [new file with mode: 0644]
lib/router.php
lib/rssaction.php
lib/settingsaction.php
lib/twitter.php
lib/twitterapi.php
lib/twitteroauthclient.php
lib/unqueuemanager.php
lib/util.php
plugins/LilUrl/LilUrlPlugin.php [new file with mode: 0644]
plugins/OpenID/OpenIDPlugin.php [new file with mode: 0644]
plugins/OpenID/User_openid.php [new file with mode: 0644]
plugins/OpenID/doc-src/openid [new file with mode: 0644]
plugins/OpenID/finishaddopenid.php [new file with mode: 0644]
plugins/OpenID/finishopenidlogin.php [new file with mode: 0644]
plugins/OpenID/openid.php [new file with mode: 0644]
plugins/OpenID/openidlogin.php [new file with mode: 0644]
plugins/OpenID/openidsettings.php [new file with mode: 0644]
plugins/OpenID/publicxrds.php [new file with mode: 0644]
plugins/PtitUrl/PtitUrlPlugin.php [new file with mode: 0644]
plugins/PubSubHubBub/PubSubHubBubPlugin.php [new file with mode: 0644]
plugins/PubSubHubBub/publisher.php [new file with mode: 0644]
plugins/SimpleUrl/SimpleUrlPlugin.php [new file with mode: 0644]
plugins/TightUrl/TightUrlPlugin.php [new file with mode: 0644]
scripts/createsim.php
scripts/getvaliddaemons.php
scripts/maildaemon.php
scripts/ombqueuehandler.php
scripts/pluginqueuehandler.php [new file with mode: 0755]
scripts/startdaemons.sh
scripts/xmppdaemon.php
tests/HashTagDetectionTests.php
tests/URLDetectionTest.php
tests/UserRightsTest.php [new file with mode: 0644]
theme/readme.txt

index 68cb28603b644ea092a58a18fe91877c4c4d26af..74923dcc0a8301132a34c09e4a0542ce215885ae 100644 (file)
@@ -32,10 +32,10 @@ StartShowLaconicaStyles: backwards compatibility; deprecated
 EndShowLaconicaStyles: backwards compatibility; deprecated
 - $action: the current action
 
-StartShowUAStyles: Showing custom UA Style links
+StartShowUAStyles: Showing custom User-Agent style links
 - $action: the current action
 
-EndShowUAStyles: End showing custom UA Style links; good place to add user-agent (e.g., filter, -webkit, -moz) specific styles
+EndShowUAStyles: End showing custom User-Agent links; good place to add user-agent (e.g., filter, -webkit, -moz) specific styles
 - $action: the current action
 
 StartShowScripts: Showing JavaScript links
@@ -134,3 +134,146 @@ StartAddressData: Allows the site owner to provide additional information about
 
 EndAddressData: At the end of <address>
 - $action: the current action
+
+StartLoginGroupNav: Before showing the login and register navigation menu
+- $action: the current action
+
+EndLoginGroupNav: After showing the login and register navigation menu
+- $action: the current action
+
+StartAccountSettingsNav: Before showing the account settings menu
+- $action: the current action
+
+EndAccountSettingsNav: After showing the account settings menu
+- $action: the current action
+
+Autoload: When trying to autoload a class
+- $cls: the class being sought. A plugin might require_once the file for the class.
+
+SensitiveAction: determines if an action is 'sensitive' and should use SSL
+- $action: name of the action, like 'login'
+- $sensitive: flag for whether this is a sensitive action
+
+LoginAction: determines if an action is a 'login' action (OK for public view in private mode)
+- $action: name of the action, like 'register'
+- $login: flag for whether this is a login action
+
+StartShowHead: called before showing the <head> element and children
+- $action: action object being show
+
+EndShowHead: called after showing the <head> element (and </head>)
+- $action: action object being shown
+
+StartShowBody: called before showing the <body> element and children
+- $action: action object being shown
+
+EndShowBody: called after showing the <body> element (and </body>)
+- $action: action object being shown
+
+StartPersonalGroupNav: beginning of personal group nav menu
+- $action: action object being shown
+
+EndPersonalGroupNav: end of personal group nav menu (good place to add a menu item)
+- $action: action object being shown
+
+StartEndHTML: just before the </html> tag
+- $action: action object being shown
+
+EndEndHTML: just after the </html> tag
+- $action: action object being shown
+
+StartShowDesign: just before showing a site, user, or group design
+- $action: action object being shown
+
+EndShowDesign: just after showing a site, user, or group design
+- $action: action object being shown
+
+StartShowExportData: just before showing the <div> with export data (feeds)
+- $action: action object being shown
+
+EndShowExportData: just after showing the <div> with export data (feeds)
+- $action: action object being shown
+
+StartShowNoticeItem: just before showing the notice item
+- $action: action object being shown
+
+EndShowNoticeItem: just after showing the notice item
+- $action: action object being shown
+
+StartShowPageNotice: just before showing the page notice (instructions or error)
+- $action: action object being shown
+
+EndShowPageNotice: just after showing the page notice (instructions or error)
+- $action: action object being shown
+
+StartShowPageTitle: just before showing the main h1 title of a page (only for registration)
+- $action: action object being shown
+
+StartProfileFormData: just before showing text entry fields on profile settings page
+- $action: action object being shown
+
+EndProfileFormData: just after showing text entry fields on profile settings page
+- $action: action object being shown
+
+StartProfileSaveForm: before starting to save a profile settings form
+- $action: action object being shown
+
+EndProfileSaveForm: after saving a profile settings form (after commit, no profile or user object!)
+- $action: action object being shown
+
+StartRegistrationFormData: just before showing text entry fields on registration page
+- $action: action object being shown
+
+EndRegistrationFormData: just after showing text entry fields on registration page
+- $action: action object being shown
+
+StartRegistrationTry: before validating and saving a new user
+- $action: action object being shown
+
+EndRegistrationTry: after saving a new user (note: no profile or user object!)
+- $action: action object being shown
+
+StartNewQueueManager: before trying to start a new queue manager; good for plugins implementing new queue manager classes
+- $qm: empty queue manager to set
+
+RedirectToLogin: event when we force a redirect to login (like when going to a settings page on a remembered login)
+- $action: action object being shown
+- $user: current user
+
+StartLoadDoc: before loading a help doc (hook this to show your own documentation)
+- $title: title of the document
+- $output: HTML output to show
+
+EndLoadDoc: after loading a help doc (hook this to modify other documentation)
+- $title: title of the document
+- $output: HTML output to show
+
+StartApiRss: after the rss <channel> element is started
+- $action: action object being shown
+
+StartApiAtom: after the <feed> element is started
+- $action: action object being shown
+
+StartEnqueueNotice: about to add a notice to the queues (good place to add a new transport)
+- $notice: the notice being added
+- &$transports: modifiable list of transports (as strings) to queue for
+
+EndEnqueueNotice: after adding a notice to the queues
+- $notice: the notice being added
+- $transports: modifiable list of transports to use
+
+UnqueueHandleNotice: Handle a notice when no queue manager is available
+- $notice: the notice to handle
+- $queue: the "queue" that is being executed
+
+GetValidDaemons: Just before determining which daemons to run
+- &$daemons: modifiable list of daemon scripts to run, filenames relative to scripts/
+
+HandleQueuedNotice: Handle a queued notice at queue time (or immediately if no queue)
+- &$notice: notice to handle
+
+StartShowHeadElements: Right after the <head> tag
+- $action: the current action
+
+EndShowHeadElements: Right before the </head> tag; put <script>s here if you need them in <head>
+- $action: the current action
diff --git a/README b/README
index 75621998111fa1c3f38fe8e30c03d0cdffdc02f2..f3b2528b85ab6bd1932ce997badd5266efb65f73 100644 (file)
--- a/README
+++ b/README
@@ -968,8 +968,6 @@ closed: If set to 'true', will disallow registration on your site.
        the service, *then* set this variable to 'true'.
 inviteonly: If set to 'true', will only allow registration if the user
            was invited by an existing user.
-openidonly: If set to 'true', will only allow registrations and logins
-           through OpenID.
 private: If set to 'true', anonymous users will be redirected to the
          'login' page. Also, API methods that normally require no
          authentication will require it. Note that this does not turn
@@ -997,6 +995,9 @@ shorturllength: Length of URL at which URLs in a message exceeding 140
 dupelimit: minimum time allowed for one person to say the same thing
            twice. Default 60s. Anything lower is considered a user
            or UI error.
+textlimit: default max size for texts in the site. Defaults to 140.
+           0 means no limit. Can be fine-tuned for notices, messages,
+           profile bios and group descriptions.
 
 db
 --
@@ -1197,14 +1198,6 @@ For configuring invites.
 
 enabled: Whether to allow users to send invites. Default true.
 
-openid
-------
-
-For configuring OpenID.
-
-enabled: Whether to allow users to register and login using OpenID. Default
-        true.
-
 tag
 ---
 
@@ -1331,6 +1324,8 @@ banned: an array of usernames and/or profile IDs of 'banned' profiles.
         The site will reject any notices by these users -- they will
         not be accepted at all. (Compare with blacklisted users above,
         whose posts just won't show up in the public stream.)
+biolimit: max character length of bio; 0 means no limit; null means to use
+          the site text limit default.
 
 newuser
 -------
@@ -1427,6 +1422,9 @@ Options for group functionality.
 
 maxaliases: maximum number of aliases a group can have. Default 3. Set
             to 0 or less to prevent aliases in a group.
+desclimit: maximum number of characters to allow in group descriptions.
+           null (default) means to use the site-wide text limits. 0
+           means no limit.
 
 oohembed
 --------
@@ -1505,6 +1503,24 @@ linkcolor: Hex color of all links.
 backgroundimage: Image to use for the background.
 disposition: Flags for whether or not to tile the background image.
 
+notice
+------
+
+Configuration options specific to notices.
+
+contentlimit: max length of the plain-text content of a notice.
+              Default is null, meaning to use the site-wide text limit.
+              0 means no limit.
+
+message
+-------
+
+Configuration options specific to messages.
+
+contentlimit: max length of the plain-text content of a message.
+              Default is null, meaning to use the site-wide text limit.
+              0 means no limit.
+
 Plugins
 =======
 
index c99aaeded32ab74b544d1a532ec9630c0a784b3d..76bd40473a954729f43fd2f53bbc852292b35446 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Access token class.
+ * Access token class
  *
  * PHP version 5
  *
@@ -32,10 +32,11 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
     exit(1);
 }
 
+require_once INSTALLDIR.'/extlib/libomb/service_provider.php';
 require_once INSTALLDIR.'/lib/omb.php';
 
 /**
- * Access token class.
+ * Access token class
  *
  * @category Action
  * @package  StatusNet
@@ -47,28 +48,23 @@ require_once INSTALLDIR.'/lib/omb.php';
 class AccesstokenAction extends Action
 {
     /**
-     * Class handler.
+     * Class handler
      *
      * @param array $args query arguments
      *
-     * @return boolean false if user doesn't exist
-     */
+     * @return nothing
+     *
+     **/
     function handle($args)
     {
         parent::handle($args);
         try {
-            common_debug('getting request from env variables', __FILE__);
-            common_remove_magic_from_request();
-            $req = OAuthRequest::from_request('POST', common_local_url('accesstoken'));
-            common_debug('getting a server', __FILE__);
-            $server = omb_oauth_server();
-            common_debug('fetching the access token', __FILE__);
-            $token = $server->fetch_access_token($req);
-            common_debug('got this token: "'.print_r($token, true).'"', __FILE__);
-            common_debug('printing the access token', __FILE__);
-            print $token;
-        } catch (OAuthException $e) {
+            $srv = new OMB_Service_Provider(null, omb_oauth_datastore(),
+                                            omb_oauth_server());
+            $srv->writeAccessToken();
+        } catch (Exception $e) {
             $this->serverError($e->getMessage());
         }
     }
 }
+?>
index bfde3a7e4ad051a392841abb910a449ab717dc2a..f1786462e161e9d55cbadbb806a3362ee09269cb 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-/*
+/**
  * StatusNet - the distributed open-source microblogging tool
  * Copyright (C) 2008, 2009, StatusNet, Inc.
  *
  *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category Actions
+ * @package  Actions
+ * @author   Evan Prodromou <evan@status.net>
+ * @author   Mike Cochrane <mikec@mikenz.geek.nz>
+ * @author   Robin Millette <millette@controlyourself.ca>
+ * @author   Adrian Lang <mail@adrianlang.de>
+ * @author   Meitar Moscovitz <meitarm@gmail.com>
+ * @author   Sarven Capadisli <csarven@status.net>
+ * @author   Craig Andrews <candrews@integralblue.com>
+ * @author   Jeffery To <jeffery.to@gmail.com>
+ * @author   Zach Copley <zach@controlyourself.ca>
+ * @license  GNU Affero General Public License http://www.gnu.org/licenses/
+ * @link     http://status.net
  */
 
-if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+    exit(1);
+}
 
 require_once INSTALLDIR.'/lib/personalgroupnav.php';
 require_once INSTALLDIR.'/lib/noticelist.php';
@@ -43,8 +59,8 @@ class AllAction extends ProfileAction
             $this->notice = $this->user->noticesWithFriends(($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1);
         }
 
-        if($this->page > 1 && $this->notice->N == 0){
-            $this->serverError(_('No such page'),$code=404);
+        if ($this->page > 1 && $this->notice->N == 0) {
+            $this->serverError(_('No such page'), $code = 404);
         }
 
         return true;
@@ -73,20 +89,33 @@ class AllAction extends ProfileAction
 
     function getFeeds()
     {
-        return array(new Feed(Feed::RSS1,
-                              common_local_url('allrss', array('nickname' =>
-                                                               $this->user->nickname)),
-                              sprintf(_('Feed for friends of %s (RSS 1.0)'), $this->user->nickname)),
-                     new Feed(Feed::RSS2,
-                              common_local_url('api', array('apiaction' => 'statuses',
-                                                            'method' => 'friends_timeline',
-                                                            'argument' => $this->user->nickname.'.rss')),
-                              sprintf(_('Feed for friends of %s (RSS 2.0)'), $this->user->nickname)),
-                     new Feed(Feed::ATOM,
-                              common_local_url('api', array('apiaction' => 'statuses',
-                                                            'method' => 'friends_timeline',
-                                                            'argument' => $this->user->nickname.'.atom')),
-                              sprintf(_('Feed for friends of %s (Atom)'), $this->user->nickname)));
+        return array(
+            new Feed(Feed::RSS1,
+                common_local_url(
+                    'allrss', array(
+                        'nickname' =>
+                        $this->user->nickname)
+                ),
+                sprintf(_('Feed for friends of %s (RSS 1.0)'), $this->user->nickname)),
+            new Feed(Feed::RSS2,
+                common_local_url(
+                    'api', array(
+                        'apiaction' => 'statuses',
+                        'method' => 'friends_timeline',
+                        'argument' => $this->user->nickname.'.rss'
+                    )
+                ),
+                sprintf(_('Feed for friends of %s (RSS 2.0)'), $this->user->nickname)),
+            new Feed(Feed::ATOM,
+                common_local_url(
+                    'api', array(
+                        'apiaction' => 'statuses',
+                        'method' => 'friends_timeline',
+                        'argument' => $this->user->nickname.'.atom'
+                    )
+                ),
+                sprintf(_('Feed for friends of %s (Atom)'), $this->user->nickname))
+        );
     }
 
     function showLocalNav()
@@ -106,11 +135,8 @@ class AllAction extends ProfileAction
             } else {
                 $message .= sprintf(_('You can try to [nudge %s](../%s) from his profile or [post something to his or her attention](%%%%action.newnotice%%%%?status_textarea=%s).'), $this->user->nickname, $this->user->nickname, '@' . $this->user->nickname);
             }
-        }
-        else {
-            $message .= sprintf(_('Why not [register an account](%%%%action.%s%%%%) and then nudge %s or post a notice to his or her attention.'),
-                                (!common_config('site','openidonly')) ? 'register' : 'openidlogin',
-                                $this->user->nickname);
+        } else {
+            $message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to his or her attention.'), $this->user->nickname);
         }
 
         $this->elementStart('div', 'guide');
@@ -128,17 +154,19 @@ class AllAction extends ProfileAction
             $this->showEmptyListMessage();
         }
 
-        $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE,
-                          $this->page, 'all', array('nickname' => $this->user->nickname));
+        $this->pagination(
+            $this->page > 1, $cnt > NOTICES_PER_PAGE,
+            $this->page, 'all', array('nickname' => $this->user->nickname)
+        );
     }
 
     function showPageTitle()
     {
         $user =& common_current_user();
         if ($user && ($user->id == $this->user->id)) {
-            $this->element('h1', NULL, _("You and friends"));
+            $this->element('h1', null, _("You and friends"));
         } else {
-            $this->element('h1', NULL, sprintf(_('%s and friends'), $this->user->nickname));
+            $this->element('h1', null, sprintf(_('%s and friends'), $this->user->nickname));
         }
     }
 
index 57efb73f0e85275df28109accaabd2aae2cc110a..28b1be27d82a254d82bebca9943138576b9e3927 100644 (file)
@@ -68,6 +68,7 @@ class AllrssAction extends Rss10Action
             $this->clientError(_('No such user.'));
             return false;
         } else {
+            $this->notices = $this->getNotices($this->limit);
             return true;
         }
     }
index 3705d035c4a22c1813a8ddb6f7f852ce12ccc6c0..1bc90de1108cb2f385eb97c8ffbb145dc5e0d946 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-/*
+/**
  * StatusNet - the distributed open-source microblogging tool
  * Copyright (C) 2008, 2009, StatusNet, Inc.
  *
  *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program.     If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category Actions
+ * @package  Actions
+ * @author   Evan Prodromou <evan@status.net>
+ * @author   Brenda Wallace <shiny@cpan.org>
+ * @author   Jeffery To <jeffery.to@gmail.com>
+ * @author   Robin Millette <millette@controlyourself.ca>
+ * @author   Tom Adams <tom@holizz.com>
+ * @author   Christopher Vollick <psycotica0@gmail.com>
+ * @author   CiaranG <ciaran@ciarang.com>
+ * @author   Craig Andrews <candrews@integralblue.com>
+ * @author   Gina Haeussge <osd@foosel.net>
+ * @author   Mike Cochrane <mikec@mikenz.geek.nz>
+ * @author   Sarven Capadisli <csarven@status.net>
+ * @license  GNU Affero General Public License http://www.gnu.org/licenses/
+ * @link     http://status.net
  */
 
-if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+    exit(1);
+}
 
 class ApiAction extends Action
 {
@@ -37,7 +55,7 @@ class ApiAction extends Action
         $this->api_action = $this->arg('apiaction');
         $method = $this->arg('method');
         $argument = $this->arg('argument');
-       $this->basic_auth_process_header();
+        $this->basic_auth_process_header();
 
         if (isset($argument)) {
             $cmdext = explode('.', $argument);
@@ -46,7 +64,7 @@ class ApiAction extends Action
             $this->content_type = strtolower($cmdext[1]);
         } else {
 
-            Requested format / content-type will be an extension on the method
+            //Requested format / content-type will be an extension on the method
             $cmdext = explode('.', $method);
             $this->api_method = $cmdext[0];
             $this->content_type = strtolower($cmdext[1]);
@@ -55,10 +73,10 @@ class ApiAction extends Action
         if ($this->requires_auth()) {
             if (!isset($this->auth_user)) {
 
-                This header makes basic auth go
+                //This header makes basic auth go
                 header('WWW-Authenticate: Basic realm="StatusNet API"');
 
-                If the user hits cancel -- bam!
+                //If the user hits cancel -- bam!
                 $this->show_basic_auth_error();
             } else {
                 $nickname = $this->auth_user;
@@ -69,7 +87,7 @@ class ApiAction extends Action
                     $this->user = $user;
                     $this->process_command();
                 } else {
-                    basic authentication failed
+                    //basic authentication failed
                     list($proxy, $ip) = common_client_ip();
 
                     common_log(LOG_WARNING, "Failed API auth attempt, nickname = $nickname, proxy = $proxy, ip = $ip.");
@@ -84,7 +102,7 @@ class ApiAction extends Action
                 if ($user) {
                     $this->user = $user;
                 }
-                Twitter doesn't throw an error if the user isn't found
+                //Twitter doesn't throw an error if the user isn't found
             }
 
             $this->process_command();
@@ -97,7 +115,7 @@ class ApiAction extends Action
         $actionfile = INSTALLDIR."/actions/$action.php";
 
         if (file_exists($actionfile)) {
-            require_once($actionfile);
+            include_once $actionfile;
             $action_class = ucfirst($action)."Action";
             $action_obj = new $action_class();
 
@@ -113,10 +131,10 @@ class ApiAction extends Action
 
                 call_user_func(array($action_obj, $this->api_method), $_REQUEST, $apidata);
             } else {
-                $this->clientError("API method not found!", $code=404);
+                $this->clientError("API method not found!", $code = 404);
             }
         } else {
-            $this->clientError("API method not found!", $code=404);
+            $this->clientError("API method not found!", $code = 404);
         }
     }
 
@@ -185,10 +203,11 @@ class ApiAction extends Action
             $user_id     = $this->arg('user_id');
             $screen_name = $this->arg('screen_name');
 
-            if (empty($this->api_arg) &&
-                empty($id)            &&
-                empty($user_id)       &&
-                empty($screen_name)) {
+            if (empty($this->api_arg)
+                && empty($id)
+                && empty($user_id)
+                && empty($screen_name)
+            ) {
                 return true;
             } else {
                 return false;
@@ -209,35 +228,29 @@ class ApiAction extends Action
 
     function basic_auth_process_header()
     {
-       if(isset($_SERVER['AUTHORIZATION']) || isset($_SERVER['HTTP_AUTHORIZATION']))
-       {
-               $authorization_header = isset($_SERVER['HTTP_AUTHORIZATION'])?$_SERVER['HTTP_AUTHORIZATION']:$_SERVER['AUTHORIZATION'];
-       }
-
-       if(isset($_SERVER['PHP_AUTH_USER']))
-       {
-               $this->auth_user = $_SERVER['PHP_AUTH_USER'];
-               $this->auth_pw = $_SERVER['PHP_AUTH_PW'];
-       }
-       elseif ( isset($authorization_header) && strstr(substr($authorization_header, 0,5),'Basic')  )
-       {
-               // decode the HTTP_AUTHORIZATION header on php-cgi server self
-               // on fcgid server the header name is AUTHORIZATION
-
-               $auth_hash = base64_decode( substr($authorization_header, 6) );
-               list($this->auth_user, $this->auth_pw) = explode(':', $auth_hash);
-
-               // set all to NULL on a empty basic auth request
-               if($this->auth_user == "") {
-                       $this->auth_user = NULL;
-                       $this->auth_pw = NULL;
-               }
-       }
-       else
-       {
-               $this->auth_user = NULL;
-               $this->auth_pw = NULL;
-       }
+        if (isset($_SERVER['AUTHORIZATION']) || isset($_SERVER['HTTP_AUTHORIZATION'])) {
+            $authorization_header = isset($_SERVER['HTTP_AUTHORIZATION'])? $_SERVER['HTTP_AUTHORIZATION'] : $_SERVER['AUTHORIZATION'];
+        }
+
+        if (isset($_SERVER['PHP_AUTH_USER'])) {
+            $this->auth_user = $_SERVER['PHP_AUTH_USER'];
+            $this->auth_pw = $_SERVER['PHP_AUTH_PW'];
+        } elseif (isset($authorization_header) && strstr(substr($authorization_header, 0, 5), 'Basic')) {
+            // decode the HTTP_AUTHORIZATION header on php-cgi server self
+            // on fcgid server the header name is AUTHORIZATION
+
+            $auth_hash = base64_decode(substr($authorization_header, 6));
+            list($this->auth_user, $this->auth_pw) = explode(':', $auth_hash);
+
+            // set all to null on a empty basic auth request
+            if ($this->auth_user == "") {
+                $this->auth_user = null;
+                $this->auth_pw = null;
+            }
+        } else {
+            $this->auth_user = null;
+            $this->auth_pw = null;
+        }
     }
 
     function show_basic_auth_error()
@@ -253,7 +266,7 @@ class ApiAction extends Action
             $this->element('request', null, $_SERVER['REQUEST_URI']);
             $this->elementEnd('hash');
             $this->endXML();
-        } else if ($this->content_type == 'json')  {
+        } else if ($this->content_type == 'json') {
             header('Content-Type: application/json; charset=utf-8');
             $error_array = array('error' => $msg, 'request' => $_SERVER['REQUEST_URI']);
             print(json_encode($error_array));
index 2016942862197af7def453190a3429ee17436051..6fd74f3ff7889bf40e55d9df9343edecb0d10e6c 100644 (file)
@@ -67,11 +67,7 @@ class ConfirmaddressAction extends Action
         parent::handle($args);
         if (!common_logged_in()) {
             common_set_returnto($this->selfUrl());
-            if (!common_config('site', 'openidonly')) {
-                common_redirect(common_local_url('login'));
-            } else {
-                common_redirect(common_local_url('openidlogin'));
-            }
+            common_redirect(common_local_url('login'));
             return;
         }
         $code = $this->trimmed('code');
index 3d040f2fa91ed7b7e736c5be7d744d61667aeaea..4a48a9c346cba2b9714e30184c65273d8ae56632 100644 (file)
@@ -32,15 +32,45 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
     exit(1);
 }
 
-require_once INSTALLDIR.'/lib/deleteaction.php';
-
-class DeletenoticeAction extends DeleteAction
+class DeletenoticeAction extends Action
 {
-    var $error = null;
+    var $error        = null;
+    var $user         = null;
+    var $notice       = null;
+    var $profile      = null;
+    var $user_profile = null;
+
+    function prepare($args)
+    {
+        parent::prepare($args);
+
+        $this->user   = common_current_user();
+        $notice_id    = $this->trimmed('notice');
+        $this->notice = Notice::staticGet($notice_id);
+
+        if (!$this->notice) {
+            common_user_error(_('No such notice.'));
+            exit;
+        }
+
+        $this->profile      = $this->notice->getProfile();
+        $this->user_profile = $this->user->getProfile();
+
+        return true;
+    }
 
     function handle($args)
     {
         parent::handle($args);
+
+        if (!common_logged_in()) {
+            common_user_error(_('Not logged in.'));
+            exit;
+        } else if ($this->notice->profile_id != $this->user_profile->id &&
+                   !$this->user->hasRight(Right::deleteOthersNotice)) {
+            common_user_error(_('Can\'t delete this notice.'));
+            exit;
+        }
         // XXX: Ajax!
 
         if ($_SERVER['REQUEST_METHOD'] == 'POST') {
index 68295234c5ca1c7a2f6d74fe2f922ac730de9cc5..836f039d32128d9696d5bbc07b598155fd803218 100644 (file)
@@ -58,12 +58,24 @@ class DocAction extends Action
     function handle($args)
     {
         parent::handle($args);
-        $this->title    = $this->trimmed('title');
-        $this->filename = INSTALLDIR.'/doc-src/'.$this->title;
-        if (!file_exists($this->filename)) {
-            $this->clientError(_('No such document.'));
-            return;
+
+        $this->title  = $this->trimmed('title');
+        $this->output = null;
+
+        if (Event::handle('StartLoadDoc', array(&$this->title, &$this->output))) {
+
+            $this->filename = INSTALLDIR.'/doc-src/'.$this->title;
+            if (!file_exists($this->filename)) {
+                $this->clientError(_('No such document.'));
+                return;
+            }
+
+            $c = file_get_contents($this->filename);
+            $this->output = common_markup_to_html($c);
+
+            Event::handle('EndLoadDoc', array($this->title, &$this->output));
         }
+
         $this->showPage();
     }
 
@@ -93,9 +105,7 @@ class DocAction extends Action
      */
     function showContent()
     {
-        $c      = file_get_contents($this->filename);
-        $output = common_markup_to_html($c);
-        $this->raw($output);
+        $this->raw($this->output);
     }
 
     /**
index b8dac31cb180421a565e62c2e9d10e5cbdadaba7..5dd039f8a30c3dc55d4d64f9994890e5e1bfe8c2 100644 (file)
@@ -202,8 +202,8 @@ class EditgroupAction extends GroupDesignAction
         } else if (!is_null($fullname) && mb_strlen($fullname) > 255) {
             $this->showForm(_('Full name is too long (max 255 chars).'));
             return;
-        } else if (!is_null($description) && mb_strlen($description) > 140) {
-            $this->showForm(_('description is too long (max 140 chars).'));
+        } else if (User_group::descriptionTooLong($description)) {
+            $this->showForm(sprintf(_('description is too long (max %d chars).'), User_group::maxDescription()));
             return;
         } else if (!is_null($location) && mb_strlen($location) > 255) {
             $this->showForm(_('Location is too long (max 255 chars).'));
index 5ba508cdf5e592bfb95ecd16444c0c180fd92cbe..150b67b0b09451f752d226486031de07852e32b7 100644 (file)
@@ -153,8 +153,7 @@ class FavoritedAction extends Action
             $message .= _('Be the first to add a notice to your favorites by clicking the fave button next to any notice you like.');
         }
         else {
-            $message .= sprintf(_('Why not [register an account](%%%%action.%s%%%%) and be the first to add a notice to your favorites!'),
-                                (!common_config('site','openidonly')) ? 'register' : 'openidlogin');
+            $message .= _('Why not [register an account](%%action.register%%) and be the first to add a notice to your favorites!');
         }
 
         $this->elementStart('div', 'guide');
index 2d5ce985416d9e9d83f32b9f9d1a55082d7f6ebe..62f06e841b193ddd2cb4f94fbb47a3c6c151050e 100644 (file)
@@ -50,11 +50,11 @@ require_once INSTALLDIR.'/lib/rssaction.php';
  */
 class FavoritesrssAction extends Rss10Action
 {
-    
+
     /** The user whose favorites to display */
-    
+
     var $user = null;
-        
+
     /**
      * Find the user to display by supplied nickname
      *
@@ -66,7 +66,7 @@ class FavoritesrssAction extends Rss10Action
     function prepare($args)
     {
         parent::prepare($args);
-        
+
         $nickname   = $this->trimmed('nickname');
         $this->user = User::staticGet('nickname', $nickname);
 
@@ -74,10 +74,11 @@ class FavoritesrssAction extends Rss10Action
             $this->clientError(_('No such user.'));
             return false;
         } else {
+            $this->notices = $this->getNotices($this->limit);
             return true;
         }
     }
-    
+
     /**
      * Get notices
      *
diff --git a/actions/finishaddopenid.php b/actions/finishaddopenid.php
deleted file mode 100644 (file)
index b6de4f2..0000000
+++ /dev/null
@@ -1,185 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Complete adding an OpenID
- *
- * PHP version 5
- *
- * LICENCE: This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Settings
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2008-2009 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
-    exit(1);
-}
-
-require_once INSTALLDIR.'/lib/openid.php';
-
-/**
- * Complete adding an OpenID
- *
- * Handle the return from an OpenID verification
- *
- * @category Settings
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- */
-
-class FinishaddopenidAction extends Action
-{
-    var $msg = null;
-
-    /**
-     * Handle the redirect back from OpenID confirmation
-     *
-     * Check to see if the user's logged in, and then try
-     * to use the OpenID login system.
-     *
-     * @param array $args $_REQUEST arguments
-     *
-     * @return void
-     */
-
-    function handle($args)
-    {
-        parent::handle($args);
-        if (!common_logged_in()) {
-            $this->clientError(_('Not logged in.'));
-        } else {
-            $this->tryLogin();
-        }
-    }
-
-    /**
-     * Try to log in using OpenID
-     *
-     * Check the OpenID for validity; potentially store it.
-     *
-     * @return void
-     */
-
-    function tryLogin()
-    {
-        $consumer =& oid_consumer();
-
-        $response = $consumer->complete(common_local_url('finishaddopenid'));
-
-        if ($response->status == Auth_OpenID_CANCEL) {
-            $this->message(_('OpenID authentication cancelled.'));
-            return;
-        } else if ($response->status == Auth_OpenID_FAILURE) {
-            // Authentication failed; display the error message.
-            $this->message(sprintf(_('OpenID authentication failed: %s'),
-                                   $response->message));
-        } else if ($response->status == Auth_OpenID_SUCCESS) {
-
-            $display   = $response->getDisplayIdentifier();
-            $canonical = ($response->endpoint && $response->endpoint->canonicalID) ?
-              $response->endpoint->canonicalID : $display;
-
-            $sreg_resp = Auth_OpenID_SRegResponse::fromSuccessResponse($response);
-
-            if ($sreg_resp) {
-                $sreg = $sreg_resp->contents();
-            }
-
-            $cur =& common_current_user();
-
-            $other = oid_get_user($canonical);
-
-            if ($other) {
-                if ($other->id == $cur->id) {
-                    $this->message(_('You already have this OpenID!'));
-                } else {
-                    $this->message(_('Someone else already has this OpenID.'));
-                }
-                return;
-            }
-
-            // start a transaction
-
-            $cur->query('BEGIN');
-
-            $result = oid_link_user($cur->id, $canonical, $display);
-
-            if (!$result) {
-                $this->message(_('Error connecting user.'));
-                return;
-            }
-            if ($sreg) {
-                if (!oid_update_user($cur, $sreg)) {
-                    $this->message(_('Error updating profile'));
-                    return;
-                }
-            }
-
-            // success!
-
-            $cur->query('COMMIT');
-
-            oid_set_last($display);
-
-            common_redirect(common_local_url('openidsettings'), 303);
-        }
-    }
-
-    /**
-     * Show a failure message
-     *
-     * Something went wrong. Save the message, and show the page.
-     *
-     * @param string $msg Error message to show
-     *
-     * @return void
-     */
-
-    function message($msg)
-    {
-        $this->message = $msg;
-        $this->showPage();
-    }
-
-    /**
-     * Title of the page
-     *
-     * @return string title
-     */
-
-    function title()
-    {
-        return _('OpenID Login');
-    }
-
-    /**
-     * Show error message
-     *
-     * @return void
-     */
-
-    function showPageNotice()
-    {
-        if ($this->message) {
-            $this->element('p', 'error', $this->message);
-        }
-    }
-}
diff --git a/actions/finishopenidlogin.php b/actions/finishopenidlogin.php
deleted file mode 100644 (file)
index 9ac0369..0000000
+++ /dev/null
@@ -1,497 +0,0 @@
-<?php
-/*
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2008, 2009, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
-
-require_once(INSTALLDIR.'/lib/openid.php');
-
-class FinishopenidloginAction extends Action
-{
-    var $error = null;
-    var $username = null;
-    var $message = null;
-
-    function handle($args)
-    {
-        parent::handle($args);
-        if (!common_config('openid', 'enabled')) {
-            common_redirect(common_local_url('login'));
-        } else if (common_is_real_login()) {
-            $this->clientError(_('Already logged in.'));
-        } else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
-            $token = $this->trimmed('token');
-            if (!$token || $token != common_session_token()) {
-                $this->showForm(_('There was a problem with your session token. Try again, please.'));
-                return;
-            }
-            if ($this->arg('create')) {
-                if (!$this->boolean('license')) {
-                    $this->showForm(_('You can\'t register if you don\'t agree to the license.'),
-                                    $this->trimmed('newname'));
-                    return;
-                }
-                $this->createNewUser();
-            } else if ($this->arg('connect')) {
-                $this->connectUser();
-            } else {
-                common_debug(print_r($this->args, true), __FILE__);
-                $this->showForm(_('Something weird happened.'),
-                                $this->trimmed('newname'));
-            }
-        } else {
-            $this->tryLogin();
-        }
-    }
-
-    function showPageNotice()
-    {
-        if ($this->error) {
-            $this->element('div', array('class' => 'error'), $this->error);
-        } else {
-            $this->element('div', 'instructions',
-                           sprintf(_('This is the first time you\'ve logged into %s so we must connect your OpenID to a local account. You can either create a new account, or connect with your existing account, if you have one.'), common_config('site', 'name')));
-        }
-    }
-
-    function title()
-    {
-        return _('OpenID Account Setup');
-    }
-
-    function showForm($error=null, $username=null)
-    {
-        $this->error = $error;
-        $this->username = $username;
-
-        $this->showPage();
-    }
-
-    function showContent()
-    {
-        if (!empty($this->message_text)) {
-            $this->element('div', array('class' => 'error'), $this->message_text);
-            return;
-        }
-
-        $this->elementStart('form', array('method' => 'post',
-                                          'id' => 'account_connect',
-                                          'action' => common_local_url('finishopenidlogin')));
-        $this->hidden('token', common_session_token());
-        $this->element('h2', null,
-                       _('Create new account'));
-        $this->element('p', null,
-                       _('Create a new user with this nickname.'));
-        $this->input('newname', _('New nickname'),
-                     ($this->username) ? $this->username : '',
-                     _('1-64 lowercase letters or numbers, no punctuation or spaces'));
-        $this->elementStart('p');
-        $this->element('input', array('type' => 'checkbox',
-                                      'id' => 'license',
-                                      'name' => 'license',
-                                      'value' => 'true'));
-        $this->text(_('My text and files are available under '));
-        $this->element('a', array('href' => common_config('license', 'url')),
-                       common_config('license', 'title'));
-        $this->text(_(' except this private data: password, email address, IM address, phone number.'));
-        $this->elementEnd('p');
-        $this->submit('create', _('Create'));
-        $this->element('h2', null,
-                       _('Connect existing account'));
-        $this->element('p', null,
-                       _('If you already have an account, login with your username and password to connect it to your OpenID.'));
-        $this->input('nickname', _('Existing nickname'));
-        $this->password('password', _('Password'));
-        $this->submit('connect', _('Connect'));
-        $this->elementEnd('form');
-    }
-
-    function tryLogin()
-    {
-        $consumer = oid_consumer();
-
-        $response = $consumer->complete(common_local_url('finishopenidlogin'));
-
-        if ($response->status == Auth_OpenID_CANCEL) {
-            $this->message(_('OpenID authentication cancelled.'));
-            return;
-        } else if ($response->status == Auth_OpenID_FAILURE) {
-            // Authentication failed; display the error message.
-            $this->message(sprintf(_('OpenID authentication failed: %s'), $response->message));
-        } else if ($response->status == Auth_OpenID_SUCCESS) {
-            // This means the authentication succeeded; extract the
-            // identity URL and Simple Registration data (if it was
-            // returned).
-            $display = $response->getDisplayIdentifier();
-            $canonical = ($response->endpoint->canonicalID) ?
-              $response->endpoint->canonicalID : $response->getDisplayIdentifier();
-
-            $sreg_resp = Auth_OpenID_SRegResponse::fromSuccessResponse($response);
-
-            if ($sreg_resp) {
-                $sreg = $sreg_resp->contents();
-            }
-
-            $user = oid_get_user($canonical);
-
-            if ($user) {
-                oid_set_last($display);
-                # XXX: commented out at @edd's request until better
-                # control over how data flows from OpenID provider.
-                # oid_update_user($user, $sreg);
-                common_set_user($user);
-                common_real_login(true);
-                if (isset($_SESSION['openid_rememberme']) && $_SESSION['openid_rememberme']) {
-                    common_rememberme($user);
-                }
-                unset($_SESSION['openid_rememberme']);
-                $this->goHome($user->nickname);
-            } else {
-                $this->saveValues($display, $canonical, $sreg);
-                $this->showForm(null, $this->bestNewNickname($display, $sreg));
-            }
-        }
-    }
-
-    function message($msg)
-    {
-        $this->message_text = $msg;
-        $this->showPage();
-    }
-
-    function saveValues($display, $canonical, $sreg)
-    {
-        common_ensure_session();
-        $_SESSION['openid_display'] = $display;
-        $_SESSION['openid_canonical'] = $canonical;
-        $_SESSION['openid_sreg'] = $sreg;
-    }
-
-    function getSavedValues()
-    {
-        return array($_SESSION['openid_display'],
-                     $_SESSION['openid_canonical'],
-                     $_SESSION['openid_sreg']);
-    }
-
-    function createNewUser()
-    {
-        # FIXME: save invite code before redirect, and check here
-
-        if (common_config('site', 'closed')) {
-            $this->clientError(_('Registration not allowed.'));
-            return;
-        }
-
-        $invite = null;
-
-        if (common_config('site', 'inviteonly')) {
-            $code = $_SESSION['invitecode'];
-            if (empty($code)) {
-                $this->clientError(_('Registration not allowed.'));
-                return;
-            }
-
-            $invite = Invitation::staticGet($code);
-
-            if (empty($invite)) {
-                $this->clientError(_('Not a valid invitation code.'));
-                return;
-            }
-        }
-
-        $nickname = $this->trimmed('newname');
-
-        if (!Validate::string($nickname, array('min_length' => 1,
-                                               'max_length' => 64,
-                                               'format' => NICKNAME_FMT))) {
-            $this->showForm(_('Nickname must have only lowercase letters and numbers and no spaces.'));
-            return;
-        }
-
-        if (!User::allowed_nickname($nickname)) {
-            $this->showForm(_('Nickname not allowed.'));
-            return;
-        }
-
-        if (User::staticGet('nickname', $nickname)) {
-            $this->showForm(_('Nickname already in use. Try another one.'));
-            return;
-        }
-
-        list($display, $canonical, $sreg) = $this->getSavedValues();
-
-        if (!$display || !$canonical) {
-            $this->serverError(_('Stored OpenID not found.'));
-            return;
-        }
-
-        # Possible race condition... let's be paranoid
-
-        $other = oid_get_user($canonical);
-
-        if ($other) {
-            $this->serverError(_('Creating new account for OpenID that already has a user.'));
-            return;
-        }
-
-        $location = '';
-        if (!empty($sreg['country'])) {
-            if ($sreg['postcode']) {
-                # XXX: use postcode to get city and region
-                # XXX: also, store postcode somewhere -- it's valuable!
-                $location = $sreg['postcode'] . ', ' . $sreg['country'];
-            } else {
-                $location = $sreg['country'];
-            }
-        }
-
-        if (!empty($sreg['fullname']) && mb_strlen($sreg['fullname']) <= 255) {
-            $fullname = $sreg['fullname'];
-        } else {
-            $fullname = '';
-        }
-
-        if (!empty($sreg['email']) && Validate::email($sreg['email'], true)) {
-            $email = $sreg['email'];
-        } else {
-            $email = '';
-        }
-
-        # XXX: add language
-        # XXX: add timezone
-
-        $args = array('nickname' => $nickname,
-                      'email' => $email,
-                      'fullname' => $fullname,
-                      'location' => $location);
-
-        if (!empty($invite)) {
-            $args['code'] = $invite->code;
-        }
-
-        $user = User::register($args);
-
-        $result = oid_link_user($user->id, $canonical, $display);
-
-        oid_set_last($display);
-        common_set_user($user);
-        common_real_login(true);
-        if (isset($_SESSION['openid_rememberme']) && $_SESSION['openid_rememberme']) {
-            common_rememberme($user);
-        }
-        unset($_SESSION['openid_rememberme']);
-        common_redirect(common_local_url('showstream', array('nickname' => $user->nickname)),
-                        303);
-    }
-
-    function connectUser()
-    {
-        $nickname = $this->trimmed('nickname');
-        $password = $this->trimmed('password');
-
-        if (!common_check_user($nickname, $password)) {
-            $this->showForm(_('Invalid username or password.'));
-            return;
-        }
-
-        # They're legit!
-
-        $user = User::staticGet('nickname', $nickname);
-
-        list($display, $canonical, $sreg) = $this->getSavedValues();
-
-        if (!$display || !$canonical) {
-            $this->serverError(_('Stored OpenID not found.'));
-            return;
-        }
-
-        $result = oid_link_user($user->id, $canonical, $display);
-
-        if (!$result) {
-            $this->serverError(_('Error connecting user to OpenID.'));
-            return;
-        }
-
-        oid_update_user($user, $sreg);
-        oid_set_last($display);
-        common_set_user($user);
-        common_real_login(true);
-        if (isset($_SESSION['openid_rememberme']) && $_SESSION['openid_rememberme']) {
-            common_rememberme($user);
-        }
-        unset($_SESSION['openid_rememberme']);
-        $this->goHome($user->nickname);
-    }
-
-    function goHome($nickname)
-    {
-        $url = common_get_returnto();
-        if ($url) {
-            # We don't have to return to it again
-            common_set_returnto(null);
-        } else {
-            $url = common_local_url('all',
-                                    array('nickname' =>
-                                          $nickname));
-        }
-        common_redirect($url, 303);
-    }
-
-    function bestNewNickname($display, $sreg)
-    {
-
-        # Try the passed-in nickname
-
-        if (!empty($sreg['nickname'])) {
-            $nickname = $this->nicknamize($sreg['nickname']);
-            if ($this->isNewNickname($nickname)) {
-                return $nickname;
-            }
-        }
-
-        # Try the full name
-
-        if (!empty($sreg['fullname'])) {
-            $fullname = $this->nicknamize($sreg['fullname']);
-            if ($this->isNewNickname($fullname)) {
-                return $fullname;
-            }
-        }
-
-        # Try the URL
-
-        $from_url = $this->openidToNickname($display);
-
-        if ($from_url && $this->isNewNickname($from_url)) {
-            return $from_url;
-        }
-
-        # XXX: others?
-
-        return null;
-    }
-
-    function isNewNickname($str)
-    {
-        if (!Validate::string($str, array('min_length' => 1,
-                                          'max_length' => 64,
-                                          'format' => NICKNAME_FMT))) {
-            return false;
-        }
-        if (!User::allowed_nickname($str)) {
-            return false;
-        }
-        if (User::staticGet('nickname', $str)) {
-            return false;
-        }
-        return true;
-    }
-
-    function openidToNickname($openid)
-    {
-        if (Auth_Yadis_identifierScheme($openid) == 'XRI') {
-            return $this->xriToNickname($openid);
-        } else {
-            return $this->urlToNickname($openid);
-        }
-    }
-
-    # We try to use an OpenID URL as a legal StatusNet user name in this order
-    # 1. Plain hostname, like http://evanp.myopenid.com/
-    # 2. One element in path, like http://profile.typekey.com/EvanProdromou/
-    #    or http://getopenid.com/evanprodromou
-
-    function urlToNickname($openid)
-    {
-        static $bad = array('query', 'user', 'password', 'port', 'fragment');
-
-        $parts = parse_url($openid);
-
-        # If any of these parts exist, this won't work
-
-        foreach ($bad as $badpart) {
-            if (array_key_exists($badpart, $parts)) {
-                return null;
-            }
-        }
-
-        # We just have host and/or path
-
-        # If it's just a host...
-        if (array_key_exists('host', $parts) &&
-            (!array_key_exists('path', $parts) || strcmp($parts['path'], '/') == 0))
-        {
-            $hostparts = explode('.', $parts['host']);
-
-            # Try to catch common idiom of nickname.service.tld
-
-            if ((count($hostparts) > 2) &&
-                (strlen($hostparts[count($hostparts) - 2]) > 3) && # try to skip .co.uk, .com.au
-                (strcmp($hostparts[0], 'www') != 0))
-            {
-                return $this->nicknamize($hostparts[0]);
-            } else {
-                # Do the whole hostname
-                return $this->nicknamize($parts['host']);
-            }
-        } else {
-            if (array_key_exists('path', $parts)) {
-                # Strip starting, ending slashes
-                $path = preg_replace('@/$@', '', $parts['path']);
-                $path = preg_replace('@^/@', '', $path);
-                if (strpos($path, '/') === false) {
-                    return $this->nicknamize($path);
-                }
-            }
-        }
-
-        return null;
-    }
-
-    function xriToNickname($xri)
-    {
-        $base = $this->xriBase($xri);
-
-        if (!$base) {
-            return null;
-        } else {
-            # =evan.prodromou
-            # or @gratis*evan.prodromou
-            $parts = explode('*', substr($base, 1));
-            return $this->nicknamize(array_pop($parts));
-        }
-    }
-
-    function xriBase($xri)
-    {
-        if (substr($xri, 0, 6) == 'xri://') {
-            return substr($xri, 6);
-        } else {
-            return $xri;
-        }
-    }
-
-    # Given a string, try to make it work as a nickname
-
-    function nicknamize($str)
-    {
-        $str = preg_replace('/\W/', '', $str);
-        return strtolower($str);
-    }
-}
index 871bc3d2d131c3d8fbd7b1c33deb598ca461ebfc..b1cec66f48af55ac41de11b0fab041433cf3b093 100644 (file)
@@ -1,5 +1,16 @@
 <?php
-/*
+/**
+ * Handler for remote subscription finish callback
+ *
+ * PHP version 5
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @author   Robin Millette <millette@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
  * StatusNet - the distributed open-source microblogging tool
  * Copyright (C) 2008, 2009, StatusNet, Inc.
  *
  *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+ **/
 
 if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
 
-require_once(INSTALLDIR.'/lib/omb.php');
+require_once INSTALLDIR.'/extlib/libomb/service_consumer.php';
+require_once INSTALLDIR.'/lib/omb.php';
 
+/**
+ * Handler for remote subscription finish callback
+ *
+ * When a remote user subscribes a local user, a redirect to this action is
+ * issued after the remote user authorized his service to subscribe.
+ *
+ * @category Action
+ * @package  Laconica
+ * @author   Evan Prodromou <evan@status.net>
+ * @author   Robin Millette <millette@controlyourself.ca>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://laconi.ca/
+ */
 class FinishremotesubscribeAction extends Action
 {
 
+    /**
+     * Class handler.
+     *
+     * @param array $args query arguments
+     *
+     * @return nothing
+     *
+     **/
     function handle($args)
     {
-
         parent::handle($args);
 
-        if (common_logged_in()) {
-            $this->clientError(_('You can use the local subscription!'));
-            return;
-        }
-
-        $omb = $_SESSION['oauth_authorization_request'];
+        /* Restore session data. RemotesubscribeAction should have stored
+           this entry. */
+        $service  = unserialize($_SESSION['oauth_authorization_request']);
 
-        if (!$omb) {
+        if (!$service) {
             $this->clientError(_('Not expecting this response!'));
             return;
         }
 
-        common_debug('stored request: '.print_r($omb,true), __FILE__);
-
-        common_remove_magic_from_request();
-        $req = OAuthRequest::from_request('POST', common_local_url('finishuserauthorization'));
-
-        $token = $req->get_parameter('oauth_token');
-
-        # I think this is the success metric
-
-        if ($token != $omb['token']) {
-            $this->clientError(_('Not authorized.'));
-            return;
-        }
-
-        $version = $req->get_parameter('omb_version');
-
-        if ($version != OMB_VERSION_01) {
-            $this->clientError(_('Unknown version of OMB protocol.'));
-            return;
-        }
-
-        $nickname = $req->get_parameter('omb_listener_nickname');
-
-        if (!$nickname) {
-            $this->clientError(_('No nickname provided by remote server.'));
-            return;
-        }
-
-        $profile_url = $req->get_parameter('omb_listener_profile');
+        common_debug('stored request: '. print_r($service, true), __FILE__);
 
-        if (!$profile_url) {
-            $this->clientError(_('No profile URL returned by server.'));
-            return;
-        }
-
-        if (!Validate::uri($profile_url, array('allowed_schemes' => array('http', 'https')))) {
-            $this->clientError(_('Invalid profile URL returned by server.'));
-            return;
-        }
-
-        if ($profile_url == common_local_url('showstream', array('nickname' => $nickname))) {
-            $this->clientError(_('You can use the local subscription!'));
-            return;
-        }
-
-        common_debug('listenee: "'.$omb['listenee'].'"', __FILE__);
-
-        $user = User::staticGet('nickname', $omb['listenee']);
+        /* Create user objects for both users. Do it early for request
+           validation. */
+        $user = User::staticGet('uri', $service->getListeneeURI());
 
         if (!$user) {
-            $this->clientError(_('User being listened to doesn\'t exist.'));
+            $this->clientError(_('User being listened to does not exist.'));
             return;
         }
 
-        $other = User::staticGet('uri', $omb['listener']);
+        $other = User::staticGet('uri', $service->getListenerURI());
 
         if ($other) {
             $this->clientError(_('You can use the local subscription!'));
             return;
         }
 
-        $fullname = $req->get_parameter('omb_listener_fullname');
-        $homepage = $req->get_parameter('omb_listener_homepage');
-        $bio = $req->get_parameter('omb_listener_bio');
-        $location = $req->get_parameter('omb_listener_location');
-        $avatar_url = $req->get_parameter('omb_listener_avatar');
+        $remote = Remote_profile::staticGet('uri', $service->getListenerURI());
 
-        list($newtok, $newsecret) = $this->access_token($omb);
+        $profile = Profile::staticGet($remote->id);
 
-        if (!$newtok || !$newsecret) {
-            $this->clientError(_('Couldn\'t convert request tokens to access tokens.'));
+        if ($user->hasBlocked($profile)) {
+            $this->clientError(_('That user has blocked you from subscribing.'));
             return;
         }
 
-        # XXX: possible attack point; subscribe and return someone else's profile URI
-
-        $remote = Remote_profile::staticGet('uri', $omb['listener']);
-
-        if ($remote) {
-            $exists = true;
-            $profile = Profile::staticGet($remote->id);
-            $orig_remote = clone($remote);
-            $orig_profile = clone($profile);
-            # XXX: compare current postNotice and updateProfile URLs to the ones
-            # stored in the DB to avoid (possibly...) above attack
-        } else {
-            $exists = false;
-            $remote = new Remote_profile();
-            $remote->uri = $omb['listener'];
-            $profile = new Profile();
-        }
-
-        $profile->nickname = $nickname;
-        $profile->profileurl = $profile_url;
-
-        if (!is_null($fullname)) {
-            $profile->fullname = $fullname;
-        }
-        if (!is_null($homepage)) {
-            $profile->homepage = $homepage;
-        }
-        if (!is_null($bio)) {
-            $profile->bio = $bio;
-        }
-        if (!is_null($location)) {
-            $profile->location = $location;
-        }
-
-        if ($exists) {
-            $profile->update($orig_profile);
-        } else {
-            $profile->created = DB_DataObject_Cast::dateTime(); # current time
-            $id = $profile->insert();
-            if (!$id) {
-                $this->serverError(_('Error inserting new profile'));
+        /* Perform the handling itself via libomb. */
+        try {
+            $service->finishAuthorization();
+        } catch (OAuthException $e) {
+            if ($e->getMessage() == 'The authorized token does not equal the ' .
+                                    'submitted token.') {
+                $this->clientError(_('You are not authorized.'));
                 return;
-            }
-            $remote->id = $id;
-        }
-
-        if ($avatar_url) {
-            if (!$this->add_avatar($profile, $avatar_url)) {
-                $this->serverError(_('Error inserting avatar'));
-                return;
-            }
-        }
-
-        $remote->postnoticeurl = $omb['post_notice_url'];
-        $remote->updateprofileurl = $omb['update_profile_url'];
-
-        if ($exists) {
-            if (!$remote->update($orig_remote)) {
-                $this->serverError(_('Error updating remote profile'));
+            } else {
+                $this->clientError(_('Could not convert request token to ' .
+                                     'access token.'));
                 return;
             }
-        } else {
-            $remote->created = DB_DataObject_Cast::dateTime(); # current time
-            if (!$remote->insert()) {
-                $this->serverError(_('Error inserting remote profile'));
-                return;
-            }
-        }
-
-        if ($user->hasBlocked($profile)) {
-            $this->clientError(_('That user has blocked you from subscribing.'));
+        } catch (OMB_RemoteServiceException $e) {
+            $this->clientError(_('Remote service uses unknown version of ' .
+                                 'OMB protocol.'));
+            return;
+        } catch (Exception $e) {
+            common_debug('Got exception ' . print_r($e, true), __FILE__);
+            $this->clientError($e->getMessage());
             return;
         }
 
-        $sub = new Subscription();
-
-        $sub->subscriber = $remote->id;
-        $sub->subscribed = $user->id;
-
-        $sub_exists = false;
-
-        if ($sub->find(true)) {
-            $sub_exists = true;
-            $orig_sub = clone($sub);
-        } else {
-            $sub_exists = false;
-            $sub->created = DB_DataObject_Cast::dateTime(); # current time
-        }
-
-        $sub->token = $newtok;
-        $sub->secret = $newsecret;
+        /* The service URLs are not accessible from datastore, so setting them
+           after insertion of the profile. */
+        $orig_remote = clone($remote);
 
-        if ($sub_exists) {
-            $result = $sub->update($orig_sub);
-        } else {
-            $result = $sub->insert();
-        }
+        $remote->postnoticeurl    =
+                            $service->getServiceURI(OMB_ENDPOINT_POSTNOTICE);
+        $remote->updateprofileurl =
+                            $service->getServiceURI(OMB_ENDPOINT_UPDATEPROFILE);
 
-        if (!$result) {
-            common_log_db_error($sub, ($sub_exists) ? 'UPDATE' : 'INSERT', __FILE__);
-            $this->clientError(_('Couldn\'t insert new subscription.'));
-            return;
+        if (!$remote->update($orig_remote)) {
+                $this->serverError(_('Error updating remote profile'));
+                return;
         }
 
-        # Notify user, if necessary
-
-        mail_subscribe_notify_profile($user, $profile);
-
-        # Clear the data
+        /* Clear the session data. */
         unset($_SESSION['oauth_authorization_request']);
 
-        # If we show subscriptions in reverse chron order, this should
-        # show up close to the top of the page
-
+        /* If we show subscriptions in reverse chronological order, the new one
+           should show up close to the top of the page. */
         common_redirect(common_local_url('subscribers', array('nickname' =>
                                                              $user->nickname)),
                         303);
     }
-
-    function add_avatar($profile, $url)
-    {
-        $temp_filename = tempnam(sys_get_temp_dir(), 'listener_avatar');
-        copy($url, $temp_filename);
-        $imagefile = new ImageFile($profile->id, $temp_filename);
-        $filename = Avatar::filename($profile->id,
-                                     image_type_to_extension($imagefile->type),
-                                     null,
-                                     common_timestamp());
-        rename($temp_filename, Avatar::path($filename));
-        return $profile->setOriginal($filename);
-    }
-
-    function access_token($omb)
-    {
-
-        common_debug('starting request for access token', __FILE__);
-
-        $con = omb_oauth_consumer();
-        $tok = new OAuthToken($omb['token'], $omb['secret']);
-
-        common_debug('using request token "'.$tok.'"', __FILE__);
-
-        $url = $omb['access_token_url'];
-
-        common_debug('using access token url "'.$url.'"', __FILE__);
-
-        # XXX: Is this the right thing to do? Strip off GET params and make them
-        # POST params? Seems wrong to me.
-
-        $parsed = parse_url($url);
-        $params = array();
-        parse_str($parsed['query'], $params);
-
-        $req = OAuthRequest::from_consumer_and_token($con, $tok, "POST", $url, $params);
-
-        $req->set_parameter('omb_version', OMB_VERSION_01);
-
-        # XXX: test to see if endpoint accepts this signature method
-
-        $req->sign_request(omb_hmac_sha1(), $con, $tok);
-
-        # We re-use this tool's fetcher, since it's pretty good
-
-        common_debug('posting to access token url "'.$req->get_normalized_http_url().'"', __FILE__);
-        common_debug('posting request data "'.$req->to_postdata().'"', __FILE__);
-
-        $fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
-        $result = $fetcher->post($req->get_normalized_http_url(),
-                                 $req->to_postdata(),
-                                 array('User-Agent: StatusNet/' . STATUSNET_VERSION));
-
-        common_debug('got result: "'.print_r($result,true).'"', __FILE__);
-
-        if ($result->status != 200) {
-            return null;
-        }
-
-        parse_str($result->body, $return);
-
-        return array($return['oauth_token'], $return['oauth_token_secret']);
-    }
 }
index 70c1ded488e89c46ff9397b5a3da65f27b30e923..6a6b55e78f73a98096a5f289ae5625e4ebde9583 100644 (file)
@@ -104,6 +104,7 @@ class groupRssAction extends Rss10Action
             return false;
         }
 
+        $this->notices = $this->getNotices($this->limit);
         return true;
     }
 
index be15efc47cea5c0be163b3095db9264b53328840..f0cca715615aa3940b5a145507d2f915467a1a2d 100644 (file)
@@ -82,8 +82,7 @@ class GroupsearchAction extends SearchAction
                 $message = _('If you can\'t find the group you\'re looking for, you can [create it](%%action.newgroup%%) yourself.');
             }
             else {
-                $message = sprintf(_('Why not [register an account](%%%%action.%s%%%%) and [create the group](%%%%action.newgroup%%%%) yourself!'),
-                                   (!common_config('site','openidonly')) ? 'register' : 'openidlogin');
+                $message = _('Why not [register an account](%%action.register%%) and [create the group](%%action.newgroup%%) yourself!');
             }
             $this->elementStart('div', 'guide');
             $this->raw(common_markup_to_html($message));
index 9fa6a76f671f471a83b13b590db846401fd6b039..788130c5825e4989a9e7f93fc6baf0270fe184bd 100644 (file)
@@ -241,7 +241,7 @@ class InviteAction extends CurrentUserDesignAction
                         common_root_url(),
                         $personal,
                         common_local_url('showstream', array('nickname' => $user->nickname)),
-                        common_local_url((!common_config('site', 'openidonly')) ? 'register' : 'openidlogin', array('code' => $invite->code)));
+                        common_local_url('register', array('code' => $invite->code)));
 
         mail_send($recipients, $headers, $body);
     }
index ac8c40c3e5f60e97cf98393204ff96ca79fc6fef..f6d0163105f42675a8667c55411ce4a8c96626e7 100644 (file)
@@ -67,8 +67,6 @@ class LoginAction extends Action
      *
      * Switches on request method; either shows the form or handles its input.
      *
-     * Checks if only OpenID is allowed and redirects to openidlogin if so.
-     *
      * @param array $args $_REQUEST data
      *
      * @return void
@@ -77,9 +75,7 @@ class LoginAction extends Action
     function handle($args)
     {
         parent::handle($args);
-        if (common_config('site', 'openidonly')) {
-            common_redirect(common_local_url('openidlogin'));
-        } else if (common_is_real_login()) {
+        if (common_is_real_login()) {
             $this->clientError(_('Already logged in.'));
         } else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
             $this->checkLogin();
@@ -259,11 +255,6 @@ class LoginAction extends Action
             return _('For security reasons, please re-enter your ' .
                      'user name and password ' .
                      'before changing your settings.');
-        } else if (common_config('openid', 'enabled')) {
-            return _('Login with your username and password. ' .
-                     'Don\'t have a username yet? ' .
-                     '[Register](%%action.register%%) a new account, or ' .
-                     'try [OpenID](%%action.openidlogin%%). ');
         } else {
             return _('Login with your username and password. ' .
                      'Don\'t have a username yet? ' .
index 298b2a484b58088a76e7bf334a2dc55c746b7dd2..1e0adae57592a536bdaf7b6bbf3eb7ae60959f14 100644 (file)
@@ -32,8 +32,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
     exit(1);
 }
 
-require_once INSTALLDIR.'/lib/openid.php';
-
 /**
  * Logout action class.
  *
index 01cb636aaf80a43555221836eb1485bd7b7e0fa6..a2cf72528b2e182645d588b23c3747bcfc709237 100644 (file)
@@ -146,8 +146,8 @@ class NewgroupAction extends Action
         } else if (!is_null($fullname) && mb_strlen($fullname) > 255) {
             $this->showForm(_('Full name is too long (max 255 chars).'));
             return;
-        } else if (!is_null($description) && mb_strlen($description) > 140) {
-            $this->showForm(_('description is too long (max 140 chars).'));
+        } else if (User_group::descriptionTooLong($description)) {
+            $this->showForm(sprintf(_('description is too long (max %d chars).'), User_group::maxDescription()));
             return;
         } else if (!is_null($location) && mb_strlen($location) > 255) {
             $this->showForm(_('Location is too long (max 255 chars).'));
index 828a339cfe1db56578b9b22db06f88e71c9cbba0..a0b17fc18a5ec315eac3c25b13c5c7f1c271dcb7 100644 (file)
@@ -144,9 +144,10 @@ class NewmessageAction extends Action
         } else {
             $content_shortened = common_shorten_links($this->content);
 
-            if (mb_strlen($content_shortened) > 140) {
-                $this->showForm(_('That\'s too long. ' .
-                    'Max message size is 140 chars.'));
+            if (Message::contentTooLong($content_shortened)) {
+                $this->showForm(sprintf(_('That\'s too long. ' .
+                                          'Max message size is %d chars.'),
+                                        Message::maxContent()));
                 return;
             }
         }
index 6e3720e09ecee566d1a95d0f3feed8a07c6582ef..d5b0332f4878bd58984b0425614a802beb0d04f6 100644 (file)
@@ -162,9 +162,10 @@ 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.'));
+            if (Notice::contentTooLong($content_shortened)) {
+                $this->clientError(sprintf(_('That\'s too long. '.
+                                             'Max notice size is %d chars.'),
+                                           Notice::maxContent()));
             }
         }
 
@@ -241,9 +242,10 @@ class NewnoticeAction extends Action
             $short_fileurl = common_shorten_url($fileurl);
             $content_shortened .= ' ' . $short_fileurl;
 
-            if (mb_strlen($content_shortened) > 140) {
+            if (Notice::contentTooLong($content_shortened)) {
                 $this->deleteFile($filename);
-                $this->clientError(_('Max notice size is 140 chars, including attachment URL.'));
+                $this->clientError(sprintf(_('Max notice size is %d chars, including attachment URL.'),
+                                           Notice::maxContent()));
             }
 
             // Also, not sure this is necessary -- Zach
@@ -253,13 +255,6 @@ class NewnoticeAction extends Action
         $notice = Notice::saveNew($user->id, $content_shortened, 'web', 1,
                                   ($replyto == 'false') ? null : $replyto);
 
-        if (is_string($notice)) {
-            if (isset($filename)) {
-                $this->deleteFile($filename);
-            }
-            $this->clientError($notice);
-        }
-
         if (isset($mimetype)) {
             $this->attachFile($notice, $fileRecord);
         }
index 69dcd1a46c08d815f988d04e2eb1feed0409f61d..79cf572ccaf59f2e4e2a5777966bd886c7625dc2 100644 (file)
@@ -121,9 +121,7 @@ class NoticesearchAction extends SearchAction
                 $message = sprintf(_('Be the first to [post on this topic](%%%%action.newnotice%%%%?status_textarea=%s)!'), urlencode($q));
             }
             else {
-                $message = sprintf(_('Why not [register an account](%%%%action.%s%%%%) and be the first to [post on this topic](%%%%action.newnotice%%%%?status_textarea=%s)!'),
-                                   (!common_config('site','openidonly')) ? 'register' : 'openidlogin',
-                                   urlencode($q));
+                $message = sprintf(_('Why not [register an account](%%%%action.register%%%%) and be the first to  [post on this topic](%%%%action.newnotice%%%%?status_textarea=%s)!'), urlencode($q));
             }
 
             $this->elementStart('div', 'guide');
diff --git a/actions/openidlogin.php b/actions/openidlogin.php
deleted file mode 100644 (file)
index 9b7deef..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-<?php
-/*
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2008, 2009, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
-
-require_once(INSTALLDIR.'/lib/openid.php');
-
-class OpenidloginAction extends Action
-{
-    function handle($args)
-    {
-        parent::handle($args);
-        if (!common_config('openid', 'enabled')) {
-            common_redirect(common_local_url('login'));
-        } else if (common_is_real_login()) {
-            $this->clientError(_('Already logged in.'));
-        } else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
-            $openid_url = $this->trimmed('openid_url');
-
-            # CSRF protection
-            $token = $this->trimmed('token');
-            if (!$token || $token != common_session_token()) {
-                $this->showForm(_('There was a problem with your session token. Try again, please.'), $openid_url);
-                return;
-            }
-
-            $rememberme = $this->boolean('rememberme');
-
-            common_ensure_session();
-
-            $_SESSION['openid_rememberme'] = $rememberme;
-
-            $result = oid_authenticate($openid_url,
-                                       'finishopenidlogin');
-
-            if (is_string($result)) { # error message
-                unset($_SESSION['openid_rememberme']);
-                $this->showForm($result, $openid_url);
-            }
-        } else {
-            $openid_url = oid_get_last();
-            $this->showForm(null, $openid_url);
-        }
-    }
-
-    function getInstructions()
-    {
-        if (common_logged_in() && !common_is_real_login() &&
-            common_get_returnto()) {
-            // rememberme logins have to reauthenticate before
-            // changing any profile settings (cookie-stealing protection)
-            return _('For security reasons, please re-login with your ' .
-                     '[OpenID](%%doc.openid%%) ' .
-                     'before changing your settings.');
-        } else {
-            return _('Login with an [OpenID](%%doc.openid%%) account.');
-        }
-    }
-
-    function showPageNotice()
-    {
-        if ($this->error) {
-            $this->element('div', array('class' => 'error'), $this->error);
-        } else {
-            $instr = $this->getInstructions();
-            $output = common_markup_to_html($instr);
-            $this->elementStart('div', 'instructions');
-            $this->raw($output);
-            $this->elementEnd('div');
-        }
-    }
-
-    function showScripts()
-    {
-        parent::showScripts();
-        $this->autofocus('openid_url');
-    }
-
-    function title()
-    {
-        return _('OpenID Login');
-    }
-
-    function showForm($error=null, $openid_url)
-    {
-        $this->error = $error;
-        $this->openid_url = $openid_url;
-        $this->showPage();
-    }
-
-    function showContent() {
-        $formaction = common_local_url('openidlogin');
-        $this->elementStart('form', array('method' => 'post',
-                                           'id' => 'form_openid_login',
-                                           'class' => 'form_settings',
-                                           'action' => $formaction));
-        $this->elementStart('fieldset');
-        $this->element('legend', null, _('OpenID login'));
-        $this->hidden('token', common_session_token());
-
-        $this->elementStart('ul', 'form_data');
-        $this->elementStart('li');
-        $this->input('openid_url', _('OpenID URL'),
-                     $this->openid_url,
-                     _('Your OpenID URL'));
-        $this->elementEnd('li');
-        $this->elementStart('li', array('id' => 'settings_rememberme'));
-        $this->checkbox('rememberme', _('Remember me'), false,
-                        _('Automatically login in the future; ' .
-                           'not for shared computers!'));
-        $this->elementEnd('li');
-        $this->elementEnd('ul');
-        $this->submit('submit', _('Login'));
-        $this->elementEnd('fieldset');
-        $this->elementEnd('form');
-    }
-
-    function showLocalNav()
-    {
-        $nav = new LoginGroupNav($this);
-        $nav->show();
-    }
-}
diff --git a/actions/openidsettings.php b/actions/openidsettings.php
deleted file mode 100644 (file)
index 30725fc..0000000
+++ /dev/null
@@ -1,246 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Settings for OpenID
- *
- * PHP version 5
- *
- * LICENCE: This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Settings
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2008-2009 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
-    exit(1);
-}
-
-require_once INSTALLDIR.'/lib/accountsettingsaction.php';
-require_once INSTALLDIR.'/lib/openid.php';
-
-/**
- * Settings for OpenID
- *
- * Lets users add, edit and delete OpenIDs from their account
- *
- * @category Settings
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- */
-
-class OpenidsettingsAction extends AccountSettingsAction
-{
-    /**
-     * Title of the page
-     *
-     * @return string Page title
-     */
-
-    function title()
-    {
-        return _('OpenID settings');
-    }
-
-    /**
-     * Instructions for use
-     *
-     * @return string Instructions for use
-     */
-
-    function getInstructions()
-    {
-        return _('[OpenID](%%doc.openid%%) lets you log into many sites' .
-                 ' with the same user account.'.
-                 ' Manage your associated OpenIDs from here.');
-    }
-
-    function showScripts()
-    {
-        parent::showScripts();
-        $this->autofocus('openid_url');
-    }
-
-    /**
-     * Show the form for OpenID management
-     *
-     * We have one form with a few different submit buttons to do different things.
-     *
-     * @return void
-     */
-
-    function showContent()
-    {
-        if (!common_config('openid', 'enabled')) {
-            $this->element('div', array('class' => 'error'),
-                           _('OpenID is not available.'));
-            return;
-        }
-
-        $user = common_current_user();
-
-        $this->elementStart('form', array('method' => 'post',
-                                          'id' => 'form_settings_openid_add',
-                                          'class' => 'form_settings',
-                                          'action' =>
-                                          common_local_url('openidsettings')));
-        $this->elementStart('fieldset', array('id' => 'settings_openid_add'));
-        $this->element('legend', null, _('Add OpenID'));
-        $this->hidden('token', common_session_token());
-        $this->element('p', 'form_guide',
-                       _('If you want to add an OpenID to your account, ' .
-                         'enter it in the box below and click "Add".'));
-        $this->elementStart('ul', 'form_data');
-        $this->elementStart('li');
-        $this->element('label', array('for' => 'openid_url'),
-                       _('OpenID URL'));
-        $this->element('input', array('name' => 'openid_url',
-                                      'type' => 'text',
-                                      'id' => 'openid_url'));
-        $this->elementEnd('li');
-        $this->elementEnd('ul');
-        $this->element('input', array('type' => 'submit',
-                                      'id' => 'settings_openid_add_action-submit',
-                                      'name' => 'add',
-                                      'class' => 'submit',
-                                      'value' => _('Add')));
-        $this->elementEnd('fieldset');
-        $this->elementEnd('form');
-
-        $oid = new User_openid();
-
-        $oid->user_id = $user->id;
-
-        $cnt = $oid->find();
-
-        if ($cnt > 0) {
-
-            $this->element('h2', null, _('Remove OpenID'));
-
-            if ($cnt == 1 && !$user->password) {
-
-                $this->element('p', 'form_guide',
-                               _('Removing your only OpenID '.
-                                 'would make it impossible to log in! ' .
-                                 'If you need to remove it, '.
-                                 'add another OpenID first.'));
-
-                if ($oid->fetch()) {
-                    $this->elementStart('p');
-                    $this->element('a', array('href' => $oid->canonical),
-                                   $oid->display);
-                    $this->elementEnd('p');
-                }
-
-            } else {
-
-                $this->element('p', 'form_guide',
-                               _('You can remove an OpenID from your account '.
-                                 'by clicking the button marked "Remove".'));
-                $idx = 0;
-
-                while ($oid->fetch()) {
-                    $this->elementStart('form',
-                                        array('method' => 'POST',
-                                              'id' => 'form_settings_openid_delete' . $idx,
-                                              'class' => 'form_settings',
-                                              'action' =>
-                                              common_local_url('openidsettings')));
-                    $this->elementStart('fieldset');
-                    $this->hidden('token', common_session_token());
-                    $this->element('a', array('href' => $oid->canonical),
-                                   $oid->display);
-                    $this->element('input', array('type' => 'hidden',
-                                                  'id' => 'openid_url'.$idx,
-                                                  'name' => 'openid_url',
-                                                  'value' => $oid->canonical));
-                    $this->element('input', array('type' => 'submit',
-                                                  'id' => 'remove'.$idx,
-                                                  'name' => 'remove',
-                                                  'class' => 'submit remove',
-                                                  'value' => _('Remove')));
-                    $this->elementEnd('fieldset');
-                    $this->elementEnd('form');
-                    $idx++;
-                }
-            }
-        }
-    }
-
-    /**
-     * Handle a POST request
-     *
-     * Muxes to different sub-functions based on which button was pushed
-     *
-     * @return void
-     */
-
-    function handlePost()
-    {
-        // CSRF protection
-        $token = $this->trimmed('token');
-        if (!$token || $token != common_session_token()) {
-            $this->showForm(_('There was a problem with your session token. '.
-                              'Try again, please.'));
-            return;
-        }
-
-        if ($this->arg('add')) {
-            $result = oid_authenticate($this->trimmed('openid_url'),
-                                       'finishaddopenid');
-            if (is_string($result)) { // error message
-                $this->showForm($result);
-            }
-        } else if ($this->arg('remove')) {
-            $this->removeOpenid();
-        } else {
-            $this->showForm(_('Something weird happened.'));
-        }
-    }
-
-    /**
-     * Handles a request to remove an OpenID from the user's account
-     *
-     * Validates input and, if everything is OK, deletes the OpenID.
-     * Reloads the form with a success or error notification.
-     *
-     * @return void
-     */
-
-    function removeOpenid()
-    {
-        $openid_url = $this->trimmed('openid_url');
-
-        $oid = User_openid::staticGet('canonical', $openid_url);
-
-        if (!$oid) {
-            $this->showForm(_('No such OpenID.'));
-            return;
-        }
-        $cur = common_current_user();
-        if (!$cur || $oid->user_id != $cur->id) {
-            $this->showForm(_('That OpenID does not belong to you.'));
-            return;
-        }
-        $oid->delete();
-        $this->showForm(_('OpenID removed.'), true);
-        return;
-    }
-}
index f898e220794e3c2c7bad536df3831f0fe6916681..011b4fc83832e7c39de789855ea44067318a871f 100644 (file)
@@ -97,19 +97,20 @@ class OthersettingsAction extends AccountSettingsAction
         $this->elementStart('fieldset');
         $this->hidden('token', common_session_token());
 
-        // I18N
-
-        $services = array(
-                          '' => 'None',
-                          'ur1.ca' => 'ur1.ca (free service)',
-                          '2tu.us' => '2tu.us (free service)',
-                          'ptiturl.com' => 'ptiturl.com',
-                          'bit.ly' => 'bit.ly',
-                          'tinyurl.com' => 'tinyurl.com',
-                          'is.gd' => 'is.gd',
-                          'snipr.com' => 'snipr.com',
-                          'metamark.net' => 'metamark.net'
-                          );
+        $services=array();
+        global $_shorteners;
+        if($_shorteners){
+            foreach($_shorteners as $name=>$value)
+            {
+                $services[$name]=$name;
+                if($value['info']['freeService']){
+                    // I18N
+                    $services[$name].=' (free service)';
+                }
+            }
+        }
+        asort($services);
+        $services['']='None';
 
         $this->elementStart('ul', 'form_data');
         $this->elementStart('li');
index e775ca17e83e6359ebefefe3711d86a13c485a8f..c2e1c44cae0b4a84099663aee85d954befa4edaf 100644 (file)
@@ -1,5 +1,16 @@
 <?php
-/*
+/**
+ * Handle postnotice action
+ *
+ * PHP version 5
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @author   Robin Millette <millette@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
  * StatusNet - the distributed open-source microblogging tool
  * Copyright (C) 2008, 2009, StatusNet, Inc.
  *
 
 if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
 
-require_once(INSTALLDIR.'/lib/omb.php');
+require_once INSTALLDIR.'/lib/omb.php';
+require_once INSTALLDIR.'/extlib/libomb/service_provider.php';
 
+/**
+ * Handler for postnotice action
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @author   Robin Millette <millette@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ */
 class PostnoticeAction extends Action
 {
+    /**
+     * For initializing members of the class.
+     *
+     * @param array $argarray misc. arguments
+     *
+     * @return boolean true
+     */
+    function prepare($argarray)
+    {
+        parent::prepare($argarray);
+        try {
+            $this->checkNotice();
+        } catch (Exception $e) {
+            $this->clientError($e->getMessage());
+            return false;
+        }
+        return true;
+    }
+
     function handle($args)
     {
         parent::handle($args);
         try {
-            common_remove_magic_from_request();
-            $req = OAuthRequest::from_request('POST', common_local_url('postnotice'));
-            # Note: server-to-server function!
-            $server = omb_oauth_server();
-            list($consumer, $token) = $server->verify_request($req);
-            if ($this->save_notice($req, $consumer, $token)) {
-                print "omb_version=".OMB_VERSION_01;
-            }
-        } catch (OAuthException $e) {
+            $srv = new OMB_Service_Provider(null, omb_oauth_datastore(),
+                                            omb_oauth_server());
+            $srv->handlePostNotice();
+        } catch (Exception $e) {
             $this->serverError($e->getMessage());
             return;
         }
     }
 
-    function save_notice(&$req, &$consumer, &$token)
+    function checkNotice()
     {
-        $version = $req->get_parameter('omb_version');
-        if ($version != OMB_VERSION_01) {
-            $this->clientError(_('Unsupported OMB version'), 400);
-            return false;
-        }
-        # First, check to see
-        $listenee =  $req->get_parameter('omb_listenee');
-        $remote_profile = Remote_profile::staticGet('uri', $listenee);
-        if (!$remote_profile) {
-            $this->clientError(_('Profile unknown'), 403);
-            return false;
-        }
-        $sub = Subscription::staticGet('token', $token->key);
-        if (!$sub) {
-            $this->clientError(_('No such subscription'), 403);
-            return false;
-        }
-        $content = $req->get_parameter('omb_notice_content');
-        $content_shortened = common_shorten_links($content);
-        if (mb_strlen($content_shortened) > 140) {
+        $content = common_shorten_links($_POST['omb_notice_content']);
+        if (Notice::contentTooLong($content)) {
             $this->clientError(_('Invalid notice content'), 400);
             return false;
         }
-        $notice_uri = $req->get_parameter('omb_notice');
-        if (!Validate::uri($notice_uri) &&
-            !common_valid_tag($notice_uri)) {
-            $this->clientError(_('Invalid notice uri'), 400);
-            return false;
-        }
-        $notice_url = $req->get_parameter('omb_notice_url');
-        if ($notice_url && !common_valid_http_url($notice_url)) {
-            $this->clientError(_('Invalid notice url'), 400);
-            return false;
+        $license      = $_POST['omb_notice_license'];
+        $site_license = common_config('license', 'url');
+        if ($license && !common_compatible_license($license, $site_license)) {
+            throw new Exception(sprintf(_('Notice license ‘%s’ is not ' .
+                                          'compatible with site license ‘%s’.'),
+                                        $license, $site_license));
         }
-        $notice = Notice::staticGet('uri', $notice_uri);
-        if (!$notice) {
-            $notice = Notice::saveNew($remote_profile->id, $content, 'omb', false, null, $notice_uri);
-            if (is_string($notice)) {
-                common_server_serror($notice, 500);
-                return false;
-            }
-            common_broadcast_notice($notice, true);
-        }
-        return true;
     }
 }
+?>
\ No newline at end of file
index 2d66e99469cf7692141724185669fdc7f742e027..5445d9bb257ee0c513ead7c2799a1f11958cbe81 100644 (file)
@@ -117,9 +117,16 @@ class ProfilesettingsAction extends AccountSettingsAction
                          _('URL of your homepage, blog, or profile on another site'));
             $this->elementEnd('li');
             $this->elementStart('li');
+            $maxBio = Profile::maxBio();
+            if ($maxBio > 0) {
+                $bioInstr = sprintf(_('Describe yourself and your interests in %d chars'),
+                                    $maxBio);
+            } else {
+                $bioInstr = _('Describe yourself and your interests');
+            }
             $this->textarea('bio', _('Bio'),
                             ($this->arg('bio')) ? $this->arg('bio') : $profile->bio,
-                            _('Describe yourself and your interests in 140 chars'));
+                            $bioInstr);
             $this->elementEnd('li');
             $this->elementStart('li');
             $this->input('location', _('Location'),
@@ -210,8 +217,9 @@ class ProfilesettingsAction extends AccountSettingsAction
             } else if (!is_null($fullname) && mb_strlen($fullname) > 255) {
                 $this->showForm(_('Full name is too long (max 255 chars).'));
                 return;
-            } else if (!is_null($bio) && mb_strlen($bio) > 140) {
-                $this->showForm(_('Bio is too long (max 140 chars).'));
+            } else if (Profile::bioTooLong($bio)) {
+                $this->showForm(sprintf(_('Bio is too long (max %d chars).'),
+                                        Profile::maxBio()));
                 return;
             } else if (!is_null($location) && mb_strlen($location) > 255) {
                 $this->showForm(_('Location is too long (max 255 chars).'));
index d426648f3d3623ad214adc00a3d748ef930749e9..73fad182a3f2ab71e7452f349637826dfb95d2d9 100644 (file)
@@ -114,8 +114,6 @@ class PublicAction extends Action
     {
         parent::handle($args);
 
-        header('X-XRDS-Location: '. common_local_url('publicxrds'));
-
         $this->showPage();
     }
 
@@ -156,22 +154,6 @@ class PublicAction extends Action
                               _('Public Stream Feed (Atom)')));
     }
 
-    /**
-     * Extra head elements
-     *
-     * We include a <meta> element linking to the publicxrds page, for OpenID
-     * client-side authentication.
-     *
-     * @return void
-     */
-
-    function extraHead()
-    {
-        // for client side of OpenID authentication
-        $this->element('meta', array('http-equiv' => 'X-XRDS-Location',
-                                     'content' => common_local_url('publicxrds')));
-    }
-
     /**
      * Show tabset for this page
      *
@@ -196,8 +178,7 @@ class PublicAction extends Action
         }
         else {
             if (! (common_config('site','closed') || common_config('site','inviteonly'))) {
-                $message .= sprintf(_('Why not [register an account](%%%%action.%s%%%%) and be the first to post!'),
-                                    (!common_config('site','openidonly')) ? 'register' : 'openidlogin');
+                $message .= _('Why not [register an account](%%action.register%%) and be the first to post!');
             }
        }
 
@@ -244,11 +225,10 @@ class PublicAction extends Action
     function showAnonymousMessage()
     {
         if (! (common_config('site','closed') || common_config('site','inviteonly'))) {
-            $m = sprintf(_('This is %%%%site.name%%%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
-                           'based on the Free Software [StatusNet](http://status.net/) tool. ' .
-                           '[Join now](%%%%action.%s%%%%) to share notices about yourself with friends, family, and colleagues! ' .
-                           '([Read more](%%%%doc.help%%%%))'),
-                         (!common_config('site','openidonly')) ? 'register' : 'openidlogin');
+            $m = _('This is %%site.name%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
+                   'based on the Free Software [StatusNet](http://status.net/) tool. ' .
+                   '[Join now](%%action.register%%) to share notices about yourself with friends, family, and colleagues! ' .
+                   '([Read more](%%doc.help%%))');
         } else {
             $m = _('This is %%site.name%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
                    'based on the Free Software [StatusNet](http://status.net/) tool.');
index 593888b9f66fd6fee0bd0f59333f4c174fc51dec..0c5d061cb65614148b6711eb0c2b60e24f1c25c9 100644 (file)
@@ -49,9 +49,23 @@ require_once INSTALLDIR.'/lib/rssaction.php';
  */
 class PublicrssAction extends Rss10Action
 {
+    /**
+     * Read arguments and initialize members
+     *
+     * @param array $args Arguments from $_REQUEST
+     * @return boolean success
+     */
+
+    function prepare($args)
+    {
+        parent::prepare($args);
+        $this->notices = $this->getNotices($this->limit);
+        return true;
+    }
+
     /**
      * Initialization.
-     * 
+     *
      * @return boolean true
      */
     function init()
@@ -73,7 +87,7 @@ class PublicrssAction extends Rss10Action
         while ($notice->fetch()) {
             $notices[] = clone($notice);
         }
-        
+
         return $notices;
     }
 
index 60bb53e27c934d9682b6ac616144d20d6c890294..e7f6ee36c736552bc33817bf5018940ea0e1d254 100644 (file)
@@ -72,8 +72,7 @@ class PublictagcloudAction extends Action
             $message .= _('Be the first to post one!');
         }
         else {
-            $message .= sprintf(_('Why not [register an account](%%%%action.%s%%%%) and be the first to post one!'),
-                                (!common_config('site','openidonly')) ? 'register' : 'openidlogin');
+            $message .= _('Why not [register an account](%%action.register%%) and be the first to post one!');
         }
 
         $this->elementStart('div', 'guide');
diff --git a/actions/publicxrds.php b/actions/publicxrds.php
deleted file mode 100644 (file)
index 209a10e..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-<?php
-
-/**
- * Public XRDS for OpenID
- *
- * PHP version 5
- *
- * @category Action
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @author   Robin Millette <millette@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2008, 2009, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
-    exit(1);
-}
-
-require_once INSTALLDIR.'/lib/openid.php';
-
-/**
- * Public XRDS for OpenID
- *
- * @category Action
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @author   Robin Millette <millette@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * @todo factor out similarities with XrdsAction
- */
-class PublicxrdsAction extends Action
-{
-    /**
-     * Is read only?
-     *
-     * @return boolean true
-     */
-    function isReadOnly($args)
-    {
-        return true;
-    }
-
-    /**
-     * Class handler.
-     *
-     * @param array $args array of arguments
-     *
-     * @return nothing
-     */
-    function handle($args)
-    {
-        parent::handle($args);
-        header('Content-Type: application/xrds+xml');
-        $this->startXML();
-        $this->elementStart('XRDS', array('xmlns' => 'xri://$xrds'));
-        $this->elementStart('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)',
-                                          'xmlns:simple' => 'http://xrds-simple.net/core/1.0',
-                                          'version' => '2.0'));
-        $this->element('Type', null, 'xri://$xrds*simple');
-        foreach (array('finishopenidlogin', 'finishaddopenid') as $finish) {
-            $this->showService(Auth_OpenID_RP_RETURN_TO_URL_TYPE,
-                                common_local_url($finish));
-        }
-        $this->elementEnd('XRD');
-        $this->elementEnd('XRDS');
-        $this->endXML();
-    }
-
-    /**
-     * Show service.
-     *
-     * @param string $type    XRDS type
-     * @param string $uri     URI
-     * @param array  $params  type parameters, null by default
-     * @param array  $sigs    type signatures, null by default
-     * @param string $localId local ID, null by default
-     *
-     * @return void
-     */
-    function showService($type, $uri, $params=null, $sigs=null, $localId=null)
-    {
-        $this->elementStart('Service');
-        if ($uri) {
-            $this->element('URI', null, $uri);
-        }
-        $this->element('Type', null, $type);
-        if ($params) {
-            foreach ($params as $param) {
-                $this->element('Type', null, $param);
-            }
-        }
-        if ($sigs) {
-            foreach ($sigs as $sig) {
-                $this->element('Type', null, $sig);
-            }
-        }
-        if ($localId) {
-            $this->element('LocalID', null, $localId);
-        }
-        $this->elementEnd('Service');
-    }
-}
-
index eefbc340a1f427f5484ff5a3f5978f007edf6e5e..100ab74242c74a4244739a3fcc46a56ca937c027 100644 (file)
@@ -116,8 +116,6 @@ class RegisterAction extends Action
      *
      * Checks if registration is closed and shows an error if so.
      *
-     * Checks if only OpenID is allowed and redirects to openidlogin if so.
-     *
      * @param array $args $_REQUEST data
      *
      * @return void
@@ -129,8 +127,6 @@ class RegisterAction extends Action
 
         if (common_config('site', 'closed')) {
             $this->clientError(_('Registration not allowed.'));
-        } else if (common_config('site', 'openidonly')) {
-            common_redirect(common_local_url('openidlogin'));
         } else if (common_logged_in()) {
             $this->clientError(_('Already logged in.'));
         } else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
@@ -217,8 +213,9 @@ class RegisterAction extends Action
             } else if (!is_null($fullname) && mb_strlen($fullname) > 255) {
                 $this->showForm(_('Full name is too long (max 255 chars).'));
                 return;
-            } else if (!is_null($bio) && mb_strlen($bio) > 140) {
-                $this->showForm(_('Bio is too long (max 140 chars).'));
+            } else if (Profile::bioTooLong($bio)) {
+                $this->showForm(sprintf(_('Bio is too long (max %d chars).'),
+                                        Profile::maxBio()));
                 return;
             } else if (!is_null($location) && mb_strlen($location) > 255) {
                 $this->showForm(_('Location is too long (max 255 chars).'));
@@ -335,22 +332,11 @@ class RegisterAction extends Action
         } else if ($this->error) {
             $this->element('p', 'error', $this->error);
         } else {
-            if (common_config('openid', 'enabled')) {
-                $instr =
-                  common_markup_to_html(_('With this form you can create '.
-                                          ' a new account. ' .
-                                          'You can then post notices and '.
-                                          'link up to friends and colleagues. '.
-                                          '(Have an [OpenID](http://openid.net/)? ' .
-                                          'Try our [OpenID registration]'.
-                                          '(%%action.openidlogin%%)!)'));
-            } else {
-                $instr =
-                  common_markup_to_html(_('With this form you can create '.
-                                          ' a new account. ' .
-                                          'You can then post notices and '.
-                                          'link up to friends and colleagues.'));
-            }
+            $instr =
+              common_markup_to_html(_('With this form you can create '.
+                                      ' a new account. ' .
+                                      'You can then post notices and '.
+                                      'link up to friends and colleagues. '));
 
             $this->elementStart('div', 'instructions');
             $this->raw($instr);
@@ -463,10 +449,16 @@ class RegisterAction extends Action
                            'or profile on another site'));
             $this->elementEnd('li');
             $this->elementStart('li');
+            $maxBio = Profile::maxBio();
+            if ($maxBio > 0) {
+                $bioInstr = sprintf(_('Describe yourself and your interests in %d chars'),
+                                    $maxBio);
+            } else {
+                $bioInstr = _('Describe yourself and your interests');
+            }
             $this->textarea('bio', _('Bio'),
                             $this->trimmed('bio'),
-                            _('Describe yourself and your '.
-                              'interests in 140 chars'));
+                            $bioInstr);
             $this->elementEnd('li');
             $this->elementStart('li');
             $this->input('location', _('Location'),
index 374392d4a31df096599dc11cc2e59f002566aeaa..aee2a5d8e79f1d7a54a7c1f945eecf4752299344 100644 (file)
@@ -1,5 +1,16 @@
 <?php
-/*
+/**
+ * Handler for remote subscription
+ *
+ * PHP version 5
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @author   Robin Millette <millette@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
  * StatusNet - the distributed open-source microblogging tool
  * Copyright (C) 2008, 2009, StatusNet, Inc.
  *
  *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
+ **/
 
 if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
 
-require_once(INSTALLDIR.'/lib/omb.php');
+require_once INSTALLDIR.'/lib/omb.php';
+require_once INSTALLDIR.'/extlib/libomb/service_consumer.php';
+require_once INSTALLDIR.'/extlib/libomb/profile.php';
+
+/**
+ * Handler for remote subscription
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @author   Robin Millette <millette@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ */
 
 class RemotesubscribeAction extends Action
 {
@@ -36,7 +60,7 @@ class RemotesubscribeAction extends Action
             return false;
         }
 
-        $this->nickname = $this->trimmed('nickname');
+        $this->nickname    = $this->trimmed('nickname');
         $this->profile_url = $this->trimmed('profile_url');
 
         return true;
@@ -47,7 +71,7 @@ class RemotesubscribeAction extends Action
         parent::handle($args);
 
         if ($_SERVER['REQUEST_METHOD'] == 'POST') {
-            # CSRF protection
+            /* Use a session token for CSRF protection. */
             $token = $this->trimmed('token');
             if (!$token || $token != common_session_token()) {
                 $this->showForm(_('There was a problem with your session token. '.
@@ -71,13 +95,11 @@ class RemotesubscribeAction extends Action
         if ($this->err) {
             $this->element('div', 'error', $this->err);
         } else {
-            $inst = sprintf(_('To subscribe, you can [login](%%%%action.%s%%%%),' .
-                              ' or [register](%%%%action.%s%%%%) a new ' .
-                              ' account. If you already have an account ' .
-                              ' on a [compatible microblogging site](%%doc.openmublog%%), ' .
-                              ' enter your profile URL below.'),
-                            (!common_config('site','openidonly')) ? 'login' : 'openidlogin',
-                            (!common_config('site','openidonly')) ? 'register' : 'openidlogin');
+            $inst = _('To subscribe, you can [login](%%action.login%%),' .
+                      ' or [register](%%action.register%%) a new ' .
+                      ' account. If you already have an account ' .
+                      ' on a [compatible microblogging site](%%doc.openmublog%%), ' .
+                      ' enter your profile URL below.');
             $output = common_markup_to_html($inst);
             $this->elementStart('div', 'instructions');
             $this->raw($output);
@@ -92,8 +114,8 @@ class RemotesubscribeAction extends Action
 
     function showContent()
     {
-        # id = remotesubscribe conflicts with the
-        # button on profile page
+        /* The id 'remotesubscribe' conflicts with the
+           button on profile page. */
         $this->elementStart('form', array('id' => 'form_remote_subscribe',
                                           'method' => 'post',
                                           'class' => 'form_settings',
@@ -119,247 +141,50 @@ class RemotesubscribeAction extends Action
 
     function remoteSubscription()
     {
-        $user = $this->getUser();
-
-        if (!$user) {
+        if (!$this->nickname) {
             $this->showForm(_('No such user.'));
             return;
         }
 
+        $user = User::staticGet('nickname', $this->nickname);
+
         $this->profile_url = $this->trimmed('profile_url');
 
         if (!$this->profile_url) {
-            $this->showForm(_('No such user.'));
+            $this->showForm(_('No such user'));
             return;
         }
 
-        if (!Validate::uri($this->profile_url, array('allowed_schemes' => array('http', 'https')))) {
+        if (!common_valid_http_url($this->profile_url)) {
             $this->showForm(_('Invalid profile URL (bad format)'));
             return;
         }
 
-        $fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
-        $yadis = Auth_Yadis_Yadis::discover($this->profile_url, $fetcher);
-
-        if (!$yadis || $yadis->failed) {
-            $this->showForm(_('Not a valid profile URL (no YADIS document).'));
-            return;
-        }
-
-        # XXX: a little liberal for sites that accidentally put whitespace before the xml declaration
-
-        $xrds =& Auth_Yadis_XRDS::parseXRDS(trim($yadis->response_text));
-
-        if (!$xrds) {
-            $this->showForm(_('Not a valid profile URL (no XRDS defined).'));
-            return;
-        }
-
-        $omb = $this->getOmb($xrds);
-
-        if (!$omb) {
-            $this->showForm(_('Not a valid profile URL (incorrect services).'));
-            return;
-        }
-
-        if (omb_service_uri($omb[OAUTH_ENDPOINT_REQUEST]) ==
-            common_local_url('requesttoken'))
-        {
-            $this->showForm(_('That\'s a local profile! Login to subscribe.'));
+        try {
+            $service = new OMB_Service_Consumer($this->profile_url,
+                                                common_root_url(),
+                                                omb_oauth_datastore());
+        } catch (OMB_InvalidYadisException $e) {
+            $this->showForm(_('Not a valid profile URL (no YADIS document or ' .
+                              'no or invalid XRDS defined).'));
             return;
         }
 
-        if (User::staticGet('uri', omb_local_id($omb[OAUTH_ENDPOINT_REQUEST]))) {
-            $this->showForm(_('That\'s a local profile! Login to subscribe.'));
+        if ($service->getServiceURI(OAUTH_ENDPOINT_REQUEST) ==
+            common_local_url('requesttoken') ||
+            User::staticGet('uri', $service->getRemoteUserURI())) {
+            $this->showForm(_('That’s a local profile! Login to subscribe.'));
             return;
         }
 
-        list($token, $secret) = $this->requestToken($omb);
-
-        if (!$token || !$secret) {
-            $this->showForm(_('Couldn\'t get a request token.'));
+        try {
+            $service->requestToken();
+        } catch (OMB_RemoteServiceException $e) {
+            $this->showForm(_('Couldnt get a request token.'));
             return;
         }
 
-        $this->requestAuthorization($user, $omb, $token, $secret);
-    }
-
-    function getUser()
-    {
-        $user = null;
-        if ($this->nickname) {
-            $user = User::staticGet('nickname', $this->nickname);
-        }
-        return $user;
-    }
-
-    function getOmb($xrds)
-    {
-        static $omb_endpoints = array(OMB_ENDPOINT_UPDATEPROFILE, OMB_ENDPOINT_POSTNOTICE);
-        static $oauth_endpoints = array(OAUTH_ENDPOINT_REQUEST, OAUTH_ENDPOINT_AUTHORIZE,
-                                        OAUTH_ENDPOINT_ACCESS);
-        $omb = array();
-
-        # XXX: the following code could probably be refactored to eliminate dupes
-
-        $oauth_services = omb_get_services($xrds, OAUTH_DISCOVERY);
-
-        if (!$oauth_services) {
-            return null;
-        }
-
-        $oauth_service = $oauth_services[0];
-
-        $oauth_xrd = $this->getXRD($oauth_service, $xrds);
-
-        if (!$oauth_xrd) {
-            return null;
-        }
-
-        if (!$this->addServices($oauth_xrd, $oauth_endpoints, $omb)) {
-            return null;
-        }
-
-        $omb_services = omb_get_services($xrds, OMB_NAMESPACE);
-
-        if (!$omb_services) {
-            return null;
-        }
-
-        $omb_service = $omb_services[0];
-
-        $omb_xrd = $this->getXRD($omb_service, $xrds);
-
-        if (!$omb_xrd) {
-            return null;
-        }
-
-        if (!$this->addServices($omb_xrd, $omb_endpoints, $omb)) {
-            return null;
-        }
-
-        # XXX: check that we got all the services we needed
-
-        foreach (array_merge($omb_endpoints, $oauth_endpoints) as $type) {
-            if (!array_key_exists($type, $omb) || !$omb[$type]) {
-                return null;
-            }
-        }
-
-        if (!omb_local_id($omb[OAUTH_ENDPOINT_REQUEST])) {
-            return null;
-        }
-
-        return $omb;
-    }
-
-    function getXRD($main_service, $main_xrds)
-    {
-        $uri = omb_service_uri($main_service);
-        if (strpos($uri, "#") !== 0) {
-            # FIXME: more rigorous handling of external service definitions
-            return null;
-        }
-        $id = substr($uri, 1);
-        $nodes = $main_xrds->allXrdNodes;
-        $parser = $main_xrds->parser;
-        foreach ($nodes as $node) {
-            $attrs = $parser->attributes($node);
-            if (array_key_exists('xml:id', $attrs) &&
-                $attrs['xml:id'] == $id) {
-                # XXX: trick the constructor into thinking this is the only node
-                $bogus_nodes = array($node);
-                return new Auth_Yadis_XRDS($parser, $bogus_nodes);
-            }
-        }
-        return null;
-    }
-
-    function addServices($xrd, $types, &$omb)
-    {
-        foreach ($types as $type) {
-            $matches = omb_get_services($xrd, $type);
-            if ($matches) {
-                $omb[$type] = $matches[0];
-            } else {
-                # no match for type
-                return false;
-            }
-        }
-        return true;
-    }
-
-    function requestToken($omb)
-    {
-        $con = omb_oauth_consumer();
-
-        $url = omb_service_uri($omb[OAUTH_ENDPOINT_REQUEST]);
-
-        # XXX: Is this the right thing to do? Strip off GET params and make them
-        # POST params? Seems wrong to me.
-
-        $parsed = parse_url($url);
-        $params = array();
-        parse_str($parsed['query'], $params);
-
-        $req = OAuthRequest::from_consumer_and_token($con, null, "POST", $url, $params);
-
-        $listener = omb_local_id($omb[OAUTH_ENDPOINT_REQUEST]);
-
-        if (!$listener) {
-            return null;
-        }
-
-        $req->set_parameter('omb_listener', $listener);
-        $req->set_parameter('omb_version', OMB_VERSION_01);
-
-        # XXX: test to see if endpoint accepts this signature method
-
-        $req->sign_request(omb_hmac_sha1(), $con, null);
-
-        # We re-use this tool's fetcher, since it's pretty good
-
-        $fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
-
-        $result = $fetcher->post($req->get_normalized_http_url(),
-                                 $req->to_postdata(),
-                                 array('User-Agent: StatusNet/' . STATUSNET_VERSION));
-        if ($result->status != 200) {
-            return null;
-        }
-
-        parse_str($result->body, $return);
-
-        return array($return['oauth_token'], $return['oauth_token_secret']);
-    }
-
-    function requestAuthorization($user, $omb, $token, $secret)
-    {
-        $con = omb_oauth_consumer();
-        $tok = new OAuthToken($token, $secret);
-
-        $url = omb_service_uri($omb[OAUTH_ENDPOINT_AUTHORIZE]);
-
-        # XXX: Is this the right thing to do? Strip off GET params and make them
-        # POST params? Seems wrong to me.
-
-        $parsed = parse_url($url);
-        $params = array();
-        parse_str($parsed['query'], $params);
-
-        $req = OAuthRequest::from_consumer_and_token($con, $tok, 'GET', $url, $params);
-
-        # We send over a ton of information. This lets the other
-        # server store info about our user, and it lets the current
-        # user decide if they really want to authorize the subscription.
-
-        $req->set_parameter('omb_version', OMB_VERSION_01);
-        $req->set_parameter('omb_listener', omb_local_id($omb[OAUTH_ENDPOINT_REQUEST]));
-        $req->set_parameter('omb_listenee', $user->uri);
-        $req->set_parameter('omb_listenee_profile', common_profile_url($user->nickname));
-        $req->set_parameter('omb_listenee_nickname', $user->nickname);
-        $req->set_parameter('omb_listenee_license', common_config('license', 'url'));
-
+        /* Create an OMB_Profile from $user. */
         $profile = $user->getProfile();
         if (!$profile) {
             common_log_db_error($user, 'SELECT', __FILE__);
@@ -367,49 +192,16 @@ class RemotesubscribeAction extends Action
             return;
         }
 
-        if (!is_null($profile->fullname)) {
-            $req->set_parameter('omb_listenee_fullname', $profile->fullname);
-        }
-        if (!is_null($profile->homepage)) {
-            $req->set_parameter('omb_listenee_homepage', $profile->homepage);
-        }
-        if (!is_null($profile->bio)) {
-            $req->set_parameter('omb_listenee_bio', $profile->bio);
-        }
-        if (!is_null($profile->location)) {
-            $req->set_parameter('omb_listenee_location', $profile->location);
-        }
-        $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
-        if ($avatar) {
-            $req->set_parameter('omb_listenee_avatar', $avatar->url);
-        }
-
-        # XXX: add a nonce to prevent replay attacks
-
-        $req->set_parameter('oauth_callback', common_local_url('finishremotesubscribe'));
-
-        # XXX: test to see if endpoint accepts this signature method
-
-        $req->sign_request(omb_hmac_sha1(), $con, $tok);
-
-        # store all our info here
-
-        $omb['listenee'] = $user->nickname;
-        $omb['listener'] = omb_local_id($omb[OAUTH_ENDPOINT_REQUEST]);
-        $omb['token'] = $token;
-        $omb['secret'] = $secret;
-        # call doesn't work after bounce back so we cache; maybe serialization issue...?
-        $omb['access_token_url'] = omb_service_uri($omb[OAUTH_ENDPOINT_ACCESS]);
-        $omb['post_notice_url'] = omb_service_uri($omb[OMB_ENDPOINT_POSTNOTICE]);
-        $omb['update_profile_url'] = omb_service_uri($omb[OMB_ENDPOINT_UPDATEPROFILE]);
+        $target_url = $service->requestAuthorization(
+                                   profile_to_omb_profile($user->uri, $profile),
+                                   common_local_url('finishremotesubscribe'));
 
         common_ensure_session();
 
-        $_SESSION['oauth_authorization_request'] = $omb;
-
-        # Redirect to authorization service
+        $_SESSION['oauth_authorization_request'] = serialize($service);
 
-        common_redirect($req->to_url(), 303);
-        return;
+        /* Redirect to the remote service for authorization. */
+        common_redirect($target_url, 303);
     }
 }
+?>
index cca43023097c2f11ff5806cf629488cd88831bd9..6003ad30bde3e60b5a7fb5b9f953d20d1d634583 100644 (file)
@@ -192,9 +192,7 @@ class RepliesAction extends OwnerDesignAction
             }
         }
         else {
-            $message .= sprintf(_('Why not [register an account](%%%%action.%s%%%%) and then nudge %s or post a notice to his or her attention.'),
-                                (!common_config('site','openidonly')) ? 'register' : 'openidlogin',
-                                $this->user->nickname);
+            $message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to his or her attention.'), $this->user->nickname);
         }
 
         $this->elementStart('div', 'guide');
index c71c9226fb59dbc0e9cba5d8c5c23ad2f1a30a99..76aae21adb994652a0fd16e8bbc090aeb95ccea3 100644 (file)
@@ -38,6 +38,7 @@ class RepliesrssAction extends Rss10Action
             $this->clientError(_('No such user.'));
             return false;
         } else {
+            $this->notices = $this->getNotices($this->limit);
             return true;
         }
     }
index a17efcdd50abaf55dbc5bc21b2f7daf52cd33a0d..e095161a7d01bf988604faba3ab9356f053bf191 100644 (file)
@@ -34,6 +34,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
 }
 
 require_once INSTALLDIR.'/lib/omb.php';
+require_once INSTALLDIR.'/extlib/libomb/service_provider.php';
 
 /**
  * Request token action class.
@@ -49,17 +50,17 @@ class RequesttokenAction extends Action
 {
      /**
      * Is read only?
-     * 
+     *
      * @return boolean false
      */
-    function isReadOnly($args)
+    function isReadOnly()
     {
         return false;
     }
-    
+
     /**
      * Class handler.
-     * 
+     *
      * @param array $args array of arguments
      *
      * @return void
@@ -68,14 +69,12 @@ class RequesttokenAction extends Action
     {
         parent::handle($args);
         try {
-            common_remove_magic_from_request();
-            $req    = OAuthRequest::from_request('POST', common_local_url('requesttoken'));
-            $server = omb_oauth_server();
-            $token  = $server->fetch_request_token($req);
-            print $token.'&omb_version='.OMB_VERSION_01;
-        } catch (OAuthException $e) {
+            $srv = new OMB_Service_Provider(null, omb_oauth_datastore(),
+                                            omb_oauth_server());
+            $srv->writeRequestToken();
+        } catch (Exception $e) {
             $this->serverError($e->getMessage());
         }
     }
 }
-
+?>
index 0f7a66330242be0a588c05f0b19a16b6e306e4b8..b96d2af37fc568a672132bba2a32280b488ecfc8 100644 (file)
@@ -196,9 +196,7 @@ class ShowfavoritesAction extends OwnerDesignAction
             }
         }
         else {
-            $message = sprintf(_('%s hasn\'t added any notices to his favorites yet. Why not [register an account](%%%%action.%s%%%%) and then post something interesting they would add to their favorites :)'),
-                               $this->user->nickname,
-                               (!common_config('site','openidonly')) ? 'register' : 'openidlogin');
+            $message = sprintf(_('%s hasn\'t added any notices to his favorites yet. Why not [register an account](%%%%action.register%%%%) and then post something interesting they would add to their favorites :)'), $this->user->nickname);
         }
 
         $this->elementStart('div', 'guide');
index 8157ee3c852f0884288e058c8e83bae90bc88806..ff994976215263447b6151d5f65b131873c1039f 100644 (file)
@@ -450,9 +450,8 @@ class ShowgroupAction extends GroupDesignAction
             $m = sprintf(_('**%s** is a user group on %%%%site.name%%%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
                 'based on the Free Software [StatusNet](http://status.net/) tool. Its members share ' .
                 'short messages about their life and interests. '.
-                '[Join now](%%%%action.%s%%%%) to become part of this group and many more! ([Read more](%%%%doc.help%%%%))'),
-                     $this->group->nickname,
-                     (!common_config('site','openidonly')) ? 'register' : 'openidlogin');
+                '[Join now](%%%%action.register%%%%) to become part of this group and many more! ([Read more](%%%%doc.help%%%%))'),
+                     $this->group->nickname);
         } else {
             $m = sprintf(_('**%s** is a user group on %%%%site.name%%%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
                 'based on the Free Software [StatusNet](http://status.net/) tool. Its members share ' .
index 3bc52b2dbc7bae3fef45a4bf7cedc24df156bc38..41408c23ccb4a396d92d814366824a5871475ae5 100644 (file)
@@ -84,7 +84,13 @@ class ShownoticeAction extends OwnerDesignAction
         $this->notice = Notice::staticGet($id);
 
         if (empty($this->notice)) {
-            $this->clientError(_('No such notice.'), 404);
+            // Did we used to have it, and it got deleted?
+            $deleted = Deleted_notice::staticGet($id);
+            if (!empty($deleted)) {
+                $this->clientError(_('Notice deleted.'), 410);
+            } else {
+                $this->clientError(_('No such notice.'), 404);
+            }
             return false;
         }
 
index 89285b13c7aae1cd4982ce8f7e411f96bbd84f9f..cdac4f47bc732c9c8ad5522441009f70bb550859 100644 (file)
@@ -358,9 +358,7 @@ class ShowstreamAction extends ProfileAction
             }
         }
         else {
-            $message .= sprintf(_('Why not [register an account](%%%%action.%s%%%%) and then nudge %s or post a notice to his or her attention.'),
-                                (!common_config('site','openidonly')) ? 'register' : 'openidlogin',
-                                $this->user->nickname);
+            $message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to his or her attention.'), $this->user->nickname);
         }
 
         $this->elementStart('div', 'guide');
@@ -394,10 +392,8 @@ class ShowstreamAction extends ProfileAction
         if (!(common_config('site','closed') || common_config('site','inviteonly'))) {
             $m = sprintf(_('**%s** has an account on %%%%site.name%%%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
                  'based on the Free Software [StatusNet](http://status.net/) tool. ' .
-                 '[Join now](%%%%action.%s%%%%) to follow **%s**\'s notices and many more! ([Read more](%%%%doc.help%%%%))'),
-                 $this->user->nickname,
-                 (!common_config('site','openidonly')) ? 'register' : 'openidlogin',
-                 $this->user->nickname);
+                 '[Join now](%%%%action.register%%%%) to follow **%s**\'s notices and many more! ([Read more](%%%%doc.help%%%%))'),
+                 $this->user->nickname, $this->user->nickname);
         } else {
             $m = sprintf(_('**%s** has an account on %%%%site.name%%%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
                  'based on the Free Software [StatusNet](http://status.net/) tool. '),
index f7d08d9d0b03d35493368982a18214c831af709c..df9ec996159abf64dfb4c04b26bdfb9b7c13e359 100644 (file)
@@ -111,9 +111,7 @@ class SubscribersAction extends GalleryAction
             }
         }
         else {
-            $message = sprintf(_('%s has no subscribers. Why not [register an account](%%%%action.%s%%%%) and be the first?'),
-                               $this->user->nickname,
-                               (!common_config('site','openidonly')) ? 'register' : 'openidlogin');
+            $message = sprintf(_('%s has no subscribers. Why not [register an account](%%%%action.register%%%%) and be the first?'), $this->user->nickname);
         }
 
         $this->elementStart('div', 'guide');
index dbe55804b16337aada7f06f373c481e19eb96d44..08b8f4e9c18a78121cd133cbbb594f5a90a5067f 100644 (file)
@@ -141,9 +141,10 @@ class Twitapidirect_messagesAction extends TwitterapiAction
                 $code = 406, $apidata['content-type']);
         } else {
             $content_shortened = common_shorten_links($content);
-            if (mb_strlen($content_shortened) > 140) {
-                $this->clientError(_('That\'s too long. Max message size is 140 chars.'),
-                    $code = 406, $apidata['content-type']);
+            if (Message::contentTooLong($content_shortened)) {
+                $this->clientError(sprintf(_('That\'s too long. Max message size is %d chars.'),
+                                           Message::maxContent()),
+                                   $code = 406, $apidata['content-type']);
                 return;
             }
         }
index 4deb1b764adb7ba6d8bebbef666b8e314a22c82d..493144e77e588a0030a1b0c671d728b2ae5315c0 100644 (file)
@@ -293,6 +293,105 @@ require_once INSTALLDIR.'/lib/twitterapi.php';
          }
      }
 
+     function join($args, $apidata)
+     {
+         parent::handle($args);
+
+         common_debug("in groups api action");
+
+         $this->auth_user = $apidata['user'];
+         $group = $this->get_group($apidata['api_arg'], $apidata);
+
+         if (empty($group)) {
+             $this->clientError('Not Found', 404, $apidata['content-type']);
+             return false;
+         }
+
+         if($this->auth_user->isMember($group)){
+            $this->clientError(_('You are already a member of that group'), $code = 403);
+            return false;
+         }
+
+         if (Group_block::isBlocked($group, $this->auth_user->getProfile())) {
+            $this->clientError(_('You have been blocked from that group by the admin.'), 403);
+            return false;
+         }
+
+         $member = new Group_member();
+
+         $member->group_id   = $group->id;
+         $member->profile_id = $this->auth_user->id;
+         $member->created    = common_sql_now();
+
+         $result = $member->insert();
+
+         if (!$result) {
+            common_log_db_error($member, 'INSERT', __FILE__);
+            $this->serverError(sprintf(_('Could not join user %s to group %s'),
+                                       $this->auth_user->nickname, $group->nickname));
+         }
+
+         switch($apidata['content-type']) {
+          case 'xml':
+             $this->show_single_xml_group($group);
+             break;
+          case 'json':
+             $this->show_single_json_group($group);
+             break;
+          default:
+             $this->clientError(_('API method not found!'), $code = 404);
+         }
+     }
+
+     function leave($args, $apidata)
+     {
+         parent::handle($args);
+
+         common_debug("in groups api action");
+
+         $this->auth_user = $apidata['user'];
+         $group = $this->get_group($apidata['api_arg'], $apidata);
+
+         if (empty($group)) {
+             $this->clientError('Not Found', 404, $apidata['content-type']);
+             return false;
+         }
+
+         if(! $this->auth_user->isMember($group)){
+            $this->clientError(_('You are not a member of that group'), $code = 403);
+            return false;
+         }
+
+         $member = new Group_member();
+
+         $member->group_id   = $group->id;
+         $member->profile_id = $this->auth_user->id;
+
+         if (!$member->find(true)) {
+            $this->serverError(_('Could not find membership record.'));
+            return;
+         }
+
+         $result = $member->delete();
+
+         if (!$result) {
+            common_log_db_error($member, 'INSERT', __FILE__);
+            $this->serverError(sprintf(_('Could not remove user %s to group %s'),
+                                       $this->auth_user->nickname, $group->nickname));
+         }
+
+         switch($apidata['content-type']) {
+          case 'xml':
+             $this->show_single_xml_group($group);
+             break;
+          case 'json':
+             $this->show_single_json_group($group);
+             break;
+          default:
+             $this->clientError(_('API method not found!'), $code = 404);
+         }
+     }
+
      function is_member($args, $apidata)
      {
          parent::handle($args);
@@ -326,4 +425,29 @@ require_once INSTALLDIR.'/lib/twitterapi.php';
              $this->clientError(_('API method not found!'), $code = 404);
          }
      }
+
+     function create($args, $apidata)
+     {
+        die("todo");
+     }
+
+     function update($args, $apidata)
+     {
+        die("todo");
+     }
+
+     function update_group_logo($args, $apidata)
+     {
+        die("todo");
+     }
+
+     function destroy($args, $apidata)
+     {
+        die("todo");
+     }
+
+     function tag($args, $apidata)
+     {
+        die("todo");
+     }
 }
index 41887a68f44e6213cac8c8b9b528fb5a1d555d2f..87043b1821cb47672af710706b63c901014cd9bc 100644 (file)
@@ -247,14 +247,15 @@ class TwitapistatusesAction extends TwitterapiAction
 
             $status_shortened = common_shorten_links($status);
 
-            if (mb_strlen($status_shortened) > 140) {
+            if (Notice::contentTooLong($status_shortened)) {
 
                 // XXX: Twitter truncates anything over 140, flags the status
                 // as "truncated." Sending this error may screw up some clients
                 // that assume Twitter will truncate for them.    Should we just
                 // truncate too? -- Zach
-                $this->clientError(_('That\'s too long. Max notice size is 140 chars.'),
-                    $code = 406, $apidata['content-type']);
+                $this->clientError(sprintf(_('That\'s too long. Max notice size is %d chars.'),
+                                           Notice::maxContent()),
+                                   $code = 406, $apidata['content-type']);
                 return;
             }
         }
@@ -296,11 +297,6 @@ class TwitapistatusesAction extends TwitterapiAction
                 html_entity_decode($status, ENT_NOQUOTES, 'UTF-8'),
                     $source, 1, $reply_to);
 
-            if (is_string($notice)) {
-                $this->serverError($notice, 500, $apidata['content-type']);
-                return;
-            }
-
             common_broadcast_notice($notice);
             $apidata['api_arg'] = $notice->id;
         }
@@ -401,8 +397,14 @@ class TwitapistatusesAction extends TwitterapiAction
         } else {
             // XXX: Twitter just sets a 404 header and doens't bother
             // to return an err msg
-            $this->clientError(_('No status with that ID found.'),
-                404, $apidata['content-type']);
+            $deleted = Deleted_notice::staticGet($notice_id);
+            if (!empty($deleted)) {
+                $this->clientError(_('Status deleted.'),
+                                   410, $apidata['content-type']);
+            } else {
+                $this->clientError(_('No status with that ID found.'),
+                                   404, $apidata['content-type']);
+            }
         }
     }
 
index 9a4cf8e4659b6f51c09fb23a48993457efd52014..3cec9523cdff1fd3c1376592121e8cf21f52dacb 100644 (file)
@@ -1,5 +1,16 @@
 <?php
-/*
+/**
+ * Handle an updateprofile action
+ *
+ * PHP version 5
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @author   Robin Millette <millette@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
  * StatusNet - the distributed open-source microblogging tool
  * Copyright (C) 2008, 2009, StatusNet, Inc.
  *
 
 if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
 
-require_once(INSTALLDIR.'/lib/omb.php');
+require_once INSTALLDIR.'/lib/omb.php';
+require_once INSTALLDIR.'/extlib/libomb/service_provider.php';
 
+/**
+ * Handle an updateprofile action
+ *
+ * @category Action
+ * @package  Laconica
+ * @author   Evan Prodromou <evan@status.net>
+ * @author   Robin Millette <millette@controlyourself.ca>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://laconi.ca/
+ */
 class UpdateprofileAction extends Action
 {
-    
-    function handle($args)
-    {
-        parent::handle($args);
-        try {
-            common_remove_magic_from_request();
-            $req = OAuthRequest::from_request('POST', common_local_url('updateprofile'));
-            # Note: server-to-server function!
-            $server = omb_oauth_server();
-            list($consumer, $token) = $server->verify_request($req);
-            if ($this->update_profile($req, $consumer, $token)) {
-                header('HTTP/1.1 200 OK');
-                header('Content-type: text/plain');
-                print "omb_version=".OMB_VERSION_01;
-            }
-        } catch (OAuthException $e) {
-            $this->serverError($e->getMessage());
-            return;
-        }
-    }
 
-    function update_profile($req, $consumer, $token)
+    /**
+     * For initializing members of the class.
+     *
+     * @param array $argarray misc. arguments
+     *
+     * @return boolean true
+     */
+    function prepare($argarray)
     {
-        $version = $req->get_parameter('omb_version');
-        if ($version != OMB_VERSION_01) {
-            $this->clientError(_('Unsupported OMB version'), 400);
+        parent::prepare($argarray);
+        $license      = $_POST['omb_listenee_license'];
+        $site_license = common_config('license', 'url');
+        if (!common_compatible_license($license, $site_license)) {
+            $this->clientError(sprintf(_('Listenee stream license ‘%s’ is not '.
+                                         'compatible with site license ‘%s’.'),
+                                       $license, $site_license));
             return false;
         }
-        # First, check to see if listenee exists
-        $listenee =  $req->get_parameter('omb_listenee');
-        $remote = Remote_profile::staticGet('uri', $listenee);
-        if (!$remote) {
-            $this->clientError(_('Profile unknown'), 404);
-            return false;
-        }
-        # Second, check to see if they should be able to post updates!
-        # We see if there are any subscriptions to that remote user with
-        # the given token.
-
-        $sub = new Subscription();
-        $sub->subscribed = $remote->id;
-        $sub->token = $token->key;
-        if (!$sub->find(true)) {
-            $this->clientError(_('You did not send us that profile'), 403);
-            return false;
-        }
-
-        $profile = Profile::staticGet('id', $remote->id);
-        if (!$profile) {
-            # This one is our fault
-            $this->serverError(_('Remote profile with no matching profile'), 500);
-            return false;
-        }
-        $nickname = $req->get_parameter('omb_listenee_nickname');
-        if ($nickname && !Validate::string($nickname, array('min_length' => 1,
-                                                            'max_length' => 64,
-                                                            'format' => NICKNAME_FMT))) {
-            $this->clientError(_('Nickname must have only lowercase letters and numbers and no spaces.'));
-            return false;
-        }
-        $license = $req->get_parameter('omb_listenee_license');
-        if ($license && !common_valid_http_url($license)) {
-            $this->clientError(sprintf(_("Invalid license URL '%s'"), $license));
-            return false;
-        }
-        $profile_url = $req->get_parameter('omb_listenee_profile');
-        if ($profile_url && !common_valid_http_url($profile_url)) {
-            $this->clientError(sprintf(_("Invalid profile URL '%s'."), $profile_url));
-            return false;
-        }
-        # optional stuff
-        $fullname = $req->get_parameter('omb_listenee_fullname');
-        if ($fullname && mb_strlen($fullname) > 255) {
-            $this->clientError(_("Full name is too long (max 255 chars)."));
-            return false;
-        }
-        $homepage = $req->get_parameter('omb_listenee_homepage');
-        if ($homepage && (!common_valid_http_url($homepage) || mb_strlen($homepage) > 255)) {
-            $this->clientError(sprintf(_("Invalid homepage '%s'"), $homepage));
-            return false;
-        }
-        $bio = $req->get_parameter('omb_listenee_bio');
-        if ($bio && mb_strlen($bio) > 140) {
-            $this->clientError(_("Bio is too long (max 140 chars)."));
-            return false;
-        }
-        $location = $req->get_parameter('omb_listenee_location');
-        if ($location && mb_strlen($location) > 255) {
-            $this->clientError(_("Location is too long (max 255 chars)."));
-            return false;
-        }
-        $avatar = $req->get_parameter('omb_listenee_avatar');
-        if ($avatar) {
-            if (!common_valid_http_url($avatar) || strlen($avatar) > 255) {
-                $this->clientError(sprintf(_("Invalid avatar URL '%s'"), $avatar));
-                return false;
-            }
-            $size = @getimagesize($avatar);
-            if (!$size) {
-                $this->clientError(sprintf(_("Can't read avatar URL '%s'"), $avatar));
-                return false;
-            }
-            if ($size[0] != AVATAR_PROFILE_SIZE || $size[1] != AVATAR_PROFILE_SIZE) {
-                $this->clientError(sprintf(_("Wrong size image at '%s'"), $avatar));
-                return false;
-            }
-            if (!in_array($size[2], array(IMAGETYPE_GIF, IMAGETYPE_JPEG,
-                                          IMAGETYPE_PNG))) {
-                $this->clientError(sprintf(_("Wrong image type for '%s'"), $avatar));
-                return false;
-            }
-        }
-
-        $orig_profile = clone($profile);
+        return true;
+    }
 
-        /* Use values even if they are an empty string. Parsing an empty string in
-           updateProfile is the specified way of clearing a parameter in OMB. */
-        if (!is_null($nickname)) {
-            $profile->nickname = $nickname;
-        }
-        if (!is_null($profile_url)) {
-            $profile->profileurl = $profile_url;
-        }
-        if (!is_null($fullname)) {
-            $profile->fullname = $fullname;
-        }
-        if (!is_null($homepage)) {
-            $profile->homepage = $homepage;
-        }
-        if (!is_null($bio)) {
-            $profile->bio = $bio;
-        }
-        if (!is_null($location)) {
-            $profile->location = $location;
-        }
+    function handle($args)
+    {
+        parent::handle($args);
 
-        if (!$profile->update($orig_profile)) {
-            $this->serverError(_('Could not save new profile info'), 500);
-            return false;
-        } else {
-            if ($avatar) {
-                $temp_filename = tempnam(sys_get_temp_dir(), 'listenee_avatar');
-                copy($avatar, $temp_filename);
-                $imagefile = new ImageFile($profile->id, $temp_filename);
-                $filename = Avatar::filename($profile->id,
-                                     image_type_to_extension($imagefile->type),
-                                     null,
-                                     common_timestamp());
-                rename($temp_filename, Avatar::path($filename));
-                if (!$profile->setOriginal($filename)) {
-                    $this->serverError(_('Could not save avatar info'), 500);
-                    return false;
-                }
-            }
-            return true;
+        try {
+            $srv = new OMB_Service_Provider(null, omb_oauth_datastore(),
+                                            omb_oauth_server());
+            $srv->handleUpdateProfile();
+        } catch (Exception $e) {
+            $this->serverError($e->getMessage());
+            return;
         }
     }
 }
index a9ac1f256f79eb3bef2e445fe2fbab668c33089e..dc59e6c94112ccec04d926c11647ce3c9b7a3e5b 100644 (file)
@@ -1,5 +1,16 @@
 <?php
-/*
+/**
+ * Let the user authorize a remote subscription request
+ *
+ * PHP version 5
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @author   Robin Millette <millette@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
  * StatusNet - the distributed open-source microblogging tool
  * Copyright (C) 2008, 2009, StatusNet, Inc.
  *
@@ -19,7 +30,9 @@
 
 if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
 
-require_once(INSTALLDIR.'/lib/omb.php');
+require_once INSTALLDIR.'/lib/omb.php';
+require_once INSTALLDIR.'/extlib/libomb/service_provider.php';
+require_once INSTALLDIR.'/extlib/libomb/profile.php';
 define('TIMESTAMP_THRESHOLD', 300);
 
 class UserauthorizationAction extends Action
@@ -32,46 +45,58 @@ class UserauthorizationAction extends Action
         parent::handle($args);
 
         if ($_SERVER['REQUEST_METHOD'] == 'POST') {
-            # CSRF protection
+            /* Use a session token for CSRF protection. */
             $token = $this->trimmed('token');
             if (!$token || $token != common_session_token()) {
-                $params = $this->getStoredParams();
-                $this->showForm($params, _('There was a problem with your session token. '.
-                                        'Try again, please.'));
+                $srv = $this->getStoredParams();
+                $this->showForm($srv->getRemoteUser(), _('There was a problem ' .
+                                        'with your session token. Try again, ' .
+                                        'please.'));
                 return;
             }
-            # We've shown the form, now post user's choice
+            /* We've shown the form, now post user's choice. */
             $this->sendAuthorization();
         } else {
             if (!common_logged_in()) {
-                # Go log in, and then come back
+                /* Go log in, and then come back. */
                 common_set_returnto($_SERVER['REQUEST_URI']);
 
-                if (!common_config('site', 'openidonly')) {
-                    common_redirect(common_local_url('login'));
-                } else {
-                    common_redirect(common_local_url('openidlogin'));
-                }
+                common_redirect(common_local_url('login'));
+                return;
+            }
+
+            $user    = common_current_user();
+            $profile = $user->getProfile();
+            if (!$profile) {
+                common_log_db_error($user, 'SELECT', __FILE__);
+                $this->serverError(_('User without matching profile'));
                 return;
             }
 
+            /* TODO: If no token is passed the user should get a prompt to enter
+               it according to OAuth Core 1.0. */
             try {
-                $this->validateRequest();
-                $this->storeParams($_GET);
-                $this->showForm($_GET);
-            } catch (OAuthException $e) {
+                $this->validateOmb();
+                $srv = new OMB_Service_Provider(
+                        profile_to_omb_profile($user->uri, $profile),
+                        omb_oauth_datastore());
+
+                $remote_user = $srv->handleUserAuth();
+            } catch (Exception $e) {
                 $this->clearParams();
                 $this->clientError($e->getMessage());
                 return;
             }
 
+            $this->storeParams($srv);
+            $this->showForm($remote_user);
         }
     }
 
     function showForm($params, $error=null)
     {
         $this->params = $params;
-        $this->error = $error;
+        $this->error  = $error;
         $this->showPage();
     }
 
@@ -83,23 +108,24 @@ class UserauthorizationAction extends Action
     function showPageNotice()
     {
         $this->element('p', null, _('Please check these details to make sure '.
-                                    'that you want to subscribe to this user\'s notices. '.
-                                    'If you didn\'t just ask to subscribe to someone\'s notices, '.
-                                    'click "Reject".'));
+                                    'that you want to subscribe to this ' .
+                                    'user’s notices. If you didn’t just ask ' .
+                                    'to subscribe to someone’s notices, '.
+                                    'click “Reject”.'));
     }
 
     function showContent()
     {
         $params = $this->params;
 
-        $nickname = $params['omb_listenee_nickname'];
-        $profile = $params['omb_listenee_profile'];
-        $license = $params['omb_listenee_license'];
-        $fullname = $params['omb_listenee_fullname'];
-        $homepage = $params['omb_listenee_homepage'];
-        $bio = $params['omb_listenee_bio'];
-        $location = $params['omb_listenee_location'];
-        $avatar = $params['omb_listenee_avatar'];
+        $nickname = $params->getNickname();
+        $profile  = $params->getProfileURL();
+        $license  = $params->getLicenseURL();
+        $fullname = $params->getFullname();
+        $homepage = $params->getHomepage();
+        $bio      = $params->getBio();
+        $location = $params->getLocation();
+        $avatar   = $params->getAvatarURL();
 
         $this->elementStart('div', array('class' => 'profile'));
         $this->elementStart('div', 'entity_profile vcard');
@@ -176,11 +202,14 @@ class UserauthorizationAction extends Action
                                           'id' => 'userauthorization',
                                           'class' => 'form_user_authorization',
                                           'name' => 'userauthorization',
-                                          'action' => common_local_url('userauthorization')));
+                                          'action' => common_local_url(
+                                                         'userauthorization')));
         $this->hidden('token', common_session_token());
 
-        $this->submit('accept', _('Accept'), 'submit accept', null, _('Subscribe to this user'));
-        $this->submit('reject', _('Reject'), 'submit reject', null, _('Reject this subscription'));
+        $this->submit('accept', _('Accept'), 'submit accept', null,
+                      _('Subscribe to this user'));
+        $this->submit('reject', _('Reject'), 'submit reject', null,
+                      _('Reject this subscription'));
         $this->elementEnd('form');
         $this->elementEnd('li');
         $this->elementEnd('ul');
@@ -190,191 +219,27 @@ class UserauthorizationAction extends Action
 
     function sendAuthorization()
     {
-        $params = $this->getStoredParams();
+        $srv = $this->getStoredParams();
 
-        if (!$params) {
+        if (is_null($srv)) {
             $this->clientError(_('No authorization request!'));
             return;
         }
 
-        $callback = $params['oauth_callback'];
-
-        if ($this->arg('accept')) {
-            if (!$this->authorizeToken($params)) {
-                $this->clientError(_('Error authorizing token'));
-            }
-            if (!$this->saveRemoteProfile($params)) {
-                $this->clientError(_('Error saving remote profile'));
-            }
-            if (!$callback) {
-                $this->showAcceptMessage($params['oauth_token']);
-            } else {
-                $newparams = array();
-                $newparams['oauth_token'] = $params['oauth_token'];
-                $newparams['omb_version'] = OMB_VERSION_01;
-                $user = User::staticGet('uri', $params['omb_listener']);
-                $profile = $user->getProfile();
-                if (!$profile) {
-                    common_log_db_error($user, 'SELECT', __FILE__);
-                    $this->serverError(_('User without matching profile'));
-                    return;
-                }
-                $newparams['omb_listener_nickname'] = $user->nickname;
-                $newparams['omb_listener_profile'] = common_local_url('showstream',
-                                                                   array('nickname' => $user->nickname));
-                if (!is_null($profile->fullname)) {
-                    $newparams['omb_listener_fullname'] = $profile->fullname;
-                }
-                if (!is_null($profile->homepage)) {
-                    $newparams['omb_listener_homepage'] = $profile->homepage;
-                }
-                if (!is_null($profile->bio)) {
-                    $newparams['omb_listener_bio'] = $profile->bio;
-                }
-                if (!is_null($profile->location)) {
-                    $newparams['omb_listener_location'] = $profile->location;
-                }
-                $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
-                if ($avatar) {
-                    $newparams['omb_listener_avatar'] = $avatar->url;
-                }
-                $parts = array();
-                foreach ($newparams as $k => $v) {
-                    $parts[] = $k . '=' . OAuthUtil::urlencode_rfc3986($v);
-                }
-                $query_string = implode('&', $parts);
-                $parsed = parse_url($callback);
-                $url = $callback . (($parsed['query']) ? '&' : '?') . $query_string;
-                common_redirect($url, 303);
-            }
-        } else {
-            if (!$callback) {
-                $this->showRejectMessage();
-            } else {
-                # XXX: not 100% sure how to signal failure... just redirect without token?
-                common_redirect($callback, 303);
-            }
-        }
-    }
-
-    function authorizeToken(&$params)
-    {
-        $token_field = $params['oauth_token'];
-        $rt = new Token();
-        $rt->tok = $token_field;
-        $rt->type = 0;
-        $rt->state = 0;
-        if ($rt->find(true)) {
-            $orig_rt = clone($rt);
-            $rt->state = 1; # Authorized but not used
-            if ($rt->update($orig_rt)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    # XXX: refactor with similar code in finishremotesubscribe.php
-
-    function saveRemoteProfile(&$params)
-    {
-        # FIXME: we should really do this when the consumer comes
-        # back for an access token. If they never do, we've got stuff in a
-        # weird state.
-
-        $nickname = $params['omb_listenee_nickname'];
-        $fullname = $params['omb_listenee_fullname'];
-        $profile_url = $params['omb_listenee_profile'];
-        $homepage = $params['omb_listenee_homepage'];
-        $bio = $params['omb_listenee_bio'];
-        $location = $params['omb_listenee_location'];
-        $avatar_url = $params['omb_listenee_avatar'];
-
-        $listenee = $params['omb_listenee'];
-        $remote = Remote_profile::staticGet('uri', $listenee);
-
-        if ($remote) {
-            $exists = true;
-            $profile = Profile::staticGet($remote->id);
-            $orig_remote = clone($remote);
-            $orig_profile = clone($profile);
-        } else {
-            $exists = false;
-            $remote = new Remote_profile();
-            $remote->uri = $listenee;
-            $profile = new Profile();
-        }
-
-        $profile->nickname = $nickname;
-        $profile->profileurl = $profile_url;
-
-        if (!is_null($fullname)) {
-            $profile->fullname = $fullname;
-        }
-        if (!is_null($homepage)) {
-            $profile->homepage = $homepage;
-        }
-        if (!is_null($bio)) {
-            $profile->bio = $bio;
-        }
-        if (!is_null($location)) {
-            $profile->location = $location;
+        $accepted = $this->arg('accept');
+        try {
+            list($val, $token) = $srv->continueUserAuth($accepted);
+        } catch (Exception $e) {
+            $this->clientError($e->getMessage());
+            return;
         }
-
-        if ($exists) {
-            $profile->update($orig_profile);
+        if ($val !== false) {
+            common_redirect($val, 303);
+        } elseif ($accepted) {
+            $this->showAcceptMessage($token);
         } else {
-            $profile->created = DB_DataObject_Cast::dateTime(); # current time
-            $id = $profile->insert();
-            if (!$id) {
-                return false;
-            }
-            $remote->id = $id;
+            $this->showRejectMessage();
         }
-
-        if ($exists) {
-            if (!$remote->update($orig_remote)) {
-                return false;
-            }
-        } else {
-            $remote->created = DB_DataObject_Cast::dateTime(); # current time
-            if (!$remote->insert()) {
-                return false;
-            }
-        }
-
-        if ($avatar_url) {
-            if (!$this->addAvatar($profile, $avatar_url)) {
-                return false;
-            }
-        }
-
-        $user = common_current_user();
-
-        $sub = new Subscription();
-        $sub->subscriber = $user->id;
-        $sub->subscribed = $remote->id;
-        $sub->token = $params['oauth_token']; # NOTE: request token, not valid for use!
-        $sub->created = DB_DataObject_Cast::dateTime(); # current time
-
-        if (!$sub->insert()) {
-            return false;
-        }
-
-        return true;
-    }
-
-    function addAvatar($profile, $url)
-    {
-        $temp_filename = tempnam(sys_get_temp_dir(), 'listenee_avatar');
-        copy($url, $temp_filename);
-        $imagefile = new ImageFile($profile->id, $temp_filename);
-        $filename = Avatar::filename($profile->id,
-                                     image_type_to_extension($imagefile->type),
-                                     null,
-                                     common_timestamp());
-        rename($temp_filename, Avatar::path($filename));
-        return $profile->setOriginal($filename);
     }
 
     function showAcceptMessage($tok)
@@ -382,26 +247,28 @@ class UserauthorizationAction extends Action
         common_show_header(_('Subscription authorized'));
         $this->element('p', null,
                        _('The subscription has been authorized, but no '.
-                         'callback URL was passed. Check with the site\'s instructions for '.
-                         'details on how to authorize the subscription. Your subscription token is:'));
+                         'callback URL was passed. Check with the site’s ' .
+                         'instructions for details on how to authorize the ' .
+                         'subscription. Your subscription token is:'));
         $this->element('blockquote', 'token', $tok);
         common_show_footer();
     }
 
-    function showRejectMessage($tok)
+    function showRejectMessage()
     {
         common_show_header(_('Subscription rejected'));
         $this->element('p', null,
                        _('The subscription has been rejected, but no '.
-                         'callback URL was passed. Check with the site\'s instructions for '.
-                         'details on how to fully reject the subscription.'));
+                         'callback URL was passed. Check with the site’s ' .
+                         'instructions for details on how to fully reject ' .
+                         'the subscription.'));
         common_show_footer();
     }
 
     function storeParams($params)
     {
         common_ensure_session();
-        $_SESSION['userauthorizationparams'] = $params;
+        $_SESSION['userauthorizationparams'] = serialize($params);
     }
 
     function clearParams()
@@ -413,138 +280,74 @@ class UserauthorizationAction extends Action
     function getStoredParams()
     {
         common_ensure_session();
-        $params = $_SESSION['userauthorizationparams'];
+        $params = unserialize($_SESSION['userauthorizationparams']);
         return $params;
     }
 
-    # Throws an OAuthException if anything goes wrong
-
-    function validateRequest()
-    {
-        /* Find token.
-           TODO: If no token is passed the user should get a prompt to enter it
-                 according to OAuth Core 1.0 */
-        $t = new Token();
-        $t->tok = $_GET['oauth_token'];
-        $t->type = 0;
-        if (!$t->find(true)) {
-            throw new OAuthException("Invalid request token: " . $_GET['oauth_token']);
-        }
-
-        $this->validateOmb();
-        return true;
-    }
-
     function validateOmb()
     {
-        foreach (array('omb_version', 'omb_listener', 'omb_listenee',
-                       'omb_listenee_profile', 'omb_listenee_nickname',
-                       'omb_listenee_license') as $param)
-        {
-            if (!isset($_GET[$param]) || is_null($_GET[$param])) {
-                throw new OAuthException("Required parameter '$param' not found");
-            }
-        }
-        # Now, OMB stuff
-        $version = $_GET['omb_version'];
-        if ($version != OMB_VERSION_01) {
-            throw new OAuthException("OpenMicroBlogging version '$version' not supported");
-        }
         $listener = $_GET['omb_listener'];
+        $listenee = $_GET['omb_listenee'];
+        $nickname = $_GET['omb_listenee_nickname'];
+        $profile  = $_GET['omb_listenee_profile'];
+
         $user = User::staticGet('uri', $listener);
         if (!$user) {
-            throw new OAuthException("Listener URI '$listener' not found here");
-        }
-        $cur = common_current_user();
-        if ($cur->id != $user->id) {
-            throw new OAuthException("Can't add for another user!");
-        }
-        $listenee = $_GET['omb_listenee'];
-        if (!Validate::uri($listenee) &&
-            !common_valid_tag($listenee)) {
-            throw new OAuthException("Listenee URI '$listenee' not a recognizable URI");
+            throw new Exception(sprintf(_('Listener URI ‘%s’ not found here'),
+                                        $listener));
         }
+
         if (strlen($listenee) > 255) {
-            throw new OAuthException("Listenee URI '$listenee' too long");
+            throw new Exception(sprintf(_('Listenee URI ‘%s’ is too long.'),
+                                        $listenee));
         }
 
         $other = User::staticGet('uri', $listenee);
         if ($other) {
-            throw new OAuthException("Listenee URI '$listenee' is local user");
+            throw new Exception(sprintf(_('Listenee URI ‘%s’ is a local user.'),
+                                        $listenee));
         }
 
         $remote = Remote_profile::staticGet('uri', $listenee);
         if ($remote) {
-            $sub = new Subscription();
+            $sub             = new Subscription();
             $sub->subscriber = $user->id;
             $sub->subscribed = $remote->id;
             if ($sub->find(true)) {
-                throw new OAuthException("Already subscribed to user!");
+                throw new Exception('You are already subscribed to this user.');
             }
         }
-        $nickname = $_GET['omb_listenee_nickname'];
-        if (!Validate::string($nickname, array('min_length' => 1,
-                                               'max_length' => 64,
-                                               'format' => NICKNAME_FMT))) {
-            throw new OAuthException('Nickname must have only letters and numbers and no spaces.');
-        }
-        $profile = $_GET['omb_listenee_profile'];
-        if (!common_valid_http_url($profile)) {
-            throw new OAuthException("Invalid profile URL '$profile'.");
-        }
 
-        if ($profile == common_local_url('showstream', array('nickname' => $nickname))) {
-            throw new OAuthException("Profile URL '$profile' is for a local user.");
-        }
+        if ($profile == common_profile_url($nickname)) {
+            throw new Exception(sprintf(_('Profile URL ‘%s’ is for a local user.'),
+                                        $profile));
 
-        $license = $_GET['omb_listenee_license'];
-        if (!common_valid_http_url($license)) {
-            throw new OAuthException("Invalid license URL '$license'.");
         }
+
+        $license      = $_GET['omb_listenee_license'];
         $site_license = common_config('license', 'url');
         if (!common_compatible_license($license, $site_license)) {
-            throw new OAuthException("Listenee stream license '$license' not compatible with site license '$site_license'.");
-        }
-        # optional stuff
-        $fullname = $_GET['omb_listenee_fullname'];
-        if ($fullname && mb_strlen($fullname) > 255) {
-            throw new OAuthException("Full name '$fullname' too long.");
-        }
-        $homepage = $_GET['omb_listenee_homepage'];
-        if ($homepage && (!common_valid_http_url($homepage) || mb_strlen($homepage) > 255)) {
-            throw new OAuthException("Invalid homepage '$homepage'");
-        }
-        $bio = $_GET['omb_listenee_bio'];
-        if ($bio && mb_strlen($bio) > 140) {
-            throw new OAuthException("Bio too long '$bio'");
-        }
-        $location = $_GET['omb_listenee_location'];
-        if ($location && mb_strlen($location) > 255) {
-            throw new OAuthException("Location too long '$location'");
+            throw new Exception(sprintf(_('Listenee stream license ‘%s’ is not ' .
+                                          'compatible with site license ‘%s’.'),
+                                        $license, $site_license));
         }
+
         $avatar = $_GET['omb_listenee_avatar'];
         if ($avatar) {
             if (!common_valid_http_url($avatar) || strlen($avatar) > 255) {
-                throw new OAuthException("Invalid avatar URL '$avatar'");
+                throw new Exception(sprintf(_('Avatar URL ‘%s’ is not valid.'),
+                                            $avatar));
             }
             $size = @getimagesize($avatar);
             if (!$size) {
-                throw new OAuthException("Can't read avatar URL '$avatar'");
-            }
-            if ($size[0] != AVATAR_PROFILE_SIZE || $size[1] != AVATAR_PROFILE_SIZE) {
-                throw new OAuthException("Wrong size image at '$avatar'");
+                throw new Exception(sprintf(_('Can’t read avatar URL ‘%s’.'),
+                                            $avatar));
             }
             if (!in_array($size[2], array(IMAGETYPE_GIF, IMAGETYPE_JPEG,
                                           IMAGETYPE_PNG))) {
-                throw new OAuthException("Wrong image type for '$avatar'");
+                throw new Exception(sprintf(_('Wrong image type for avatar URL '.
+                                              '‘%s’.'), $avatar));
             }
         }
-        $callback = $_GET['oauth_callback'];
-        if ($callback && !common_valid_http_url($callback)) {
-            throw new OAuthException("Invalid callback URL '$callback'");
-        }
-        if ($callback && $callback == common_local_url('finishremotesubscribe')) {
-            throw new OAuthException("Callback URL '$callback' is for local site.");
-        }
     }
-}
+}
\ No newline at end of file
index fa6d588cdf4358f7c2f99f37853bb10b7e3de8b7..19e610551d4bca2ec9cf65defde29b7e003a76de 100644 (file)
@@ -25,7 +25,6 @@ require_once(INSTALLDIR.'/lib/rssaction.php');
 
 class UserrssAction extends Rss10Action
 {
-    var $user = null;
     var $tag  = null;
 
     function prepare($args)
@@ -39,6 +38,7 @@ class UserrssAction extends Rss10Action
             $this->clientError(_('No such user.'));
             return false;
         } else {
+            $this->notices = $this->getNotices($this->limit);
             return true;
         }
     }
@@ -64,9 +64,8 @@ class UserrssAction extends Rss10Action
 
     function getNotices($limit=0)
     {
-
         $user = $this->user;
-
+        
         if (is_null($user)) {
             return null;
         }
index def10e4cf7bd90d8af6b6263eec7af093dfb1fbe..8ba89fec0ff7c56a262ebaa29374ba9a8c0b8359 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * XRDS for OpenID
+ * XRDS for OpenMicroBlogging
  *
  * PHP version 5
  *
@@ -34,9 +34,11 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
 }
 
 require_once INSTALLDIR.'/lib/omb.php';
+require_once INSTALLDIR.'/extlib/libomb/service_provider.php';
+require_once INSTALLDIR.'/extlib/libomb/xrds_mapper.php';
 
 /**
- * XRDS for OpenID
+ * XRDS for OpenMicroBlogging
  *
  * @category Action
  * @package  StatusNet
@@ -52,7 +54,7 @@ class XrdsAction extends Action
      *
      * @return boolean true
      */
-    function isReadOnly($args)
+    function isReadOnly()
     {
         return true;
     }
@@ -85,89 +87,31 @@ class XrdsAction extends Action
      */
     function showXrds($user)
     {
-        header('Content-Type: application/xrds+xml');
-        $this->startXML();
-        $this->elementStart('XRDS', array('xmlns' => 'xri://$xrds'));
+        $srv = new OMB_Service_Provider(profile_to_omb_profile($user->uri,
+                                        $user->getProfile()));
+        /* Use libomb’s default XRDS Writer. */
+        $xrds_writer = null;
+        $srv->writeXRDS(new Laconica_XRDS_Mapper(), $xrds_writer);
+    }
+}
 
-        $this->elementStart('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)',
-                                          'xml:id' => 'oauth',
-                                          'xmlns:simple' => 'http://xrds-simple.net/core/1.0',
-                                          'version' => '2.0'));
-        $this->element('Type', null, 'xri://$xrds*simple');
-        $this->showService(OAUTH_ENDPOINT_REQUEST,
-                            common_local_url('requesttoken'),
-                            array(OAUTH_AUTH_HEADER, OAUTH_POST_BODY),
-                            array(OAUTH_HMAC_SHA1),
-                            $user->uri);
-        $this->showService(OAUTH_ENDPOINT_AUTHORIZE,
-                            common_local_url('userauthorization'),
-                            array(OAUTH_AUTH_HEADER, OAUTH_POST_BODY),
-                            array(OAUTH_HMAC_SHA1));
-        $this->showService(OAUTH_ENDPOINT_ACCESS,
-                            common_local_url('accesstoken'),
-                            array(OAUTH_AUTH_HEADER, OAUTH_POST_BODY),
-                            array(OAUTH_HMAC_SHA1));
-        $this->showService(OAUTH_ENDPOINT_RESOURCE,
-                            null,
-                            array(OAUTH_AUTH_HEADER, OAUTH_POST_BODY),
-                            array(OAUTH_HMAC_SHA1));
-        $this->elementEnd('XRD');
+class Laconica_XRDS_Mapper implements OMB_XRDS_Mapper
+{
+    protected $urls;
 
-        // XXX: decide whether to include user's ID/nickname in postNotice URL
-        $this->elementStart('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)',
-                                          'xml:id' => 'omb',
-                                          'xmlns:simple' => 'http://xrds-simple.net/core/1.0',
-                                          'version' => '2.0'));
-        $this->element('Type', null, 'xri://$xrds*simple');
-        $this->showService(OMB_ENDPOINT_POSTNOTICE,
-                            common_local_url('postnotice'));
-        $this->showService(OMB_ENDPOINT_UPDATEPROFILE,
-                            common_local_url('updateprofile'));
-        $this->elementEnd('XRD');
-        $this->elementStart('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)',
-                                          'version' => '2.0'));
-        $this->element('Type', null, 'xri://$xrds*simple');
-        $this->showService(OAUTH_DISCOVERY,
-                            '#oauth');
-        $this->showService(OMB_NAMESPACE,
-                            '#omb');
-        $this->elementEnd('XRD');
-        $this->elementEnd('XRDS');
-        $this->endXML();
+    public function __construct()
+    {
+        $this->urls = array(
+            OAUTH_ENDPOINT_REQUEST => 'requesttoken',
+            OAUTH_ENDPOINT_AUTHORIZE => 'userauthorization',
+            OAUTH_ENDPOINT_ACCESS => 'accesstoken',
+            OMB_ENDPOINT_POSTNOTICE => 'postnotice',
+            OMB_ENDPOINT_UPDATEPROFILE => 'updateprofile');
     }
 
-    /**
-     * Show service.
-     *
-     * @param string $type    XRDS type
-     * @param string $uri     URI
-     * @param array  $params  type parameters, null by default
-     * @param array  $sigs    type signatures, null by default
-     * @param string $localId local ID, null by default
-     *
-     * @return void
-     */
-    function showService($type, $uri, $params=null, $sigs=null, $localId=null)
+    public function getURL($action)
     {
-        $this->elementStart('Service');
-        if ($uri) {
-            $this->element('URI', null, $uri);
-        }
-        $this->element('Type', null, $type);
-        if ($params) {
-            foreach ($params as $param) {
-                $this->element('Type', null, $param);
-            }
-        }
-        if ($sigs) {
-            foreach ($sigs as $sig) {
-                $this->element('Type', null, $sig);
-            }
-        }
-        if ($localId) {
-            $this->element('LocalID', null, $localId);
-        }
-        $this->elementEnd('Service');
+        return common_local_url($this->urls[$action]);
     }
 }
-
+?>
diff --git a/classes/Config.php b/classes/Config.php
new file mode 100644 (file)
index 0000000..92f237d
--- /dev/null
@@ -0,0 +1,131 @@
+<?php
+/*
+ * Laconica - a distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.     If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Table Definition for config
+ */
+
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class Config extends Memcached_DataObject
+{
+    ###START_AUTOCODE
+    /* the code below is auto generated do not remove the above tag */
+
+    public $__table = 'config';                          // table name
+    public $section;                         // varchar(32)  primary_key not_null
+    public $setting;                         // varchar(32)  primary_key not_null
+    public $value;                           // varchar(255)
+
+    /* Static get */
+    function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Config',$k,$v); }
+
+    /* the code above is auto generated do not remove the tag below */
+    ###END_AUTOCODE
+
+    const settingsKey = 'config:settings';
+
+    static function loadSettings()
+    {
+        $settings = self::_getSettings();
+        if (!empty($settings)) {
+            self::_applySettings($settings);
+        }
+    }
+
+    static function _getSettings()
+    {
+        $c = self::memcache();
+
+        if (!empty($c)) {
+            $settings = $c->get(common_cache_key(self::settingsKey));
+            if (!empty($settings)) {
+                return $settings;
+            }
+        }
+
+        $settings = array();
+
+        $config = new Config();
+
+        $config->find();
+
+        while ($config->fetch()) {
+            $settings[] = array($config->section, $config->setting, $config->value);
+        }
+
+        $config->free();
+
+        if (!empty($c)) {
+            $c->set(common_cache_key(self::settingsKey), $settings);
+        }
+
+        return $settings;
+    }
+
+    static function _applySettings($settings)
+    {
+        global $config;
+
+        foreach ($settings as $s) {
+            list($section, $setting, $value) = $s;
+            $config[$section][$setting] = $value;
+        }
+    }
+
+    function insert()
+    {
+        $result = parent::insert();
+        if ($result) {
+            Config::_blowSettingsCache();
+        }
+        return $result;
+    }
+
+    function delete()
+    {
+        $result = parent::delete();
+        if ($result) {
+            Config::_blowSettingsCache();
+        }
+        return $result;
+    }
+
+    function update($orig=null)
+    {
+        $result = parent::update($orig);
+        if ($result) {
+            Config::_blowSettingsCache();
+        }
+        return $result;
+    }
+
+    function _blowSettingsCache()
+    {
+        $c = self::memcache();
+
+        if (!empty($c)) {
+            $c->delete(common_cache_key(self::settingsKey));
+        }
+    }
+}
diff --git a/classes/Deleted_notice.php b/classes/Deleted_notice.php
new file mode 100644 (file)
index 0000000..64dc85d
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+/*
+ * Laconica - a distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.     If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Table Definition for notice
+ */
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class Deleted_notice extends Memcached_DataObject
+{
+    ###START_AUTOCODE
+    /* the code below is auto generated do not remove the above tag */
+
+    public $__table = 'deleted_notice';                  // table name
+    public $id;                              // int(4)  primary_key not_null
+    public $profile_id;                      // int(4)   not_null
+    public $uri;                             // varchar(255)  unique_key
+    public $created;                         // datetime()   not_null
+    public $deleted;                         // datetime()   not_null
+
+    /* Static get */
+    function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Deleted_notice',$k,$v); }
+
+    /* the code above is auto generated do not remove the tag below */
+    ###END_AUTOCODE
+}
index 308d0a77176a63eb0ec563c8702096663c4d36b1..e04a9d5255ad50c85c840e3563e58ed0017610b9 100644 (file)
@@ -94,7 +94,13 @@ class File extends Memcached_DataObject
             $file_redir = File_redirection::staticGet('url', $given_url);
             if (empty($file_redir)) {
                 $redir_data = File_redirection::where($given_url);
-                $redir_url = $redir_data['url'];
+                if (is_array($redir_data)) {
+                    $redir_url = $redir_data['url'];
+                } elseif (is_string($redir_data)) {
+                    $redir_url = $redir_data;
+                } else {
+                    throw new ServerException("Can't process url '$given_url'");
+                }
                 // TODO: max field length
                 if ($redir_url === $given_url || strlen($redir_url) > 255) {
                     $x = File::saveNew($redir_data, $given_url);
@@ -197,17 +203,44 @@ class File extends Memcached_DataObject
         return 'http://'.$server.$path.$filename;
     }
 
-    function isEnclosure(){
-        if(isset($this->filename)){
-            return true;
-        }
-        $notEnclosureMimeTypes = array('text/html','application/xhtml+xml',null);
-        $mimetype = strtolower($this->mimetype);
-        $semicolon = strpos($mimetype,';');
-        if($semicolon){
-            $mimetype = substr($mimetype,0,$semicolon);
+    function getEnclosure(){
+        $enclosure = (object) array();
+        $enclosure->title=$this->title;
+        $enclosure->url=$this->url;
+        $enclosure->title=$this->title;
+        $enclosure->date=$this->date;
+        $enclosure->modified=$this->modified;
+        $enclosure->size=$this->size;
+        $enclosure->mimetype=$this->mimetype;
+
+        if(! isset($this->filename)){
+            $notEnclosureMimeTypes = array('text/html','application/xhtml+xml');
+            $mimetype = strtolower($this->mimetype);
+            $semicolon = strpos($mimetype,';');
+            if($semicolon){
+                $mimetype = substr($mimetype,0,$semicolon);
+            }
+            if(in_array($mimetype,$notEnclosureMimeTypes)){
+                $oembed = File_oembed::staticGet('file_id',$this->id);
+                if($oembed){
+                    $mimetype = strtolower($oembed->mimetype);
+                    $semicolon = strpos($mimetype,';');
+                    if($semicolon){
+                        $mimetype = substr($mimetype,0,$semicolon);
+                    }
+                    if(in_array($mimetype,$notEnclosureMimeTypes)){
+                        return false;
+                    }else{
+                        if($oembed->mimetype) $enclosure->mimetype=$oembed->mimetype;
+                        if($oembed->url) $enclosure->url=$oembed->url;
+                        if($oembed->title) $enclosure->title=$oembed->title;
+                        if($oembed->modified) $enclosure->modified=$oembed->modified;
+                        unset($oembed->size);
+                    }
+                }
+            }
         }
-        return(! in_array($mimetype,$notEnclosureMimeTypes));
+        return $enclosure;
     }
 }
 
index 6be6518152f7f9dcebe9e3e71e06b33d0d0b2386..e41ccfd097eb4d415d02d587f2fc08d6d7e57ecc 100644 (file)
@@ -20,6 +20,7 @@
 if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
 
 require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+require_once INSTALLDIR.'/classes/File_redirection.php';
 
 /**
  * Table Definition for file_oembed
@@ -34,6 +35,7 @@ class File_oembed extends Memcached_DataObject
     public $file_id;                         // int(4)  primary_key not_null
     public $version;                         // varchar(20)
     public $type;                            // varchar(20)
+    public $mimetype;                        // varchar(50)
     public $provider;                        // varchar(50)
     public $provider_url;                    // varchar(255)
     public $width;                           // int(4)
@@ -93,7 +95,24 @@ class File_oembed extends Memcached_DataObject
         if (!empty($data->title)) $file_oembed->title = $data->title;
         if (!empty($data->author_name)) $file_oembed->author_name = $data->author_name;
         if (!empty($data->author_url)) $file_oembed->author_url = $data->author_url;
-        if (!empty($data->url)) $file_oembed->url = $data->url;
+        if (!empty($data->url)){
+            $file_oembed->url = $data->url;
+            $given_url = File_redirection::_canonUrl($file_oembed->url);
+            if (! empty($given_url)){
+                $file = File::staticGet('url', $given_url);
+                if (empty($file)) {
+                    $file_redir = File_redirection::staticGet('url', $given_url);
+                    if (empty($file_redir)) {
+                        $redir_data = File_redirection::where($given_url);
+                        $file_oembed->mimetype = $redir_data['type'];
+                    } else {
+                        $file_id = $file_redir->file_id;
+                    }
+                } else {
+                    $file_oembed->mimetype=$file->mimetype;
+                }
+            }
+        }
         $file_oembed->insert();
         if (!empty($data->thumbnail_url)) {
             File_thumbnail::saveNew($data, $file_id);
index 76b18f672d3e23d969b50f69052e29f6f08acf81..79052bf7d3e80ca6e5b9347ee2062e5e9cc8ad22 100644 (file)
@@ -79,6 +79,9 @@ class File_redirection extends Memcached_DataObject
             }
         }
 
+        if(strpos($short_url,'://') === false){
+            return $short_url;
+        }
         $curlh = File_redirection::_commonCurl($short_url, $redirs);
         // Don't include body in output
         curl_setopt($curlh, CURLOPT_NOBODY, true);
index 4806057b4ca1183fa6c752fde2e0ce03c4f20ee0..979e6e87ccbdcb4d79de3c0fec07a19a239a6812 100644 (file)
@@ -4,7 +4,7 @@
  */
 require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
 
-class Message extends Memcached_DataObject 
+class Message extends Memcached_DataObject
 {
     ###START_AUTOCODE
     /* the code below is auto generated do not remove the above tag */
@@ -14,58 +14,73 @@ class Message extends Memcached_DataObject
     public $uri;                             // varchar(255)  unique_key
     public $from_profile;                    // int(4)   not_null
     public $to_profile;                      // int(4)   not_null
-    public $content;                         // varchar(140)  
-    public $rendered;                        // text()  
-    public $url;                             // varchar(255)  
+    public $content;                         // text()
+    public $rendered;                        // text()
+    public $url;                             // varchar(255)
     public $created;                         // datetime()   not_null
     public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
-    public $source;                          // varchar(32)  
+    public $source;                          // varchar(32)
 
     /* Static get */
-    function staticGet($k,$v=null)
-    { return Memcached_DataObject::staticGet('Message',$k,$v); }
+    function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Message',$k,$v); }
 
     /* the code above is auto generated do not remove the tag below */
     ###END_AUTOCODE
-    
+
     function getFrom()
     {
         return Profile::staticGet('id', $this->from_profile);
     }
-    
+
     function getTo()
     {
         return Profile::staticGet('id', $this->to_profile);
     }
-    
+
     static function saveNew($from, $to, $content, $source) {
-        
+
         $msg = new Message();
-        
+
         $msg->from_profile = $from;
         $msg->to_profile = $to;
         $msg->content = common_shorten_links($content);
         $msg->rendered = common_render_text($content);
         $msg->created = common_sql_now();
         $msg->source = $source;
-        
+
         $result = $msg->insert();
-        
+
         if (!$result) {
             common_log_db_error($msg, 'INSERT', __FILE__);
             return _('Could not insert message.');
         }
-        
+
         $orig = clone($msg);
         $msg->uri = common_local_url('showmessage', array('message' => $msg->id));
-        
+
         $result = $msg->update($orig);
-        
+
         if (!$result) {
             common_log_db_error($msg, 'UPDATE', __FILE__);
             return _('Could not update message with new URI.');
         }
-        
+
         return $msg;
     }
+
+    static function maxContent()
+    {
+        $desclimit = common_config('message', 'contentlimit');
+        // null => use global limit (distinct from 0!)
+        if (is_null($desclimit)) {
+            $desclimit = common_config('site', 'textlimit');
+        }
+        return $desclimit;
+    }
+
+    static function contentTooLong($content)
+    {
+        $contentlimit = self::maxContent();
+        return ($contentlimit > 0 && !empty($content) && (mb_strlen($content) > $contentlimit));
+    }
 }
index 7d050262674265f13dfe0cbeb577aa1a41e62c9a..93d5de79081d7395efc71e256e0d5cf4d8f29267 100644 (file)
@@ -40,7 +40,7 @@ class Notice extends Memcached_DataObject
     public $id;                              // int(4)  primary_key not_null
     public $profile_id;                      // int(4)   not_null
     public $uri;                             // varchar(255)  unique_key
-    public $content;                         // varchar(140)
+    public $content;                         // text()
     public $rendered;                        // text()
     public $url;                             // varchar(255)
     public $created;                         // datetime()   not_null
@@ -51,9 +51,7 @@ class Notice extends Memcached_DataObject
     public $conversation;                    // int(4)
 
     /* Static get */
-    function staticGet($k,$v=NULL) {
-        return Memcached_DataObject::staticGet('Notice',$k,$v);
-    }
+    function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Notice',$k,$v); }
 
     /* the code above is auto generated do not remove the tag below */
     ###END_AUTOCODE
@@ -75,7 +73,21 @@ class Notice extends Memcached_DataObject
         $this->blowFavesCache(true);
         $this->blowSubsCache(true);
 
+        // For auditing purposes, save a record that the notice
+        // was deleted.
+
+        $deleted = new Deleted_notice();
+
+        $deleted->id         = $this->id;
+        $deleted->profile_id = $this->profile_id;
+        $deleted->uri        = $this->uri;
+        $deleted->created    = $this->created;
+        $deleted->deleted    = common_sql_now();
+
         $this->query('BEGIN');
+
+        $deleted->insert();
+
         //Null any notices that are replies to this notice
         $this->query(sprintf("UPDATE notice set reply_to = null WHERE reply_to = %d", $this->id));
         $related = array('Reply',
@@ -140,31 +152,31 @@ class Notice extends Memcached_DataObject
 
         $final = common_shorten_links($content);
 
-        if (mb_strlen($final) > 140) {
-            common_log(LOG_INFO, 'Rejecting notice that is too long.');
-            return _('Problem saving notice. Too long.');
+        if (Notice::contentTooLong($final)) {
+            throw new ClientException(_('Problem saving notice. Too long.'));
         }
 
         if (!$profile) {
-            common_log(LOG_ERR, 'Problem saving notice. Unknown user.');
-            return _('Problem saving notice. Unknown user.');
+            throw new ClientException(_('Problem saving notice. Unknown user.'));
         }
 
         if (common_config('throttle', 'enabled') && !Notice::checkEditThrottle($profile_id)) {
             common_log(LOG_WARNING, 'Excessive posting by profile #' . $profile_id . '; throttled.');
-            return _('Too many notices too fast; take a breather and post again in a few minutes.');
+            throw new ClientException(_('Too many notices too fast; take a breather '.
+                                        'and post again in a few minutes.'));
         }
 
         if (common_config('site', 'dupelimit') > 0 && !Notice::checkDupes($profile_id, $final)) {
             common_log(LOG_WARNING, 'Dupe posting by profile #' . $profile_id . '; throttled.');
-                       return _('Too many duplicate messages too quickly; take a breather and post again in a few minutes.');
+                       throw new ClientException(_('Too many duplicate messages too quickly;'.
+                                        ' take a breather and post again in a few minutes.'));
         }
 
                $banned = common_config('profile', 'banned');
 
         if ( in_array($profile_id, $banned) || in_array($profile->nickname, $banned)) {
             common_log(LOG_WARNING, "Attempted post from banned user: $profile->nickname (user id = $profile_id).");
-            return _('You are banned from posting notices on this site.');
+            throw new ClientException(_('You are banned from posting notices on this site.'));
         }
 
         $notice = new Notice();
@@ -210,7 +222,7 @@ class Notice extends Memcached_DataObject
 
             if (!$id) {
                 common_log_db_error($notice, 'INSERT', __FILE__);
-                return _('Problem saving notice.');
+                throw new ServerException(_('Problem saving notice.'));
             }
 
             // Update ID-dependent columns: URI, conversation
@@ -235,7 +247,7 @@ class Notice extends Memcached_DataObject
             if ($changed) {
                 if (!$notice->update($orig)) {
                     common_log_db_error($notice, 'UPDATE', __FILE__);
-                    return _('Problem saving notice.');
+                    throw new ServerException(_('Problem saving notice.'));
                 }
             }
 
@@ -1181,10 +1193,11 @@ class Notice extends Memcached_DataObject
         $attachments = $this->attachments();
         if($attachments){
             foreach($attachments as $attachment){
-                if ($attachment->isEnclosure()) {
-                    $attributes = array('rel'=>'enclosure','href'=>$attachment->url,'type'=>$attachment->mimetype,'length'=>$attachment->size);
-                    if($attachment->title){
-                        $attributes['title']=$attachment->title;
+                $enclosure=$attachment->getEnclosure();
+                if ($enclosure) {
+                    $attributes = array('rel'=>'enclosure','href'=>$enclosure->url,'type'=>$enclosure->mimetype,'length'=>$enclosure->size);
+                    if($enclosure->title){
+                        $attributes['title']=$enclosure->title;
                     }
                     $xs->element('link', $attributes, null);
                 }
@@ -1335,4 +1348,20 @@ class Notice extends Memcached_DataObject
             return $last->id;
         }
     }
+
+    static function maxContent()
+    {
+        $contentlimit = common_config('notice', 'contentlimit');
+        // null => use global limit (distinct from 0!)
+        if (is_null($contentlimit)) {
+            $contentlimit = common_config('site', 'textlimit');
+        }
+        return $contentlimit;
+    }
+
+    static function contentTooLong($content)
+    {
+        $contentlimit = self::maxContent();
+        return ($contentlimit > 0 && !empty($content) && (mb_strlen($content) > $contentlimit));
+    }
 }
index 6ad0e7a3a3b3f3884fbe221466cb034938550551..7f0d1275833daa51feee64172b8a35e8ec2ff346 100644 (file)
@@ -35,14 +35,13 @@ class Profile extends Memcached_DataObject
     public $fullname;                        // varchar(255)  multiple_key
     public $profileurl;                      // varchar(255)
     public $homepage;                        // varchar(255)  multiple_key
-    public $bio;                             // varchar(140)  multiple_key
+    public $bio;                             // text()  multiple_key
     public $location;                        // varchar(255)  multiple_key
     public $created;                         // datetime()   not_null
     public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
 
     /* Static get */
-    function staticGet($k,$v=null)
-    { return Memcached_DataObject::staticGet('Profile',$k,$v); }
+    function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Profile',$k,$v); }
 
     /* the code above is auto generated do not remove the tag below */
     ###END_AUTOCODE
@@ -461,4 +460,20 @@ class Profile extends Memcached_DataObject
             $c->delete(common_cache_key('profile:notice_count:'.$this->id));
         }
     }
+
+    static function maxBio()
+    {
+        $biolimit = common_config('profile', 'biolimit');
+        // null => use global limit (distinct from 0!)
+        if (is_null($biolimit)) {
+            $biolimit = common_config('site', 'textlimit');
+        }
+        return $biolimit;
+    }
+
+    static function bioTooLong($bio)
+    {
+        $biolimit = self::maxBio();
+        return ($biolimit > 0 && !empty($bio) && (mb_strlen($bio) > $biolimit));
+    }
 }
index 8386f1e185bb7e41bbf4191e4a504bb195b9b848..3f7ed09bb7ae508c324ebec07f436060d6f40ca1 100644 (file)
@@ -103,10 +103,7 @@ class User extends Memcached_DataObject
         }
         $toupdate = implode(', ', $parts);
 
-        $table = $this->tableName();
-        if(common_config('db','quote_identifiers')) {
-            $table = '"' . $table . '"';
-        }
+        $table = common_database_tablename($this->tableName());
         $qry = 'UPDATE ' . $table . ' SET ' . $toupdate .
           ' WHERE id = ' . $this->id;
         $orig->decache();
@@ -634,11 +631,7 @@ class User extends Memcached_DataObject
           'ORDER BY subscription.created DESC ';
 
         if ($offset) {
-            if (common_config('db','type') == 'pgsql') {
-                $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
-            } else {
-                $qry .= ' LIMIT ' . $offset . ', ' . $limit;
-            }
+            $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
         }
 
         $profile = new Profile();
@@ -661,11 +654,7 @@ class User extends Memcached_DataObject
           'AND subscription.subscribed != subscription.subscriber ' .
           'ORDER BY subscription.created DESC ';
 
-        if (common_config('db','type') == 'pgsql') {
-            $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
-        } else {
-            $qry .= ' LIMIT ' . $offset . ', ' . $limit;
-        }
+        $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
 
         $profile = new Profile();
 
@@ -674,19 +663,81 @@ class User extends Memcached_DataObject
         return $profile;
     }
 
-    function hasOpenID()
+    function getDesign()
+    {
+        return Design::staticGet('id', $this->design_id);
+    }
+
+    function hasRole($name)
+    {
+        $role = User_role::pkeyGet(array('user_id' => $this->id,
+                                         'role' => $name));
+        return (!empty($role));
+    }
+
+    function grantRole($name)
+    {
+        $role = new User_role();
+
+        $role->user_id = $this->id;
+        $role->role    = $name;
+        $role->created = common_sql_now();
+
+        $result = $role->insert();
+
+        if (!$result) {
+            common_log_db_error($role, 'INSERT', __FILE__);
+            return false;
+        }
+
+        return true;
+    }
+
+    function revokeRole($name)
     {
-        $oid = new User_openid();
+        $role = User_role::pkeyGet(array('user_id' => $this->id,
+                                         'role' => $name));
+
+        if (empty($role)) {
+            throw new Exception('Cannot revoke role "'.$name.'" for user #'.$this->id.'; does not exist.');
+        }
 
-        $oid->user_id = $this->id;
+        $result = $role->delete();
 
-        $cnt = $oid->find();
+        if (!$result) {
+            common_log_db_error($role, 'DELETE', __FILE__);
+            throw new Exception('Cannot revoke role "'.$name.'" for user #'.$this->id.'; database error.');
+        }
 
-        return ($cnt > 0);
+        return true;
     }
 
-    function getDesign()
+    /**
+     * Does this user have the right to do X?
+     *
+     * With our role-based authorization, this is merely a lookup for whether the user
+     * has a particular role. The implementation currently uses a switch statement
+     * to determine if the user has the pre-defined role to exercise the right. Future
+     * implementations may allow per-site roles, and different mappings of roles to rights.
+     *
+     * @param $right string Name of the right, usually a constant in class Right
+     * @return boolean whether the user has the right in question
+     */
+
+    function hasRight($right)
     {
-        return Design::staticGet('id', $this->design_id);
+        $result = false;
+        if (Event::handle('UserRightsCheck', array($this, $right, &$result))) {
+            switch ($right)
+            {
+             case Right::deleteOthersNotice:
+                $result = $this->hasRole('moderator');
+                break;
+             default:
+                $result = false;
+                break;
+            }
+        }
+        return $result;
     }
 }
index ea19cbb97acff0b2a752ebc1793d02e81a94903b..310ecff1ef766726197eaa06457b6a3c5cfcc405 100644 (file)
@@ -13,7 +13,7 @@ class User_group extends Memcached_DataObject
     public $nickname;                        // varchar(64)  unique_key
     public $fullname;                        // varchar(255)
     public $homepage;                        // varchar(255)
-    public $description;                     // varchar(140)
+    public $description;                     // text()
     public $location;                        // varchar(255)
     public $original_logo;                   // varchar(255)
     public $homepage_logo;                   // varchar(255)
@@ -298,6 +298,22 @@ class User_group extends Memcached_DataObject
         return $ids;
     }
 
+    static function maxDescription()
+    {
+        $desclimit = common_config('group', 'desclimit');
+        // null => use global limit (distinct from 0!)
+        if (is_null($desclimit)) {
+            $desclimit = common_config('site', 'textlimit');
+        }
+        return $desclimit;
+    }
+
+    static function descriptionTooLong($desc)
+    {
+        $desclimit = self::maxDescription();
+        return ($desclimit > 0 && !empty($desc) && (mb_strlen($desc) > $desclimit));
+    }
+
     function asAtomEntry($namespace=false, $source=false)
     {
         $xs = new XMLStringer(true);
diff --git a/classes/User_openid.php b/classes/User_openid.php
deleted file mode 100644 (file)
index f4fda1c..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-<?php
-/**
- * Table Definition for user_openid
- */
-require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
-
-class User_openid extends Memcached_DataObject 
-{
-    ###START_AUTOCODE
-    /* the code below is auto generated do not remove the above tag */
-
-    public $__table = 'user_openid';                     // table name
-    public $canonical;                       // varchar(255)  primary_key not_null
-    public $display;                         // varchar(255)  unique_key not_null
-    public $user_id;                         // int(4)   not_null
-    public $created;                         // datetime()   not_null
-    public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
-
-    /* Static get */
-    function staticGet($k,$v=null)
-    { return Memcached_DataObject::staticGet('User_openid',$k,$v); }
-
-    /* the code above is auto generated do not remove the tag below */
-    ###END_AUTOCODE
-}
diff --git a/classes/User_role.php b/classes/User_role.php
new file mode 100644 (file)
index 0000000..85ecfb4
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+/*
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.     If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
+
+/**
+ * Table Definition for user_role
+ */
+
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class User_role extends Memcached_DataObject
+{
+    ###START_AUTOCODE
+    /* the code below is auto generated do not remove the above tag */
+
+    public $__table = 'user_role';                       // table name
+    public $user_id;                         // int(4)  primary_key not_null
+    public $role;                            // varchar(32)  primary_key not_null
+    public $created;                         // datetime()   not_null
+
+    /* Static get */
+    function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('User_role',$k,$v); }
+
+    /* the code above is auto generated do not remove the tag below */
+    ###END_AUTOCODE
+
+    function &pkeyGet($kv)
+    {
+        return Memcached_DataObject::pkeyGet('User_role', $kv);
+    }
+}
index 766bed75deff2a8af0134b0c6773dac420895844..453981cd64f7ad34f1c74891b1e734330ad100e3 100644 (file)
@@ -1,4 +1,3 @@
-
 [avatar]
 profile_id = 129
 original = 17
@@ -16,6 +15,15 @@ width = K
 height = K
 url = U
 
+[config]
+section = 130
+setting = 130
+value = 2
+
+[config__keys]
+section = K
+setting = K
+
 [confirm_address]
 code = 130
 user_id = 129
@@ -38,6 +46,17 @@ modified = 384
 [consumer__keys]
 consumer_key = K
 
+[deleted_notice]
+id = 129
+profile_id = 129
+uri = 2
+created = 142
+deleted = 142
+
+[deleted_notice__keys]
+id = K
+uri = U
+
 [design]
 id = 129
 backgroundcolor = 1
@@ -78,6 +97,7 @@ id = N
 file_id = 129
 version = 2
 type = 2
+mimetype = 2
 provider = 2
 provider_url = 2
 width = 1
@@ -228,7 +248,7 @@ id = 129
 uri = 2
 from_profile = 129
 to_profile = 129
-content = 2
+content = 34
 rendered = 34
 url = 2
 created = 142
@@ -255,7 +275,7 @@ ts = K
 id = 129
 profile_id = 129
 uri = 2
-content = 2
+content = 34
 rendered = 34
 url = 2
 created = 142
@@ -303,7 +323,7 @@ nickname = 130
 fullname = 2
 profileurl = 2
 homepage = 2
-bio = 2
+bio = 34
 location = 2
 created = 142
 modified = 384
@@ -475,7 +495,7 @@ id = 129
 nickname = 2
 fullname = 2
 homepage = 2
-description = 2
+description = 34
 location = 2
 original_logo = 2
 homepage_logo = 2
@@ -498,3 +518,12 @@ modified = 384
 [user_openid__keys]
 canonical = K
 display = U
+
+[user_role]
+user_id = 129
+role = 130
+created = 142
+
+[user_role__keys]
+user_id = K
+role = K
index bd3776a47f4a0620c4a3a3c02e588f58af3d911a..997c9d6b0bccb971157c6cc50f5689fc17419f87 100644 (file)
@@ -38,8 +38,6 @@ $config['site']['path'] = 'statusnet';
 // $config['site']['closed'] = true;
 // Only allow registration for people invited by another user
 // $config['site']['inviteonly'] = true;
-// Only allow registrations and logins through OpenID
-// $config['site']['openidonly'] = true;
 // Make the site invisible to  non-logged-in users
 // $config['site']['private'] = true;
 
@@ -99,9 +97,6 @@ $config['sphinx']['port'] = 3312;
 // $config['xmpp']['public'][] = 'someindexer@example.net';
 // $config['xmpp']['debug'] = false;
 
-// Disable OpenID
-// $config['openid']['enabled'] = false;
-
 // Turn off invites
 // $config['invite']['enabled'] = false;
 
diff --git a/db/08to09.sql b/db/08to09.sql
new file mode 100644 (file)
index 0000000..953e0e5
--- /dev/null
@@ -0,0 +1,34 @@
+alter table notice
+     modify column content text comment 'update content';
+
+alter table message
+     modify column content text comment 'message content';
+
+alter table profile
+     modify column bio text comment 'descriptive biography';
+
+alter table user_group
+     modify column description text comment 'group description';
+
+alter table file_oembed
+     add column mimetype varchar(50) comment 'mime type of resource';
+
+create table config (
+
+    section varchar(32) comment 'configuration section',
+    setting varchar(32) comment 'configuration setting',
+    value varchar(255) comment 'configuration value',
+
+    constraint primary key (section, setting)
+
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
+create table user_role (
+
+    user_id integer not null comment 'user having the role' references user (id),
+    role    varchar(32) not null comment 'string representing the role',
+    created datetime not null comment 'date the role was granted',
+
+    constraint primary key (user_id, role)
+
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
diff --git a/db/08to09_pg.sql b/db/08to09_pg.sql
new file mode 100644 (file)
index 0000000..9e37314
--- /dev/null
@@ -0,0 +1,40 @@
+-- SQL commands to update an 0.8.x version of Laconica
+-- to 0.9.x.
+
+--these are just comments
+/*
+alter table notice
+     modify column content text comment 'update content';
+
+alter table message
+     modify column content text comment 'message content';
+
+alter table profile
+     modify column bio text comment 'descriptive biography';
+
+alter table user_group
+     modify column description text comment 'group description';
+*/
+
+alter table file_oembed
+     add column mimetype varchar(50) /*comment 'mime type of resource'*/;
+
+create table config (
+
+    section varchar(32) /* comment 'configuration section'*/,
+    setting varchar(32) /* comment 'configuration setting'*/,
+    value varchar(255) /* comment 'configuration value'*/,
+
+    primary key (section, setting)
+
+);
+
+create table user_role (
+
+    user_id integer not null /* comment 'user having the role'*/ references "user" (id),
+    role    varchar(32) not null /* comment 'string representing the role'*/,
+    created timestamp /* not null comment 'date the role was granted'*/,
+
+    primary key (user_id, role)
+
+);
index 2c04f680a85d587a032cd06fada5c63c8488eaa5..221d60ce37a6b336ee6bf0be95bae0e44226d96b 100644 (file)
@@ -6,7 +6,7 @@ create table profile (
     fullname varchar(255) comment 'display name',
     profileurl varchar(255) comment 'URL, cached so we dont regenerate',
     homepage varchar(255) comment 'identifying URL',
-    bio varchar(140) comment 'descriptive biography',
+    bio text comment 'descriptive biography',
     location varchar(255) comment 'physical location',
     created datetime not null comment 'date this record was created',
     modified timestamp comment 'date this record was modified',
@@ -110,7 +110,7 @@ create table notice (
     id integer auto_increment primary key comment 'unique identifier',
     profile_id integer not null comment 'who made the update' references profile (id),
     uri varchar(255) unique key comment 'universally unique identifier, usually a tag URI',
-    content varchar(140) comment 'update content',
+    content text comment 'update content',
     rendered text comment 'HTML version of the content',
     url varchar(255) comment 'URL of any attachment (image, video, bookmark, whatever)',
     created datetime not null comment 'date this record was created',
@@ -331,7 +331,7 @@ create table message (
     uri varchar(255) unique key comment 'universally unique identifier',
     from_profile integer not null comment 'who the message is from' references profile (id),
     to_profile integer not null comment 'who the message is to' references profile (id),
-    content varchar(140) comment 'message content',
+    content text comment 'message content',
     rendered text comment 'HTML version of the content',
     url varchar(255) comment 'URL of any attachment (image, video, bookmark, whatever)',
     created datetime not null comment 'date this record was created',
@@ -380,7 +380,7 @@ create table user_group (
     nickname varchar(64) unique key comment 'nickname for addressing',
     fullname varchar(255) comment 'display name',
     homepage varchar(255) comment 'URL, cached so we dont regenerate',
-    description varchar(140) comment 'descriptive biography',
+    description text comment 'group description',
     location varchar(255) comment 'related physical location, if any',
 
     original_logo varchar(255) comment 'original size logo',
@@ -450,6 +450,7 @@ create table file_oembed (
     file_id integer primary key comment 'oEmbed for that URL/file' references file (id),
     version varchar(20) comment 'oEmbed spec. version',
     type varchar(20) comment 'oEmbed type: photo, video, link, rich',
+    mimetype varchar(50) comment 'mime type of resource',
     provider varchar(50) comment 'name of this oEmbed provider',
     provider_url varchar(255) comment 'URL of this oEmbed provider',
     width integer comment 'width of oEmbed resource when available',
@@ -534,4 +535,36 @@ create table session (
 
     index session_modified_idx (modified)
 
-) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
\ No newline at end of file
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
+create table deleted_notice (
+
+    id integer primary key comment 'identity of notice',
+    profile_id integer not null comment 'author of the notice',
+    uri varchar(255) unique key comment 'universally unique identifier, usually a tag URI',
+    created datetime not null comment 'date the notice record was created',
+    deleted datetime not null comment 'date the notice record was created',
+
+    index deleted_notice_profile_id_idx (profile_id)
+
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
+create table config (
+
+    section varchar(32) comment 'configuration section',
+    setting varchar(32) comment 'configuration setting',
+    value varchar(255) comment 'configuration value',
+
+    constraint primary key (section, setting)
+
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
+create table user_role (
+
+    user_id integer not null comment 'user having the role' references user (id),
+    role    varchar(32) not null comment 'string representing the role',
+    created datetime not null comment 'date the role was granted',
+
+    constraint primary key (user_id, role)
+
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
index ad34720a23357e405c99a7ff433303385d4df041..672877ddf0bac25ba3bdfdbcb0c18e8a14460470 100644 (file)
@@ -465,6 +465,7 @@ create table file_oembed (
     file_id bigint default nextval('file_oembed_seq') primary key /* comment 'unique identifier' */,
     version varchar(20),
     type varchar(20),
+    mimetype varchar(50), 
     provider varchar(50),
     provider_url varchar(255),
     width integer,
@@ -529,6 +530,17 @@ create table session (
 
 create index session_modified_idx on session (modified);
 
+create table deleted_notice (
+
+    id integer primary key /* comment 'identity of notice'*/ ,
+    profile_id integer /* not null comment 'author of the notice'*/,
+    uri varchar(255) unique /* comment 'universally unique identifier, usually a tag URI'*/,
+    created timestamp not null  /* comment 'date the notice record was created'*/ ,
+    deleted timestamp not null DEFAULT CURRENT_TIMESTAMP /* comment 'date the notice record was created'*/
+);
+
+CREATE index deleted_notice_profile_id_idx on deleted_notice (profile_id);
+
 
 /* Textsearch stuff */
 
@@ -537,3 +549,23 @@ create index noticecontent_idx on notice using gist(to_tsvector('english',conten
 create trigger textsearchupdate before insert or update on profile for each row
 execute procedure tsvector_update_trigger(textsearch, 'pg_catalog.english', nickname, fullname, location, bio, homepage);
 
+
+create table config (
+
+    section varchar(32) /* comment 'configuration section'*/,
+    setting varchar(32) /* comment 'configuration setting'*/,
+    value varchar(255) /* comment 'configuration value'*/,
+
+    primary key (section, setting)
+
+);
+
+create table user_role (
+
+    user_id integer not null /* comment 'user having the role'*/ references "user" (id),
+    role    varchar(32) not null /* comment 'string representing the role'*/,
+    created timestamp /* not null comment 'date the role was granted'*/,
+
+    primary key (user_id, role)
+
+);
index 8d7acf63b4738e65bfe53fc98b43515bf322b4a5..93300ab242e3c517cc4f25b54d5c3ad898ef7e9c 100644 (file)
@@ -26,7 +26,6 @@ Here are some documents that you might find helpful in understanding
 * [SMS](%%doc.sms%%) - tying your cellphone to %%site.name%%
 * [tags](%%doc.tags%%) - different ways to use tagging
 * [Groups](%%doc.groups%%) - joining together in groups
-* [OpenID](%%doc.openid%%) - what OpenID is and how to use it with this service
 * [OpenMicroBlogging](%%doc.openmublog%%) - subscribing to remote users
 * [Privacy](%%doc.privacy%%) - %%site.name%%'s privacy policy
 * [Source](%%doc.source%%) - How to get the StatusNet source code
diff --git a/doc-src/openid b/doc-src/openid
deleted file mode 100644 (file)
index c741e36..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-%%site.name%% supports the [OpenID](http://openid.net/) standard for single signon between Web sites. OpenID lets you log into many different Web sites without using a different password for each. (See [Wikipedia's OpenID article](http://en.wikipedia.org/wiki/OpenID) for more information.)
-
-If you already have an account on %%site.name%%, you can [login](%%action.login%%) with your username and password as usual.
-To use OpenID in the future, you can [add an OpenID to your account](%%action.openidsettings%%) after you have logged in normally.
-
-There are many [Public OpenID providers](http://wiki.openid.net/Public_OpenID_providers), and you may already have an OpenID-enabled account on another service.
-
-* On wikis: If you have an account on an OpenID-enabled wiki, like [Wikitravel](http://wikitravel.org/), [wikiHow](http://www.wikihow.com/), [Vinismo](http://vinismo.com/), [AboutUs](http://aboutus.org/) or [Keiki](http://kei.ki/), you can log in to %%site.name%% by entering the **full URL** of your user page on that other wiki in the box above. For example, *http://kei.ki/en/User:Evan*.
-* [Yahoo!](http://openid.yahoo.com/) : If you have an account with Yahoo!, you can log in to this site by entering your Yahoo!-provided OpenID in the box above. Yahoo! OpenID URLs have the form *https://me.yahoo.com/yourusername*.
-* [AOL](http://dev.aol.com/aol-and-63-million-openids) : If you have an account with [AOL](http://www.aol.com/), like an [AIM](http://www.aim.com/) account, you can log in to %%site.name%% by entering your AOL-provided OpenID in the box above. AOL OpenID URLs have the form *http://openid.aol.com/yourusername*. Your username should be all lowercase, no spaces.
-* [Blogger](http://bloggerindraft.blogspot.com/2008/01/new-feature-blogger-as-openid-provider.html), [Wordpress.com](http://faq.wordpress.com/2007/03/06/what-is-openid/), [LiveJournal](http://www.livejournal.com/openid/about.bml), [Vox](http://bradfitz.vox.com/library/post/openid-for-vox.html) : If you have a blog on any of these services, enter your blog URL in the box above. For example, *http://yourusername.blogspot.com/*, *http://yourusername.wordpress.com/*, *http://yourusername.livejournal.com/*, or *http://yourusername.vox.com/*.
diff --git a/extlib/libomb/base_url_xrds_mapper.php b/extlib/libomb/base_url_xrds_mapper.php
new file mode 100755 (executable)
index 0000000..6454595
--- /dev/null
@@ -0,0 +1,51 @@
+<?php
+
+require_once 'xrds_mapper.php';
+require_once 'constants.php';
+
+/**
+ * Map XRDS actions to URLs using base URLs.
+ *
+ * This interface specifies classes which write the XRDS file announcing
+ * the OMB server. An instance of an implementing class should be passed to
+ * OMB_Service_Provider->writeXRDS.
+ *
+ * PHP version 5
+ *
+ * LICENSE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package   OMB
+ * @author    Adrian Lang <mail@adrianlang.de>
+ * @copyright 2009 Adrian Lang
+ * @license   http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
+ **/
+
+class OMB_Base_URL_XRDS_Mapper implements OMB_XRDS_Mapper {
+
+  protected $urls;
+
+  public function __construct($oauth_base, $omb_base) {
+    $this->urls = array(
+        OAUTH_ENDPOINT_REQUEST => $oauth_base . 'requesttoken',
+        OAUTH_ENDPOINT_AUTHORIZE => $oauth_base . 'userauthorization',
+        OAUTH_ENDPOINT_ACCESS => $oauth_base . 'accesstoken',
+        OMB_ENDPOINT_POSTNOTICE => $omb_base . 'postnotice',
+        OMB_ENDPOINT_UPDATEPROFILE => $omb_base . 'updateprofile');
+  }
+
+  public function getURL($action) {
+    return $this->urls[$action];
+  }
+}
+?>
diff --git a/extlib/libomb/constants.php b/extlib/libomb/constants.php
new file mode 100644 (file)
index 0000000..a097443
--- /dev/null
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Constants for libomb
+ *
+ * This file contains constant definitions for libomb. The defined constants
+ * are service and namespace URIs for OAuth and OMB as used in XRDS.
+ *
+ * PHP version 5
+ *
+ * LICENSE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package   OMB
+ * @author    Adrian Lang <mail@adrianlang.de>
+ * @copyright 2009 Adrian Lang
+ * @license   http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
+ **/
+
+/**
+ * The OMB constants.
+ **/
+
+define('OMB_VERSION_01', 'http://openmicroblogging.org/protocol/0.1');
+
+/* The OMB version supported by this libomb version. */
+define('OMB_VERSION', OMB_VERSION_01);
+
+define('OMB_ENDPOINT_UPDATEPROFILE', OMB_VERSION . '/updateProfile');
+define('OMB_ENDPOINT_POSTNOTICE', OMB_VERSION . '/postNotice');
+
+/**
+ * The OAuth constants.
+ **/
+
+define('OAUTH_NAMESPACE', 'http://oauth.net/core/1.0/');
+
+define('OAUTH_ENDPOINT_REQUEST', OAUTH_NAMESPACE.'endpoint/request');
+define('OAUTH_ENDPOINT_AUTHORIZE', OAUTH_NAMESPACE.'endpoint/authorize');
+define('OAUTH_ENDPOINT_ACCESS', OAUTH_NAMESPACE.'endpoint/access');
+define('OAUTH_ENDPOINT_RESOURCE', OAUTH_NAMESPACE.'endpoint/resource');
+
+define('OAUTH_AUTH_HEADER', OAUTH_NAMESPACE.'parameters/auth-header');
+define('OAUTH_POST_BODY', OAUTH_NAMESPACE.'parameters/post-body');
+
+define('OAUTH_HMAC_SHA1', OAUTH_NAMESPACE.'signature/HMAC-SHA1');
+
+define('OAUTH_DISCOVERY', 'http://oauth.net/discovery/1.0');
+?>
diff --git a/extlib/libomb/datastore.php b/extlib/libomb/datastore.php
new file mode 100755 (executable)
index 0000000..ab52de5
--- /dev/null
@@ -0,0 +1,200 @@
+<?php
+
+require_once 'OAuth.php';
+
+/**
+ * Data access interface
+ *
+ * This interface specifies data access methods libomb needs. It should be
+ * implemented by libomb users. OMB_Datastore is libomb’s main interface to the
+ * application’s data. Objects corresponding to this interface are used in
+ * OMB_Service_Provider and OMB_Service_Consumer.
+ *
+ * Note that it’s implemented as a class since OAuthDataStore is as well a
+ * class, though only declaring methods.
+ *
+ * OMB_Datastore extends OAuthDataStore with two OAuth-related methods for token
+ * revoking and authorizing and all OMB-related methods.
+ * Refer to OAuth.php for a complete specification of OAuth-related methods.
+ *
+ * It is the user’s duty to signal and handle errors. libomb does not check
+ * return values nor handle exceptions. It is suggested to use exceptions.
+ * Note that lookup_token and getProfile return null if the requested object
+ * is not available. This is NOT an error and should not raise an exception.
+ * Same applies for lookup_nonce which returns a boolean value. These methods
+ * may nevertheless throw an exception, for example in case of a storage errors.
+ *
+ * Most of the parameters passed to these methods are unescaped and unverified
+ * user input. Therefore they should be handled with extra care to avoid
+ * security problems like SQL injections.
+ *
+ * PHP version 5
+ *
+ * LICENSE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package   OMB
+ * @author    Adrian Lang <mail@adrianlang.de>
+ * @copyright 2009 Adrian Lang
+ * @license   http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
+ **/
+
+class OMB_Datastore extends OAuthDataStore {
+
+  /*********
+   * OAUTH *
+   *********/
+
+  /**
+   * Revoke specified OAuth token
+   *
+   * Revokes the authorization token specified by $token_key.
+   * Throws exceptions in case of error.
+   *
+   * @param string $token_key The key of the token to be revoked
+   *
+   * @access public
+   **/
+  public function revoke_token($token_key) {
+    throw new Exception();
+  }
+
+  /**
+   * Authorize specified OAuth token
+   *
+   * Authorizes the authorization token specified by $token_key.
+   * Throws exceptions in case of error.
+   *
+   * @param string $token_key The key of the token to be authorized
+   *
+   * @access public
+   **/
+  public function authorize_token($token_key) {
+    throw new Exception();
+  }
+
+  /*********
+   *  OMB  *
+   *********/
+
+  /**
+   * Get profile by identifying URI
+   *
+   * Returns an OMB_Profile object representing the OMB profile identified by
+   * $identifier_uri.
+   * Returns null if there is no such OMB profile.
+   * Throws exceptions in case of other error.
+   *
+   * @param string $identifier_uri The OMB identifier URI specifying the
+   *                               requested profile
+   *
+   * @access public
+   *
+   * @return OMB_Profile The corresponding profile
+   **/
+  public function getProfile($identifier_uri) {
+    throw new Exception();
+  }
+
+  /**
+   * Save passed profile
+   *
+   * Stores the OMB profile $profile. Overwrites an existing entry.
+   * Throws exceptions in case of error.
+   *
+   * @param OMB_Profile $profile   The OMB profile which should be saved
+   *
+   * @access public
+   **/
+  public function saveProfile($profile) {
+    throw new Exception();
+  }
+
+  /**
+   * Save passed notice
+   *
+   * Stores the OMB notice $notice. The datastore may change the passed notice.
+   * This might by neccessary for URIs depending on a database key. Note that
+   * it is the user’s duty to present a mechanism for his OMB_Datastore to
+   * appropriately change his OMB_Notice. TODO: Ugly.
+   * Throws exceptions in case of error.
+   *
+   * @param OMB_Notice $notice The OMB notice which should be saved
+   *
+   * @access public
+   **/
+  public function saveNotice(&$notice) {
+    throw new Exception();
+  }
+
+  /**
+   * Get subscriptions of a given profile
+   *
+   * Returns an array containing subscription informations for the specified
+   * profile. Every array entry should in turn be an array with keys
+   *   'uri´: The identifier URI of the subscriber
+   *   'token´: The subscribe token
+   *   'secret´: The secret token
+   * Throws exceptions in case of error.
+   *
+   * @param string $subscribed_user_uri The OMB identifier URI specifying the
+   *                                    subscribed profile
+   *
+   * @access public
+   *
+   * @return mixed An array containing the subscriptions or 0 if no
+   *               subscription has been found.
+   **/
+  public function getSubscriptions($subscribed_user_uri) {
+    throw new Exception();
+  }
+
+  /**
+   * Delete a subscription
+   *
+   * Deletes the subscription from $subscriber_uri to $subscribed_user_uri.
+   * Throws exceptions in case of error.
+   *
+   * @param string $subscriber_uri      The OMB identifier URI specifying the
+   *                                    subscribing profile
+   *
+   * @param string $subscribed_user_uri The OMB identifier URI specifying the
+   *                                    subscribed profile
+   *
+   * @access public
+   **/
+  public function deleteSubscription($subscriber_uri, $subscribed_user_uri) {
+    throw new Exception();
+  }
+
+  /**
+   * Save a subscription
+   *
+   * Saves the subscription from $subscriber_uri to $subscribed_user_uri.
+   * Throws exceptions in case of error.
+   *
+   * @param string     $subscriber_uri      The OMB identifier URI specifying
+   *                                        the subscribing profile
+   *
+   * @param string     $subscribed_user_uri The OMB identifier URI specifying
+   *                                        the subscribed profile
+   * @param OAuthToken $token               The access token
+   *
+   * @access public
+   **/
+  public function saveSubscription($subscriber_uri, $subscribed_user_uri,
+                                                                       $token) {
+    throw new Exception();
+  }
+}
+?>
diff --git a/extlib/libomb/helper.php b/extlib/libomb/helper.php
new file mode 100644 (file)
index 0000000..a1f21f2
--- /dev/null
@@ -0,0 +1,99 @@
+<?php
+
+require_once 'Validate.php';
+
+/**
+ * Helper functions for libomb
+ *
+ * This file contains helper functions for libomb.
+ *
+ * PHP version 5
+ *
+ * LICENSE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package   OMB
+ * @author    Adrian Lang <mail@adrianlang.de>
+ * @copyright 2009 Adrian Lang
+ * @license   http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
+ **/
+
+class OMB_Helper {
+
+  /**
+   * Non-scalar constants
+   *
+   * The set of OMB and OAuth Services an OMB Server has to implement.
+   */
+
+  public static $OMB_SERVICES =
+    array(OMB_ENDPOINT_UPDATEPROFILE, OMB_ENDPOINT_POSTNOTICE);
+  public static $OAUTH_SERVICES =
+    array(OAUTH_ENDPOINT_REQUEST, OAUTH_ENDPOINT_AUTHORIZE, OAUTH_ENDPOINT_ACCESS);
+
+  /**
+   * Validate URL
+   *
+   * Basic URL validation. Currently http, https, ftp and gopher are supported
+   * schemes.
+   *
+   * @param string $url The URL which is to be validated.
+   *
+   * @return bool Whether URL is valid.
+   *
+   * @access public
+   */
+  public static function validateURL($url) {
+    return Validate::uri($url, array('allowed_schemes' => array('http', 'https',
+            'gopher', 'ftp')));
+  }
+
+  /**
+   * Validate Media type
+   *
+   * Basic Media type validation. Checks for valid maintype and correct format.
+   *
+   * @param string $mediatype The Media type which is to be validated.
+   *
+   * @return bool Whether media type is valid.
+   *
+   * @access public
+   */
+  public static function validateMediaType($mediatype) {
+    if (0 === preg_match('/^(\w+)\/([\w\d-+.]+)$/', $mediatype, $subtypes)) {
+      return false;
+    }
+    if (!in_array(strtolower($subtypes[1]), array('application', 'audio', 'image',
+              'message', 'model', 'multipart', 'text', 'video'))) {
+      return false;
+    }
+    return true;
+  }
+
+  /**
+   * Remove escaping from request parameters
+   *
+   * Neutralise the evil effects of magic_quotes_gpc in the current request.
+   * This is used before handing a request off to OAuthRequest::from_request.
+   * Many thanks to Ciaran Gultnieks for this fix.
+   *
+   * @access public
+   */
+  public static function removeMagicQuotesFromRequest() {
+    if(get_magic_quotes_gpc() == 1) {
+      $_POST = array_map('stripslashes', $_POST);
+      $_GET = array_map('stripslashes', $_GET);
+    }
+  }
+}
+?>
diff --git a/extlib/libomb/invalidparameterexception.php b/extlib/libomb/invalidparameterexception.php
new file mode 100755 (executable)
index 0000000..163e1dd
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+/**
+ * Exception stating that a passed parameter is invalid
+ *
+ * This exception is raised when a parameter does not obey the OMB standard.
+ *
+ * PHP version 5
+ *
+ * LICENSE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package   OMB
+ * @author    Adrian Lang <mail@adrianlang.de>
+ * @copyright 2009 Adrian Lang
+ * @license   http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
+ **/
+class OMB_InvalidParameterException extends Exception {
+  public function __construct($value, $type, $parameter) {
+    parent::__construct("Invalid value $value for parameter $parameter in $type");
+  }
+}
+?>
diff --git a/extlib/libomb/invalidyadisexception.php b/extlib/libomb/invalidyadisexception.php
new file mode 100755 (executable)
index 0000000..797b7b9
--- /dev/null
@@ -0,0 +1,31 @@
+<?php
+/**
+ * Exception stating that a requested url does not resolve to a valid yadis
+ *
+ * This exception is raised when OMB_Service is not able to discover a valid
+ * yadis location with XRDS.
+ *
+ * PHP version 5
+ *
+ * LICENSE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package   OMB
+ * @author    Adrian Lang <mail@adrianlang.de>
+ * @copyright 2009 Adrian Lang
+ * @license   http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
+ **/
+class OMB_InvalidYadisException extends Exception {
+
+}
+?>
diff --git a/extlib/libomb/notice.php b/extlib/libomb/notice.php
new file mode 100755 (executable)
index 0000000..9ac3664
--- /dev/null
@@ -0,0 +1,272 @@
+<?php
+require_once 'invalidparameterexception.php';
+require_once 'Validate.php';
+require_once 'helper.php';
+
+/**
+ * OMB Notice representation
+ *
+ * This class represents an OMB notice.
+ *
+ * Do not call the setters with null values. Instead, if you want to delete a
+ * field, pass an empty string. The getters will return null for empty fields.
+ *
+ * PHP version 5
+ *
+ * LICENSE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package   OMB
+ * @author    Adrian Lang <mail@adrianlang.de>
+ * @copyright 2009 Adrian Lang
+ * @license   http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
+ **/
+
+class OMB_Notice {
+  protected $author;
+  protected $uri;
+  protected $content;
+  protected $url;
+  protected $license_url; /* url is an own addition for clarification. */
+  protected $seealso_url; /* url is an own addition for clarification. */
+  protected $seealso_disposition;
+  protected $seealso_mediatype;
+  protected $seealso_license_url; /* url is an addition for clarification. */
+
+  /* The notice as OMB param array. Cached and rebuild on usage.
+     false while outdated. */
+  protected $param_array;
+
+  /**
+   * Constructor for OMB_Notice
+   *
+   * Initializes the OMB_Notice object with author, uri and content.
+   * These parameters are mandatory for postNotice.
+   *
+   * @param object $author  An OMB_Profile object representing the author of the
+   *                        notice.
+   * @param string $uri     The notice URI as defined by the OMB. A unique and
+   *                        unchanging identifier for a notice.
+   * @param string $content The content of the notice. 140 chars recommended,
+   *                        but there is no limit.
+   *
+   * @access public
+   */
+  public function __construct($author, $uri, $content) {
+    $this->content = $content;
+    if (is_null($author)) {
+      throw new OMB_InvalidParameterException('', 'notice', 'omb_listenee');
+    }
+    $this->author = $author;
+
+    if (!Validate::uri($uri)) {
+      throw new OMB_InvalidParameterException($uri, 'notice', 'omb_notice');
+    }
+    $this->uri = $uri;
+
+    $this->param_array = false;
+  }
+
+  /**
+   * Returns the notice as array
+   *
+   * The method returns an array which contains the whole notice as array. The
+   * array is cached and only rebuilt on changes of the notice.
+   * Empty optional values are not passed.
+   *
+   *  @access  public
+   *  @returns array The notice as parameter array
+   */
+  public function asParameters() {
+    if ($this->param_array !== false) {
+      return $this->param_array;
+    }
+
+    $this->param_array = array(
+                 'omb_notice' => $this->uri,
+                 'omb_notice_content' => $this->content);
+
+    if (!is_null($this->url))
+      $this->param_array['omb_notice_url'] = $this->url;
+
+    if (!is_null($this->license_url))
+      $this->param_array['omb_notice_license'] = $this->license_url;
+
+    if (!is_null($this->seealso_url)) {
+      $this->param_array['omb_seealso'] = $this->seealso_url;
+
+      /* This is actually a free interpretation of the OMB standard. We assume
+         that additional seealso parameters are not of any use if seealso itself
+         is not set. */
+      if (!is_null($this->seealso_disposition))
+        $this->param_array['omb_seealso_disposition'] =
+                                                     $this->seealso_disposition;
+
+      if (!is_null($this->seealso_mediatype))
+        $this->param_array['omb_seealso_mediatype'] = $this->seealso_mediatype;
+
+      if (!is_null($this->seealso_license_url))
+        $this->param_array['omb_seealso_license'] = $this->seealso_license_url;
+    }
+    return $this->param_array;
+  }
+
+  /**
+   * Builds an OMB_Notice object from array
+   *
+   * The method builds an OMB_Notice object from the passed parameters array.
+   * The array MUST provide a notice URI and content. The array fields HAVE TO
+   * be named according to the OMB standard, i. e. omb_notice_* and
+   * omb_seealso_*. Values are handled as not passed if the corresponding array
+   * fields are not set or the empty string.
+   *
+   * @param object $author     An OMB_Profile object representing the author of
+   *                           the notice.
+   * @param string $parameters An array containing the notice parameters.
+   *
+   * @access public
+   *
+   * @returns OMB_Notice The built OMB_Notice.
+   */
+  public static function fromParameters($author, $parameters) {
+    $notice = new OMB_Notice($author, $parameters['omb_notice'],
+                             $parameters['omb_notice_content']);
+
+    if (isset($parameters['omb_notice_url'])) {
+      $notice->setURL($parameters['omb_notice_url']);
+    }
+
+    if (isset($parameters['omb_notice_license'])) {
+      $notice->setLicenseURL($parameters['omb_notice_license']);
+    }
+
+    if (isset($parameters['omb_seealso'])) {
+      $notice->setSeealsoURL($parameters['omb_seealso']);
+    }
+
+    if (isset($parameters['omb_seealso_disposition'])) {
+      $notice->setSeealsoDisposition($parameters['omb_seealso_disposition']);
+    }
+
+    if (isset($parameters['omb_seealso_mediatype'])) {
+      $notice->setSeealsoMediatype($parameters['omb_seealso_mediatype']);
+    }
+
+    if (isset($parameters['omb_seealso_license'])) {
+      $notice->setSeealsoLicenseURL($parameters['omb_seealso_license']);
+    }
+    return $notice;
+  }
+
+  public function getAuthor() {
+    return $this->author;
+  }
+
+  public function getIdentifierURI() {
+    return $this->uri;
+  }
+
+  public function getContent() {
+    return $this->content;
+  }
+
+  public function getURL() {
+    return $this->url;
+  }
+
+  public function getLicenseURL() {
+    return $this->license_url;
+  }
+
+  public function getSeealsoURL() {
+    return $this->seealso_url;
+  }
+
+  public function getSeealsoDisposition() {
+    return $this->seealso_disposition;
+  }
+
+  public function getSeealsoMediatype() {
+    return $this->seealso_mediatype;
+  }
+
+  public function getSeealsoLicenseURL() {
+    return $this->seealso_license_url;
+  }
+
+  public function setURL($url) {
+    if ($url === '') {
+      $url = null;
+    } elseif (!OMB_Helper::validateURL($url)) {
+      throw new OMB_InvalidParameterException($url, 'notice', 'omb_notice_url');
+    }
+    $this->url = $url;
+    $this->param_array = false;
+  }
+
+  public function setLicenseURL($license_url) {
+    if ($license_url === '') {
+      $license_url = null;
+    } elseif (!OMB_Helper::validateURL($license_url)) {
+      throw new OMB_InvalidParameterException($license_url, 'notice',
+                                              'omb_notice_license');
+    }
+    $this->license_url = $license_url;
+    $this->param_array = false;
+  }
+
+  public function setSeealsoURL($seealso_url) {
+    if ($seealso_url === '') {
+      $seealso_url = null;
+    } elseif (!OMB_Helper::validateURL($seealso_url)) {
+      throw new OMB_InvalidParameterException($seealso_url, 'notice',
+                                              'omb_seealso');
+    }
+    $this->seealso_url = $seealso_url;
+    $this->param_array = false;
+  }
+
+  public function setSeealsoDisposition($seealso_disposition) {
+    if ($seealso_disposition === '') {
+      $seealso_disposition = null;
+    } elseif ($seealso_disposition !== 'link' && $seealso_disposition !== 'inline') {
+      throw new OMB_InvalidParameterException($seealso_disposition, 'notice',
+                                              'omb_seealso_disposition');
+    }
+    $this->seealso_disposition = $seealso_disposition;
+    $this->param_array = false;
+  }
+
+  public function setSeealsoMediatype($seealso_mediatype) {
+    if ($seealso_mediatype === '') {
+      $seealso_mediatype = null;
+    } elseif (!OMB_Helper::validateMediaType($seealso_mediatype)) {
+      throw new OMB_InvalidParameterException($seealso_mediatype, 'notice',
+                                              'omb_seealso_mediatype');
+    }
+    $this->seealso_mediatype = $seealso_mediatype;
+    $this->param_array = false;
+  }
+
+  public function setSeealsoLicenseURL($seealso_license_url) {
+    if ($seealso_license_url === '') {
+      $seealso_license_url = null;
+    } elseif (!OMB_Helper::validateURL($seealso_license_url)) {
+      throw new OMB_InvalidParameterException($seealso_license_url, 'notice',
+                                              'omb_seealso_license');
+    }
+    $this->seealso_license_url = $seealso_license_url;
+    $this->param_array = false;
+  }
+}
+?>
diff --git a/extlib/libomb/omb_yadis_xrds.php b/extlib/libomb/omb_yadis_xrds.php
new file mode 100755 (executable)
index 0000000..8992120
--- /dev/null
@@ -0,0 +1,196 @@
+<?php
+
+require_once 'Auth/Yadis/Yadis.php';
+require_once 'unsupportedserviceexception.php';
+require_once 'invalidyadisexception.php';
+
+/**
+ * OMB XRDS representation
+ *
+ * This class represents a Yadis XRDS file for OMB. It adds some useful methods to
+ * Auth_Yadis_XRDS.
+ *
+ * PHP version 5
+ *
+ * LICENSE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package   OMB
+ * @author    Adrian Lang <mail@adrianlang.de>
+ * @copyright 2009 Adrian Lang
+ * @license   http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
+ **/
+
+class OMB_Yadis_XRDS extends Auth_Yadis_XRDS {
+
+  protected $fetcher;
+
+  /**
+   * Create an instance from URL
+   *
+   * Constructs an OMB_Yadis_XRDS object from a given URL. A full Yadis
+   * discovery is performed on the URL and the XRDS is parsed.
+   * Throws an OMB_InvalidYadisException when no Yadis is discovered or the
+   * detected XRDS file is broken.
+   *
+   * @param string                 $url     The URL on which Yadis discovery
+   *                                        should be performed on
+   * @param Auth_Yadis_HTTPFetcher $fetcher A fetcher used to get HTTP
+   *                                        resources
+   *
+   * @access public
+   *
+   * @return OMB_Yadis_XRDS The initialized object representing the given
+   *                        resource
+   **/
+  public static function fromYadisURL($url, $fetcher) {
+    /* Perform a Yadis discovery. */
+    $yadis = Auth_Yadis_Yadis::discover($url, $fetcher);
+    if ($yadis->failed) {
+      throw new OMB_InvalidYadisException($url);
+    }
+
+    /* Parse the XRDS file. */
+    $xrds = OMB_Yadis_XRDS::parseXRDS($yadis->response_text);
+    if ($xrds === null) {
+      throw new OMB_InvalidYadisException($url);
+    }
+    $xrds->fetcher = $fetcher;
+    return $xrds;
+  }
+
+  /**
+   * Get a specific service
+   *
+   * Returns the Auth_Yadis_Service object corresponding to the given service
+   * URI.
+   * Throws an OMB_UnsupportedServiceException if the service is not available.
+   *
+   * @param string $service URI specifier of the requested service
+   *
+   * @access public
+   *
+   * @return Auth_Yadis_Service The object representing the requested service
+   **/
+  public function getService($service) {
+    $match = $this->services(array( create_function('$s',
+                           "return in_array('$service', \$s->getTypes());")));
+    if ($match === array()) {
+      throw new OMB_UnsupportedServiceException($service);
+    }
+    return $match[0];
+  }
+
+  /**
+   * Get a specific XRD
+   *
+   * Returns the OMB_Yadis_XRDS object corresponding to the given URI.
+   * Throws an OMB_UnsupportedServiceException if the XRD is not available.
+   * Note that getXRD tries to resolve external XRD parts as well.
+   *
+   * @param string $uri URI specifier of the requested XRD
+   *
+   * @access public
+   *
+   * @return OMB_Yadis_XRDS The object representing the requested XRD
+   **/
+  public function getXRD($uri) {
+    $nexthash = strpos($uri, '#');
+    if ($nexthash !== 0) {
+      if ($nexthash !== false) {
+        $cururi = substr($uri, 0, $nexthash);
+        $nexturi = substr($uri, $nexthash);
+      }
+      return
+        OMB_Yadis_XRDS::fromYadisURL($cururi, $this->fetcher)->getXRD($nexturi);
+    }
+
+    $id = substr($uri, 1);
+    foreach ($this->allXrdNodes as $node) {
+      $attrs = $this->parser->attributes($node);
+      if (array_key_exists('xml:id', $attrs) && $attrs['xml:id'] == $id) {
+        /* Trick the constructor into thinking this is the only node. */
+        $bogus_nodes = array($node);
+        return new OMB_Yadis_XRDS($this->parser, $bogus_nodes);
+      }
+    }
+    throw new OMB_UnsupportedServiceException($uri);
+  }
+
+  /**
+   * Parse an XML string containing a XRDS document
+   *
+   * Parse an XML string (XRDS document) and return either a
+   * Auth_Yadis_XRDS object or null, depending on whether the
+   * XRDS XML is valid.
+   * Copy and paste from parent to select correct constructor.
+   *
+   * @param string $xml_string An XRDS XML string.
+   *
+   * @access public
+   *
+   * @return mixed An instance of OMB_Yadis_XRDS or null,
+   *               depending on the validity of $xml_string
+   **/
+
+  public function &parseXRDS($xml_string, $extra_ns_map = null) {
+    $_null = null;
+
+    if (!$xml_string) {
+      return $_null;
+    }
+
+    $parser = Auth_Yadis_getXMLParser();
+
+    $ns_map = Auth_Yadis_getNSMap();
+
+    if ($extra_ns_map && is_array($extra_ns_map)) {
+      $ns_map = array_merge($ns_map, $extra_ns_map);
+    }
+
+    if (!($parser && $parser->init($xml_string, $ns_map))) {
+      return $_null;
+    }
+
+    // Try to get root element.
+    $root = $parser->evalXPath('/xrds:XRDS[1]');
+    if (!$root) {
+      return $_null;
+    }
+
+    if (is_array($root)) {
+      $root = $root[0];
+    }
+
+    $attrs = $parser->attributes($root);
+
+    if (array_key_exists('xmlns:xrd', $attrs) &&
+          $attrs['xmlns:xrd'] != Auth_Yadis_XMLNS_XRDS) {
+      return $_null;
+    } else if (array_key_exists('xmlns', $attrs) &&
+                   preg_match('/xri/', $attrs['xmlns']) &&
+                   $attrs['xmlns'] != Auth_Yadis_XMLNS_XRD_2_0) {
+      return $_null;
+    }
+
+    // Get the last XRD node.
+    $xrd_nodes = $parser->evalXPath('/xrds:XRDS[1]/xrd:XRD');
+
+    if (!$xrd_nodes) {
+      return $_null;
+    }
+
+    $xrds = new OMB_Yadis_XRDS($parser, $xrd_nodes);
+    return $xrds;
+  }
+}
diff --git a/extlib/libomb/plain_xrds_writer.php b/extlib/libomb/plain_xrds_writer.php
new file mode 100755 (executable)
index 0000000..b4a6e99
--- /dev/null
@@ -0,0 +1,124 @@
+<?php
+
+require_once 'xrds_writer.php';
+
+/**
+ * Write OMB-specific XRDS using XMLWriter.
+ *
+ * This class writes the XRDS file announcing the OMB server. It uses
+ * OMB_XMLWriter, which is a subclass of XMLWriter. An instance of
+ * OMB_Plain_XRDS_Writer should be passed to OMB_Service_Provider->writeXRDS.
+ *
+ * PHP version 5
+ *
+ * LICENSE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package   OMB
+ * @author    Adrian Lang <mail@adrianlang.de>
+ * @copyright 2009 Adrian Lang
+ * @license   http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
+ **/
+
+class OMB_Plain_XRDS_Writer implements OMB_XRDS_Writer {
+  public function writeXRDS($user, $mapper) {
+    header('Content-Type: application/xrds+xml');
+    $xw = new XMLWriter();
+    $xw->openURI('php://output');
+    $xw->setIndent(true);
+
+    $xw->startDocument('1.0', 'UTF-8');
+    $this->writeFullElement($xw, 'XRDS',  array('xmlns' => 'xri://$xrds'), array(
+        array('XRD',  array('xmlns' => 'xri://$xrd*($v*2.0)',
+                                          'xml:id' => 'oauth',
+                                          'xmlns:simple' => 'http://xrds-simple.net/core/1.0',
+                                          'version' => '2.0'), array(
+          array('Type', null, 'xri://$xrds*simple'),
+          array('Service', null, array(
+            array('Type', null, OAUTH_ENDPOINT_REQUEST),
+            array('URI', null, $mapper->getURL(OAUTH_ENDPOINT_REQUEST)),
+            array('Type', null, OAUTH_AUTH_HEADER),
+            array('Type', null, OAUTH_POST_BODY),
+            array('Type', null, OAUTH_HMAC_SHA1),
+            array('LocalID', null, $user->getIdentifierURI())
+          )),
+          array('Service', null, array(
+            array('Type', null, OAUTH_ENDPOINT_AUTHORIZE),
+            array('URI', null, $mapper->getURL(OAUTH_ENDPOINT_AUTHORIZE)),
+            array('Type', null, OAUTH_AUTH_HEADER),
+            array('Type', null, OAUTH_POST_BODY),
+            array('Type', null, OAUTH_HMAC_SHA1)
+          )),
+          array('Service', null, array(
+            array('Type', null, OAUTH_ENDPOINT_ACCESS),
+            array('URI', null, $mapper->getURL(OAUTH_ENDPOINT_ACCESS)),
+            array('Type', null, OAUTH_AUTH_HEADER),
+            array('Type', null, OAUTH_POST_BODY),
+            array('Type', null, OAUTH_HMAC_SHA1)
+          )),
+          array('Service', null, array(
+            array('Type', null, OAUTH_ENDPOINT_RESOURCE),
+            array('Type', null, OAUTH_AUTH_HEADER),
+            array('Type', null, OAUTH_POST_BODY),
+            array('Type', null, OAUTH_HMAC_SHA1)
+          ))
+        )),
+        array('XRD',  array('xmlns' => 'xri://$xrd*($v*2.0)',
+                                          'xml:id' => 'omb',
+                                          'xmlns:simple' => 'http://xrds-simple.net/core/1.0',
+                                          'version' => '2.0'), array(
+          array('Type', null, 'xri://$xrds*simple'),
+          array('Service', null, array(
+            array('Type', null, OMB_ENDPOINT_POSTNOTICE),
+            array('URI', null, $mapper->getURL(OMB_ENDPOINT_POSTNOTICE))
+          )),
+          array('Service', null, array(
+            array('Type', null, OMB_ENDPOINT_UPDATEPROFILE),
+            array('URI', null, $mapper->getURL(OMB_ENDPOINT_UPDATEPROFILE))
+          ))
+        )),
+        array('XRD',  array('xmlns' => 'xri://$xrd*($v*2.0)',
+                                          'version' => '2.0'), array(
+          array('Type', null, 'xri://$xrds*simple'),
+          array('Service', null, array(
+            array('Type', null, OAUTH_DISCOVERY),
+            array('URI', null, '#oauth')
+          )),
+          array('Service', null, array(
+            array('Type', null, OMB_VERSION),
+            array('URI', null, '#omb')
+          ))
+        ))
+      ));
+    $xw->endDocument();
+    $xw->flush();
+  }
+
+  public static function writeFullElement($xw, $tag, $attributes, $content) {
+    $xw->startElement($tag);
+    if (!is_null($attributes)) {
+      foreach ($attributes as $name => $value) {
+        $xw->writeAttribute($name, $value);
+      }
+    }
+    if (is_array($content)) {
+      foreach ($content as $values) {
+        OMB_Plain_XRDS_Writer::writeFullElement($xw, $values[0], $values[1], $values[2]);
+      }
+    } else {
+      $xw->text($content);
+    }
+    $xw->fullEndElement();
+  }
+}
+?>
diff --git a/extlib/libomb/profile.php b/extlib/libomb/profile.php
new file mode 100755 (executable)
index 0000000..13314d3
--- /dev/null
@@ -0,0 +1,317 @@
+<?php
+require_once 'invalidparameterexception.php';
+require_once 'Validate.php';
+require_once 'helper.php';
+
+/**
+ * OMB profile representation
+ *
+ * This class represents an OMB profile.
+ *
+ * Do not call the setters with null values. Instead, if you want to delete a
+ * field, pass an empty string. The getters will return null for empty fields.
+ *
+ * PHP version 5
+ *
+ * LICENSE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package   OMB
+ * @author    Adrian Lang <mail@adrianlang.de>
+ * @copyright 2009 Adrian Lang
+ * @license   http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
+ **/
+
+class OMB_Profile {
+  protected $identifier_uri;
+  protected $profile_url;
+  protected $nickname;
+  protected $license_url;
+  protected $fullname;
+  protected $homepage;
+  protected $bio;
+  protected $location;
+  protected $avatar_url;
+
+  /* The profile as OMB param array. Cached and rebuild on usage.
+     false while outdated. */
+  protected $param_array;
+
+  /**
+   * Constructor for OMB_Profile
+   *
+   * Initializes the OMB_Profile object with an identifier uri.
+   *
+   * @param string $identifier_uri The profile URI as defined by the OMB. A unique
+   *                               and unchanging identifier for a profile.
+   *
+   * @access public
+   */
+  public function __construct($identifier_uri) {
+    if (!Validate::uri($identifier_uri)) {
+      throw new OMB_InvalidParameterException($identifier_uri, 'profile',
+                                                'omb_listenee or omb_listener');
+    }
+    $this->identifier_uri = $identifier_uri;
+    $this->param_array = false;
+  }
+
+  /**
+   * Returns the profile as array
+   *
+   * The method returns an array which contains the whole profile as array. The
+   * array is cached and only rebuilt on changes of the profile.
+   *
+   * @param bool   $force_all Specifies whether empty fields should be added to
+   *                          the array as well. This is neccessary to clear
+   *                          fields via updateProfile.
+   *
+   * @param string $prefix    The common prefix to the key for all parameters.
+   *
+   * @access public
+   *
+   * @return array The profile as parameter array
+   */
+  public function asParameters($prefix, $force_all = false) {
+    if ($this->param_array === false) {
+      $this->param_array = array('' => $this->identifier_uri);
+
+      if ($force_all || !is_null($this->profile_url)) {
+        $this->param_array['_profile'] = $this->profile_url;
+      }
+
+      if ($force_all || !is_null($this->homepage)) {
+        $this->param_array['_homepage'] = $this->homepage;
+      }
+
+      if ($force_all || !is_null($this->nickname)) {
+        $this->param_array['_nickname'] = $this->nickname;
+      }
+
+      if ($force_all || !is_null($this->license_url)) {
+        $this->param_array['_license'] = $this->license_url;
+      }
+
+      if ($force_all || !is_null($this->fullname)) {
+        $this->param_array['_fullname'] = $this->fullname;
+      }
+
+      if ($force_all || !is_null($this->bio)) {
+        $this->param_array['_bio'] = $this->bio;
+      }
+
+      if ($force_all || !is_null($this->location)) {
+        $this->param_array['_location'] = $this->location;
+      }
+
+      if ($force_all || !is_null($this->avatar_url)) {
+        $this->param_array['_avatar'] = $this->avatar_url;
+      }
+
+    }
+    $ret = array();
+    foreach ($this->param_array as $k => $v) {
+      $ret[$prefix . $k] = $v;
+    }
+    return $ret;
+  }
+
+  /**
+   * Builds an OMB_Profile object from array
+   *
+   * The method builds an OMB_Profile object from the passed parameters array. The
+   * array MUST provide a profile URI. The array fields HAVE TO be named according
+   * to the OMB standard. The prefix (omb_listener or omb_listenee) is passed as a
+   * parameter.
+   *
+   * @param string $parameters An array containing the profile parameters.
+   * @param string $prefix     The common prefix of the profile parameter keys.
+   *
+   * @access public
+   *
+   * @returns OMB_Profile The built OMB_Profile.
+   */
+  public static function fromParameters($parameters, $prefix) {
+    if (!isset($parameters[$prefix])) {
+      throw new OMB_InvalidParameterException('', 'profile', $prefix);
+    }
+
+    $profile = new OMB_Profile($parameters[$prefix]);
+    $profile->updateFromParameters($parameters, $prefix);
+    return $profile;
+  }
+
+  /**
+   * Update from array
+   *
+   * Updates from the passed parameters array. The array does not have to
+   * provide a profile URI. The array fields HAVE TO be named according to the
+   * OMB standard. The prefix (omb_listener or omb_listenee) is passed as a
+   * parameter.
+   *
+   * @param string $parameters An array containing the profile parameters.
+   * @param string $prefix     The common prefix of the profile parameter keys.
+   *
+   * @access public
+   */
+  public function updateFromParameters($parameters, $prefix) {
+    if (isset($parameters[$prefix.'_profile'])) {
+      $this->setProfileURL($parameters[$prefix.'_profile']);
+    }
+
+    if (isset($parameters[$prefix.'_license'])) {
+      $this->setLicenseURL($parameters[$prefix.'_license']);
+    }
+
+    if (isset($parameters[$prefix.'_nickname'])) {
+      $this->setNickname($parameters[$prefix.'_nickname']);
+    }
+
+    if (isset($parameters[$prefix.'_fullname'])) {
+      $this->setFullname($parameters[$prefix.'_fullname']);
+    }
+
+    if (isset($parameters[$prefix.'_homepage'])) {
+      $this->setHomepage($parameters[$prefix.'_homepage']);
+    }
+
+    if (isset($parameters[$prefix.'_bio'])) {
+      $this->setBio($parameters[$prefix.'_bio']);
+    }
+
+    if (isset($parameters[$prefix.'_location'])) {
+      $this->setLocation($parameters[$prefix.'_location']);
+    }
+
+    if (isset($parameters[$prefix.'_avatar'])) {
+      $this->setAvatarURL($parameters[$prefix.'_avatar']);
+    }
+  }
+
+  public function getIdentifierURI() {
+    return $this->identifier_uri;
+  }
+
+  public function getProfileURL() {
+    return $this->profile_url;
+  }
+
+  public function getHomepage() {
+    return $this->homepage;
+  }
+
+  public function getNickname() {
+    return $this->nickname;
+  }
+
+  public function getLicenseURL() {
+    return $this->license_url;
+  }
+
+  public function getFullname() {
+    return $this->fullname;
+  }
+
+  public function getBio() {
+    return $this->bio;
+  }
+
+  public function getLocation() {
+    return $this->location;
+  }
+
+  public function getAvatarURL() {
+    return $this->avatar_url;
+  }
+
+  public function setProfileURL($profile_url) {
+    if (!OMB_Helper::validateURL($profile_url)) {
+      throw new OMB_InvalidParameterException($profile_url, 'profile',
+                                    'omb_listenee_profile or omb_listener_profile');
+    }
+    $this->profile_url = $profile_url;
+    $this->param_array = false;
+  }
+
+  public function setNickname($nickname) {
+    if (!Validate::string($nickname,
+                          array('min_length' => 1,
+                                'max_length' => 64,
+                                'format' => VALIDATE_NUM . VALIDATE_ALPHA))) {
+      throw new OMB_InvalidParameterException($nickname, 'profile', 'nickname');
+    }
+
+    $this->nickname = $nickname;
+    $this->param_array = false;
+  }
+
+  public function setLicenseURL($license_url) {
+    if (!OMB_Helper::validateURL($license_url)) {
+      throw new OMB_InvalidParameterException($license_url, 'profile',
+                                    'omb_listenee_license or omb_listener_license');
+    }
+    $this->license_url = $license_url;
+    $this->param_array = false;
+  }
+
+  public function setFullname($fullname) {
+    if ($fullname === '') {
+      $fullname = null;
+    } elseif (!Validate::string($fullname, array('max_length' => 255))) {
+      throw new OMB_InvalidParameterException($fullname, 'profile', 'fullname');
+    }
+    $this->fullname = $fullname;
+    $this->param_array = false;
+  }
+
+  public function setHomepage($homepage) {
+    if ($homepage === '') {
+      $homepage = null;
+    }
+    $this->homepage = $homepage;
+    $this->param_array = false;
+  }
+
+  public function setBio($bio) {
+    if ($bio === '') {
+      $bio = null;
+    } elseif (!Validate::string($bio, array('max_length' => 140))) {
+      throw new OMB_InvalidParameterException($bio, 'profile', 'fullname');
+    }
+    $this->bio = $bio;
+    $this->param_array = false;
+  }
+
+  public function setLocation($location) {
+    if ($location === '') {
+      $location = null;
+    } elseif (!Validate::string($location, array('max_length' => 255))) {
+      throw new OMB_InvalidParameterException($location, 'profile', 'fullname');
+    }
+    $this->location = $location;
+    $this->param_array = false;
+  }
+
+  public function setAvatarURL($avatar_url) {
+    if ($avatar_url === '') {
+      $avatar_url = null;
+    } elseif (!OMB_Helper::validateURL($avatar_url)) {
+      throw new OMB_InvalidParameterException($avatar_url, 'profile',
+                                      'omb_listenee_avatar or omb_listener_avatar');
+    }
+    $this->avatar_url = $avatar_url;
+    $this->param_array = false;
+  }
+
+}
+?>
diff --git a/extlib/libomb/remoteserviceexception.php b/extlib/libomb/remoteserviceexception.php
new file mode 100755 (executable)
index 0000000..374d159
--- /dev/null
@@ -0,0 +1,42 @@
+<?php
+/**
+ * Exception stating that the remote service had a failure
+ *
+ * This exception is raised when a remote service failed to return a valid
+ * response to a request or send a valid request.
+ *
+ * PHP version 5
+ *
+ * LICENSE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package   OMB
+ * @author    Adrian Lang <mail@adrianlang.de>
+ * @copyright 2009 Adrian Lang
+ * @license   http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
+ **/
+class OMB_RemoteServiceException extends Exception {
+  public static function fromYadis($request_uri, $result) {
+    if ($result->status == 200) {
+        $err = 'Got wrong response ' . $result->body;
+    } else {
+        $err = 'Got error code ' . $result->status . ' with response ' . $result->body;
+    }
+    return new OMB_RemoteServiceException($request_uri . ': ' .  $err);
+  }
+
+  public static function forRequest($action_uri, $failure) {
+    return new OMB_RemoteServiceException("Handler for $action_uri: " .  $failure);
+  }
+}
+?>
diff --git a/extlib/libomb/service_consumer.php b/extlib/libomb/service_consumer.php
new file mode 100755 (executable)
index 0000000..273fd05
--- /dev/null
@@ -0,0 +1,430 @@
+<?php
+
+require_once 'constants.php';
+require_once 'Validate.php';
+require_once 'Auth/Yadis/Yadis.php';
+require_once 'OAuth.php';
+require_once 'unsupportedserviceexception.php';
+require_once 'remoteserviceexception.php';
+require_once 'omb_yadis_xrds.php';
+require_once 'helper.php';
+
+/**
+ * OMB service representation
+ *
+ * This class represents a complete remote OMB service. It provides discovery
+ * and execution of the service’s methods.
+ *
+ * PHP version 5
+ *
+ * LICENSE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package   OMB
+ * @author    Adrian Lang <mail@adrianlang.de>
+ * @copyright 2009 Adrian Lang
+ * @license   http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
+ **/
+
+class OMB_Service_Consumer {
+  protected $url; /* The service URL */
+  protected $services; /* An array of strings mapping service URI to
+                          service URL */
+
+  protected $token; /* An OAuthToken */
+
+  protected $listener_uri; /* The URI identifying the listener, i. e. the
+                              remote user. */
+
+  protected $listenee_uri; /* The URI identifying the listenee, i. e. the
+                              local user during an auth request. */
+
+  /**
+   * According to OAuth Core 1.0, an user authorization request is no full-blown
+   * OAuth request. nonce, timestamp, consumer_key and signature are not needed
+   * in this step. See http://laconi.ca/trac/ticket/827 for more informations.
+   *
+   * Since Laconica up to version 0.7.2 performs a full OAuth request check, a
+   * correct request would fail.
+   **/
+  public $performLegacyAuthRequest = true;
+
+  /* Helper stuff we are going to need. */
+  protected $fetcher;
+  protected $oauth_consumer;
+  protected $datastore;
+
+  /**
+   * Constructor for OMB_Service_Consumer
+   *
+   * Initializes an OMB_Service_Consumer object representing the OMB service
+   * specified by $service_url. Performs a complete service discovery using
+   * Yadis.
+   * Throws OMB_UnsupportedServiceException if XRDS file does not specify a
+   * complete OMB service.
+   *
+   * @param string        $service_url  The URL of the service
+   * @param string        $consumer_url An URL representing the consumer
+   * @param OMB_Datastore $datastore    An instance of a class implementing
+   *                                    OMB_Datastore
+   *
+   * @access public
+   **/
+  public function __construct ($service_url, $consumer_url, $datastore) {
+    $this->url = $service_url;
+    $this->fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
+    $this->datastore = $datastore;
+    $this->oauth_consumer = new OAuthConsumer($consumer_url, '');
+
+    $xrds = OMB_Yadis_XRDS::fromYadisURL($service_url, $this->fetcher);
+
+    /* Detect our services. This performs a validation as well, since
+       getService und getXRD throw exceptions on failure. */
+    $this->services = array();
+
+    foreach (array(OAUTH_DISCOVERY => OMB_Helper::$OAUTH_SERVICES,
+                   OMB_VERSION     => OMB_Helper::$OMB_SERVICES)
+             as $service_root => $targetservices) {
+      $uris = $xrds->getService($service_root)->getURIs();
+      $xrd = $xrds->getXRD($uris[0]);
+      foreach ($targetservices as $targetservice) {
+        $yadis_service = $xrd->getService($targetservice);
+        if ($targetservice == OAUTH_ENDPOINT_REQUEST) {
+            $localid = $yadis_service->getElements('xrd:LocalID');
+            $this->listener_uri = $yadis_service->parser->content($localid[0]);
+        }
+        $uris = $yadis_service->getURIs();
+        $this->services[$targetservice] = $uris[0];
+      }
+    }
+  }
+
+  /**
+   * Get the handler URI for a service
+   *
+   * Returns the URI the remote web service has specified for the given
+   * service.
+   *
+   * @param string $service The URI identifying the service
+   *
+   * @access public
+   *
+   * @return string The service handler URI
+   **/
+  public function getServiceURI($service) {
+    return $this->services[$service];
+  }
+
+  /**
+   * Get the remote user’s URI
+   *
+   * Returns the URI of the remote user, i. e. the listener.
+   *
+   * @access public
+   *
+   * @return string The remote user’s URI
+   **/
+  public function getRemoteUserURI() {
+    return $this->listener_uri;
+  }
+
+  /**
+   * Get the listenee’s URI
+   *
+   * Returns the URI of the user being subscribed to, i. e. the local user.
+   *
+   * @access public
+   *
+   * @return string The local user’s URI
+   **/
+  public function getListeneeURI() {
+    return $this->listenee_uri;
+  }
+
+  /**
+   * Request a request token
+   *
+   * Performs a token request on the service. Returns an OAuthToken on success.
+   * Throws an exception if the request fails.
+   *
+   * @access public
+   *
+   * @return OAuthToken An unauthorized request token
+   **/
+  public function requestToken() {
+    /* Set the token to null just in case the user called setToken. */
+    $this->token = null;
+
+    $result = $this->performAction(OAUTH_ENDPOINT_REQUEST,
+                                  array('omb_listener' => $this->listener_uri));
+    if ($result->status != 200) {
+      throw OMB_RemoteServiceException::fromYadis(OAUTH_ENDPOINT_REQUEST,
+                                                  $result);
+    }
+    parse_str($result->body, $return);
+    if (!isset($return['oauth_token']) || !isset($return['oauth_token_secret'])) {
+      throw OMB_RemoteServiceException::fromYadis(OAUTH_ENDPOINT_REQUEST,
+                                                  $result);
+    }
+    $this->setToken($return['oauth_token'], $return['oauth_token_secret']);
+    return $this->token;
+  }
+
+  /**
+   *
+   * Request authorization
+   *
+   * Returns an URL which equals to an authorization request. The end user
+   * should be redirected to this location to perform authorization.
+   * The $finish_url should be a local resource which invokes
+   * OMB_Consumer::finishAuthorization on request.
+   *
+   * @param OMB_Profile $profile    An OMB_Profile object representing the
+   *                                soon-to-be subscribed (i. e. local) user
+   * @param string      $finish_url Target location after successful
+   *                                authorization
+   *
+   * @access public
+   *
+   * @return string An URL representing an authorization request
+   **/
+  public function requestAuthorization($profile, $finish_url) {
+    if ($this->performLegacyAuthRequest) {
+      $params = $profile->asParameters('omb_listenee', false);
+      $params['omb_listener'] = $this->listener_uri;
+      $params['oauth_callback'] = $finish_url;
+
+      $url = $this->prepareAction(OAUTH_ENDPOINT_AUTHORIZE, $params, 'GET')->to_url();
+    } else {
+
+      $params = array(
+                'oauth_callback' => $finish_url,
+                'oauth_token'    => $this->token->key,
+                'omb_version'    => OMB_VERSION,
+                'omb_listener'   => $this->listener_uri);
+
+      $params = array_merge($profile->asParameters('omb_listenee', false). $params);
+
+      /* Build result URL. */
+      $url = $this->services[OAUTH_ENDPOINT_AUTHORIZE];
+      $url .= (strrpos($url, '?') === false ? '?' : '&');
+      foreach ($params as $k => $v) {
+        $url .= OAuthUtil::urlencode_rfc3986($k) . '=' . OAuthUtil::urlencode_rfc3986($v) . '&';
+      }
+    }
+
+    $this->listenee_uri = $profile->getIdentifierURI();
+
+    return $url;
+  }
+
+  /**
+   * Finish authorization
+   *
+   * Finish the subscription process by converting the received and authorized
+   * request token into an access token. After that, the subscriber’s profile
+   * and the subscription are stored in the database.
+   * Expects an OAuthRequest in query parameters.
+   * Throws exceptions on failure.
+   *
+   * @access public
+   **/
+  public function finishAuthorization() {
+    OMB_Helper::removeMagicQuotesFromRequest();
+    $req = OAuthRequest::from_request();
+    if ($req->get_parameter('oauth_token') !=
+          $this->token->key) {
+      /* That’s not the token I wanted to get authorized. */
+      throw new OAuthException('The authorized token does not equal the ' .
+                               'submitted token.');
+    }
+
+    if ($req->get_parameter('omb_version') != OMB_VERSION) {
+      throw new OMB_RemoteServiceException('The remote service uses an ' .
+                                           'unsupported OMB version');
+    }
+
+    /* Construct the profile to validate it. */
+
+    /* Fix OMB bug. Listener URI is not passed. */
+    if ($_SERVER['REQUEST_METHOD'] == 'POST') {
+      $params = $_POST;
+    } else {
+      $params = $_GET;
+    }
+    $params['omb_listener'] = $this->listener_uri;
+
+    require_once 'profile.php';
+    $listener = OMB_Profile::fromParameters($params, 'omb_listener');
+
+    /* Ask the remote service to convert the authorized request token into an
+       access token. */
+
+    $result = $this->performAction(OAUTH_ENDPOINT_ACCESS, array());
+    if ($result->status != 200) {
+      throw new OAuthException('Could not get access token');
+    }
+
+    parse_str($result->body, $return);
+    if (!isset($return['oauth_token']) || !isset($return['oauth_token_secret'])) {
+      throw new OAuthException('Could not get access token');
+    }
+    $this->setToken($return['oauth_token'], $return['oauth_token_secret']);
+
+    /* Subscription is finished and valid. Now store the new subscriber and the
+       subscription in the database. */
+
+    $this->datastore->saveProfile($listener);
+    $this->datastore->saveSubscription($this->listener_uri,
+                                       $this->listenee_uri,
+                                       $this->token);
+  }
+
+  /**
+   * Return the URI identifying the listener
+   *
+   * Returns the URI for the OMB user who tries to subscribe or already has
+   * subscribed our user. This method is a workaround for a serious OMB flaw:
+   * The Listener URI is not passed in the finishauthorization call.
+   *
+   * @access public
+   *
+   * @return string the listener’s URI
+   **/
+  public function getListenerURI() {
+    return $this->listener_uri;
+  }
+
+  /**
+   * Inform the service about a profile update
+   *
+   * Sends an updated profile to the service.
+   *
+   * @param OMB_Profile $profile The profile that has changed
+   *
+   * @access public
+   **/
+  public function updateProfile($profile) {
+    $params = $profile->asParameters('omb_listenee', true);
+    $this->performOMBAction(OMB_ENDPOINT_UPDATEPROFILE, $params, $profile->getIdentifierURI());
+  }
+
+  /**
+   * Inform the service about a new notice
+   *
+   * Sends a notice to the service.
+   *
+   * @param OMB_Notice $notice The notice
+   *
+   * @access public
+   **/
+  public function postNotice($notice) {
+    $params = $notice->asParameters();
+    $params['omb_listenee'] = $notice->getAuthor()->getIdentifierURI();
+    $this->performOMBAction(OMB_ENDPOINT_POSTNOTICE, $params, $params['omb_listenee']);
+  }
+
+  /**
+   * Set the token member variable
+   *
+   * Initializes the token based on given token and secret token.
+   *
+   * @param string $token  The token
+   * @param string $secret The secret token
+   *
+   * @access public
+   **/
+  public function setToken($token, $secret) {
+    $this->token = new OAuthToken($token, $secret);
+  }
+
+  /**
+   * Prepare an OAuthRequest object
+   *
+   * Creates an OAuthRequest object mapping the request specified by the
+   * parameters.
+   *
+   * @param string $action_uri The URI specifying the target service
+   * @param array  $params     Additional parameters for the service call
+   * @param string $method     The HTTP method used to call the service
+   *                           ('POST' or 'GET', usually)
+   *
+   * @access protected
+   *
+   * @return OAuthRequest the prepared request
+   **/
+  protected function prepareAction($action_uri, $params, $method) {
+    $url = $this->services[$action_uri];
+
+    $url_params = array();
+    parse_str(parse_url($url, PHP_URL_QUERY), $url_params);
+
+    /* Add OMB version. */
+    $url_params['omb_version'] = OMB_VERSION;
+
+    /* Add user-defined parameters. */
+    $url_params = array_merge($url_params, $params);
+
+    $req = OAuthRequest::from_consumer_and_token($this->oauth_consumer,
+                                      $this->token, $method, $url, $url_params);
+
+    /* Sign the request. */
+    $req->sign_request(new OAuthSignatureMethod_HMAC_SHA1(),
+                                           $this->oauth_consumer, $this->token);
+
+    return $req;
+  }
+
+  /**
+   * Perform a service call
+   *
+   * Creates an OAuthRequest object and execute the mapped call as POST request.
+   *
+   * @param string $action_uri The URI specifying the target service
+   * @param array  $params     Additional parameters for the service call
+   *
+   * @access protected
+   *
+   * @return Auth_Yadis_HTTPResponse The POST request response
+   **/
+  protected function performAction($action_uri, $params) {
+    $req = $this->prepareAction($action_uri, $params, 'POST');
+
+    /* Return result page. */
+    return $this->fetcher->post($req->get_normalized_http_url(), $req->to_postdata(), array());
+  }
+
+  /**
+   * Perform an OMB action
+   *
+   * Executes an OMB action – to date, it’s one of updateProfile or postNotice.
+   *
+   * @param string $action_uri   The URI specifying the target service
+   * @param array  $params       Additional parameters for the service call
+   * @param string $listenee_uri The URI identifying the local user for whom
+   *                             the action is performed
+   *
+   * @access protected
+   **/
+  protected function performOMBAction($action_uri, $params, $listenee_uri) {
+    $result = $this->performAction($action_uri, $params);
+    if ($result->status == 403) {
+      /* The remote user unsubscribed us. */
+      $this->datastore->deleteSubscription($this->listener_uri, $listenee_uri);
+    } else if ($result->status != 200 ||
+               strpos($result->body, 'omb_version=' . OMB_VERSION) === false) {
+      /* The server signaled an error or sent an incorrect response. */
+      throw OMB_RemoteServiceException::fromYadis($action_uri, $result);
+    }
+  }
+}
diff --git a/extlib/libomb/service_provider.php b/extlib/libomb/service_provider.php
new file mode 100755 (executable)
index 0000000..7531527
--- /dev/null
@@ -0,0 +1,425 @@
+<?php
+
+require_once 'constants.php';
+require_once 'remoteserviceexception.php';
+require_once 'helper.php';
+
+/**
+ * OMB service realization
+ *
+ * This class realizes a complete, simple OMB service.
+ *
+ * PHP version 5
+ *
+ * LICENSE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package   OMB
+ * @author    Adrian Lang <mail@adrianlang.de>
+ * @copyright 2009 Adrian Lang
+ * @license   http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
+ **/
+
+class OMB_Service_Provider {
+  protected $user; /* An OMB_Profile representing the user */
+  protected $datastore; /* AN OMB_Datastore */
+
+  protected $remote_user; /* An OMB_Profile representing the remote user during
+                            the authorization process */
+
+  protected $oauth_server; /* An OAuthServer; should only be accessed via
+                              getOAuthServer. */
+
+  /**
+   * Initialize an OMB_Service_Provider object
+   *
+   * Constructs an OMB_Service_Provider instance that provides OMB services
+   * referring to a particular user.
+   *
+   * @param OMB_Profile   $user         An OMB_Profile; mandatory for XRDS
+   *                                    output, user auth handling and OMB
+   *                                    action performing
+   * @param OMB_Datastore $datastore    An OMB_Datastore; mandatory for
+   *                                    everything but XRDS output
+   * @param OAuthServer   $oauth_server An OAuthServer; used for token writing
+   *                                    and OMB action handling; will use
+   *                                    default value if not set
+   *
+   * @access public
+   **/
+  public function __construct ($user = null, $datastore = null, $oauth_server = null) {
+    $this->user = $user;
+    $this->datastore = $datastore;
+    $this->oauth_server = $oauth_server;
+  }
+
+  public function getRemoteUser() {
+    return $this->remote_user;
+  }
+
+  /**
+   * Write a XRDS document
+   *
+   * Writes a XRDS document specifying the OMB service. Optionally uses a
+   * given object of a class implementing OMB_XRDS_Writer for output. Else
+   * OMB_Plain_XRDS_Writer is used.
+   *
+   * @param OMB_XRDS_Mapper $xrds_mapper An object mapping actions to URLs
+   * @param OMB_XRDS_Writer $xrds_writer Optional; The OMB_XRDS_Writer used to
+   *                                     write the XRDS document
+   *
+   * @access public
+   *
+   * @return mixed Depends on the used OMB_XRDS_Writer; OMB_Plain_XRDS_Writer
+   *               returns nothing.
+   **/
+  public function writeXRDS($xrds_mapper, $xrds_writer = null) {
+    if ($xrds_writer == null) {
+        require_once 'plain_xrds_writer.php';
+        $xrds_writer = new OMB_Plain_XRDS_Writer();
+    }
+    return $xrds_writer->writeXRDS($this->user, $xrds_mapper);
+  }
+
+  /**
+   * Echo a request token
+   *
+   * Outputs an unauthorized request token for the query found in $_GET or
+   * $_POST.
+   *
+   * @access public
+   **/
+  public function writeRequestToken() {
+    OMB_Helper::removeMagicQuotesFromRequest();
+    echo $this->getOAuthServer()->fetch_request_token(OAuthRequest::from_request());
+  }
+
+  /**
+   * Handle an user authorization request.
+   *
+   * Parses an authorization request. This includes OAuth and OMB verification.
+   * Throws exceptions on failures. Returns an OMB_Profile object representing
+   * the remote user.
+   *
+   * The OMB_Profile passed to the constructor of OMB_Service_Provider should
+   * not represent the user specified in the authorization request, but the one
+   * currently logged in to the service. This condition being satisfied,
+   * handleUserAuth will check whether the listener specified in the request is
+   * identical to the logged in user.
+   *
+   * @access public
+   *
+   * @return OMB_Profile The profile of the soon-to-be subscribed, i. e. remote
+   *                     user
+   **/
+  public function handleUserAuth() {
+    OMB_Helper::removeMagicQuotesFromRequest();
+
+    /* Verify the request token. */
+
+    $this->token = $this->datastore->lookup_token(null, "request", $_GET['oauth_token']);
+    if (is_null($this->token)) {
+      throw new OAuthException('The given request token has not been issued ' .
+                               'by this service.');
+    }
+
+    /* Verify the OMB part. */
+
+    if ($_GET['omb_version'] !== OMB_VERSION) {
+      throw OMB_RemoteServiceException::forRequest(OAUTH_ENDPOINT_AUTHORIZE,
+                                   'Wrong OMB version ' . $_GET['omb_version']);
+    }
+
+    if ($_GET['omb_listener'] !== $this->user->getIdentifierURI()) {
+      throw OMB_RemoteServiceException::forRequest(OAUTH_ENDPOINT_AUTHORIZE,
+                                 'Wrong OMB listener ' . $_GET['omb_listener']);
+    }
+
+    foreach (array('omb_listenee', 'omb_listenee_profile',
+                   'omb_listenee_nickname', 'omb_listenee_license') as $param) {
+      if (!isset($_GET[$param]) || is_null($_GET[$param])) {
+        throw OMB_RemoteServiceException::forRequest(OAUTH_ENDPOINT_AUTHORIZE,
+                                       "Required parameter '$param' not found");
+      }
+    }
+
+    /* Store given callback for later use. */
+    if (isset($_GET['oauth_callback']) && $_GET['oauth_callback'] !== '') {
+      $this->callback = $_GET['oauth_callback'];
+      if (!OMB_Helper::validateURL($this->callback)) {
+        throw OMB_RemoteServiceException::forRequest(OAUTH_ENDPOINT_AUTHORIZE,
+                                              'Invalid callback URL specified');
+      }
+    }
+    $this->remote_user = OMB_Profile::fromParameters($_GET, 'omb_listenee');
+
+    return $this->remote_user;
+  }
+
+  /**
+   * Continue the OAuth dance after user authorization
+   *
+   * Performs the appropriate actions after user answered the authorization
+   * request.
+   *
+   * @param bool $accepted Whether the user granted authorization
+   *
+   * @access public
+   *
+   * @return array A two-component array with the values:
+   *                - callback The callback URL or null if none given
+   *                - token    The authorized request token or null if not
+   *                           authorized.
+   **/
+  public function continueUserAuth($accepted) {
+    $callback = $this->callback;
+    if (!$accepted) {
+      $this->datastore->revoke_token($this->token->key);
+      $this->token = null;
+      /* TODO: The handling is probably wrong in terms of OAuth 1.0 but the way
+               laconica works. Moreover I don’t know the right way either. */
+
+    } else {
+      $this->datastore->authorize_token($this->token->key);
+      $this->datastore->saveProfile($this->remote_user);
+      $this->datastore->saveSubscription($this->user->getIdentifierURI(),
+                          $this->remote_user->getIdentifierURI(), $this->token);
+
+      if (!is_null($this->callback)) {
+        /* Callback wants to get some informations as well. */
+        $params = $this->user->asParameters('omb_listener', false);
+
+        $params['oauth_token'] = $this->token->key;
+        $params['omb_version'] = OMB_VERSION;
+
+        $callback .= (parse_url($this->callback, PHP_URL_QUERY) ? '&' : '?');
+        foreach ($params as $k => $v) {
+          $callback .= OAuthUtil::urlencode_rfc3986($k) . '=' .
+                       OAuthUtil::urlencode_rfc3986($v) . '&';
+        }
+      }
+    }
+    return array($callback, $this->token);
+  }
+
+  /**
+   * Echo an access token
+   *
+   * Outputs an access token for the query found in $_POST. OMB 0.1 specifies
+   * that the access token request has to be a POST even if OAuth allows GET as
+   * well.
+   *
+   * @access public
+   **/
+  public function writeAccessToken() {
+    OMB_Helper::removeMagicQuotesFromRequest();
+    echo $this->getOAuthServer()->fetch_access_token(
+                                            OAuthRequest::from_request('POST'));
+  }
+
+  /**
+   * Handle an updateprofile request
+   *
+   * Handles an updateprofile request posted to this service. Updates the
+   * profile through the OMB_Datastore.
+   *
+   * @access public
+   *
+   * @return OMB_Profile The updated profile
+   **/
+  public function handleUpdateProfile() {
+    list($req, $profile) = $this->handleOMBRequest(OMB_ENDPOINT_UPDATEPROFILE);
+    $profile->updateFromParameters($req->get_parameters(), 'omb_listenee');
+    $this->datastore->saveProfile($profile);
+    $this->finishOMBRequest();
+    return $profile;
+  }
+
+  /**
+   * Handle a postnotice request
+   *
+   * Handles a postnotice request posted to this service. Saves the notice
+   * through the OMB_Datastore.
+   *
+   * @access public
+   *
+   * @return OMB_Notice The received notice
+   **/
+  public function handlePostNotice() {
+    list($req, $profile) = $this->handleOMBRequest(OMB_ENDPOINT_POSTNOTICE);
+    require_once 'notice.php';
+    $notice = OMB_Notice::fromParameters($profile, $req->get_parameters());
+    $this->datastore->saveNotice($notice);
+    $this->finishOMBRequest();
+    return $notice;
+  }
+
+  /**
+   * Handle an OMB request
+   *
+   * Performs common OMB request handling.
+   *
+   * @param string $uri The URI defining the OMB endpoint being served
+   *
+   * @access protected
+   *
+   * @return array(OAuthRequest, OMB_Profile)
+   **/
+  protected function handleOMBRequest($uri) {
+
+    OMB_Helper::removeMagicQuotesFromRequest();
+    $req = OAuthRequest::from_request('POST');
+    $listenee =  $req->get_parameter('omb_listenee');
+
+    try {
+        list($consumer, $token) = $this->getOAuthServer()->verify_request($req);
+    } catch (OAuthException $e) {
+      header('HTTP/1.1 403 Forbidden');
+      throw OMB_RemoteServiceException::forRequest($uri,
+                                   'Revoked accesstoken for ' . $listenee);
+    }
+
+    $version = $req->get_parameter('omb_version');
+    if ($version !== OMB_VERSION) {
+      header('HTTP/1.1 400 Bad Request');
+      throw OMB_RemoteServiceException::forRequest($uri,
+                                   'Wrong OMB version ' . $version);
+    }
+
+    $profile = $this->datastore->getProfile($listenee);
+    if (is_null($profile)) {
+      header('HTTP/1.1 400 Bad Request');
+      throw OMB_RemoteServiceException::forRequest($uri,
+                                   'Unknown remote profile ' . $listenee);
+    }
+
+    $subscribers = $this->datastore->getSubscriptions($listenee);
+    if (count($subscribers) === 0) {
+      header('HTTP/1.1 403 Forbidden');
+      throw OMB_RemoteServiceException::forRequest($uri,
+                                   'No subscriber for ' . $listenee);
+    }
+
+    return array($req, $profile);
+  }
+
+  /**
+   * Finishes an OMB request handling
+   *
+   * Performs common OMB request handling finishing.
+   *
+   * @access protected
+   **/
+  protected function finishOMBRequest() {
+    header('HTTP/1.1 200 OK');
+    header('Content-type: text/plain');
+    /* There should be no clutter but the version. */
+    echo "omb_version=" . OMB_VERSION;
+  }
+
+  /**
+   * Return an OAuthServer
+   *
+   * Checks whether the OAuthServer is null. If so, initializes it with a
+   * default value. Returns the OAuth server.
+   *
+   * @access protected
+   **/
+  protected function getOAuthServer() {
+    if (is_null($this->oauth_server)) {
+      $this->oauth_server = new OAuthServer($this->datastore);
+      $this->oauth_server->add_signature_method(
+                                          new OAuthSignatureMethod_HMAC_SHA1());
+    }
+    return $this->oauth_server;
+  }
+
+  /**
+   * Publish a notice
+   *
+   * Posts an OMB notice. This includes storing the notice and posting it to
+   * subscribed users.
+   *
+   * @param OMB_Notice $notice The new notice
+   *
+   * @access public
+   *
+   * @return array An array mapping subscriber URIs to the exception posting to
+   *               them has raised; Empty array if no exception occured
+   **/
+  public function postNotice($notice) {
+    $uri = $this->user->getIdentifierURI();
+
+    /* $notice is passed by reference and may change. */
+    $this->datastore->saveNotice($notice);
+    $subscribers = $this->datastore->getSubscriptions($uri);
+
+    /* No one to post to. */
+    if (is_null($subscribers)) {
+        return array();
+    }
+
+    require_once 'service_consumer.php';
+
+    $err = array();
+    foreach($subscribers as $subscriber) {
+      try {
+        $service = new OMB_Service_Consumer($subscriber['uri'], $uri, $this->datastore);
+        $service->setToken($subscriber['token'], $subscriber['secret']);
+        $service->postNotice($notice);
+      } catch (Exception $e) {
+        $err[$subscriber['uri']] = $e;
+        continue;
+      }
+    }
+    return $err;
+  }
+
+  /**
+   * Publish a profile update
+   *
+   * Posts the current profile as an OMB profile update. This includes updating
+   * the stored profile and posting it to subscribed users.
+   *
+   * @access public
+   *
+   * @return array An array mapping subscriber URIs to the exception posting to
+   *               them has raised; Empty array if no exception occured
+   **/
+  public function updateProfile() {
+    $uri = $this->user->getIdentifierURI();
+
+    $this->datastore->saveProfile($this->user);
+    $subscribers = $this->datastore->getSubscriptions($uri);
+
+    /* No one to post to. */
+    if (is_null($subscribers)) {
+        return array();
+    }
+
+    require_once 'service_consumer.php';
+
+    $err = array();
+    foreach($subscribers as $subscriber) {
+      try {
+        $service = new OMB_Service_Consumer($subscriber['uri'], $uri, $this->datastore);
+        $service->setToken($subscriber['token'], $subscriber['secret']);
+        $service->updateProfile($this->user);
+      } catch (Exception $e) {
+        $err[$subscriber['uri']] = $e;
+        continue;
+      }
+    }
+    return $err;
+  }
+}
diff --git a/extlib/libomb/unsupportedserviceexception.php b/extlib/libomb/unsupportedserviceexception.php
new file mode 100755 (executable)
index 0000000..4dab63e
--- /dev/null
@@ -0,0 +1,31 @@
+<?php
+/**
+ * Exception stating that a requested service is not available
+ *
+ * This exception is raised when OMB_Service is asked to call a service the remote
+ * server does not provide.
+ *
+ * PHP version 5
+ *
+ * LICENSE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package   OMB
+ * @author    Adrian Lang <mail@adrianlang.de>
+ * @copyright 2009 Adrian Lang
+ * @license   http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
+ **/
+class OMB_UnsupportedServiceException extends Exception {
+
+}
+?>
diff --git a/extlib/libomb/xrds_mapper.php b/extlib/libomb/xrds_mapper.php
new file mode 100755 (executable)
index 0000000..7552154
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+/**
+ * Map XRDS actions to URLs
+ *
+ * This interface specifies classes which write the XRDS file announcing
+ * the OMB server. An instance of an implementing class should be passed to
+ * OMB_Service_Provider->writeXRDS.
+ *
+ * PHP version 5
+ *
+ * LICENSE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package   OMB
+ * @author    Adrian Lang <mail@adrianlang.de>
+ * @copyright 2009 Adrian Lang
+ * @license   http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
+ **/
+
+interface OMB_XRDS_Mapper {
+  public function getURL($action);
+}
+?>
diff --git a/extlib/libomb/xrds_writer.php b/extlib/libomb/xrds_writer.php
new file mode 100755 (executable)
index 0000000..31b451b
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+/**
+ * Write OMB-specific XRDS
+ *
+ * This interface specifies classes which write the XRDS file announcing
+ * the OMB server. An instance of an implementing class should be passed to
+ * OMB_Service_Provider->writeXRDS.
+ *
+ * PHP version 5
+ *
+ * LICENSE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package   OMB
+ * @author    Adrian Lang <mail@adrianlang.de>
+ * @copyright 2009 Adrian Lang
+ * @license   http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
+ **/
+
+interface OMB_XRDS_Writer {
+  public function writeXRDS($user, $mapper);
+}
+?>
index 362ab3cd37e1bef5cd189703f949063c99c6309c..51e30f57821ffd5c1053174759e6944ec63248f3 100644 (file)
--- a/index.php
+++ b/index.php
  *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category StatusNet
+ * @package  StatusNet
+ * @author   Brenda Wallace <shiny@cpan.org>
+ * @author   Christopher Vollick <psycotica0@gmail.com>
+ * @author   CiaranG <ciaran@ciarang.com>
+ * @author   Craig Andrews <candrews@integralblue.com>
+ * @author   Evan Prodromou <evan@controlezvous.ca>
+ * @author   Gina Haeussge <osd@foosel.net>
+ * @author   Jeffery To <jeffery.to@gmail.com>
+ * @author   Mike Cochrane <mikec@mikenz.geek.nz>
+ * @author   Robin Millette <millette@controlyourself.ca>
+ * @author   Sarven Capadisli <csarven@controlyourself.ca>
+ * @author   Tom Adams <tom@holizz.com>
+ * 
+ * @license  GNU Affero General Public License http://www.gnu.org/licenses/
  */
 
 define('INSTALLDIR', dirname(__FILE__));
@@ -29,7 +45,8 @@ $action = null;
 function getPath($req)
 {
     if ((common_config('site', 'fancy') || !array_key_exists('PATH_INFO', $_SERVER))
-        && array_key_exists('p', $req)) {
+        && array_key_exists('p', $req)
+    ) {
         return $req['p'];
     } else if (array_key_exists('PATH_INFO', $_SERVER)) {
         $path = $_SERVER['PATH_INFO'];
@@ -51,28 +68,35 @@ function handleError($error)
     }
 
     $logmsg = "PEAR error: " . $error->getMessage();
-    if(common_config('site', 'logdebug')) {
+    if (common_config('site', 'logdebug')) {
         $logmsg .= " : ". $error->getDebugInfo();
     }
     common_log(LOG_ERR, $logmsg);
-    if(common_config('site', 'logdebug')) {
+    if (common_config('site', 'logdebug')) {
         $bt = $error->getBacktrace();
         foreach ($bt as $line) {
             common_log(LOG_ERR, $line);
         }
     }
-    if ($error instanceof DB_DataObject_Error ||
-        $error instanceof DB_Error) {
-        $msg = sprintf(_('The database for %s isn\'t responding correctly, '.
-                         'so the site won\'t work properly. '.
-                         'The site admins probably know about the problem, '.
-                         'but you can contact them at %s to make sure. '.
-                         'Otherwise, wait a few minutes and try again.'),
-                       common_config('site', 'name'),
-                       common_config('site', 'email'));
+    if ($error instanceof DB_DataObject_Error
+        || $error instanceof DB_Error
+    ) {
+        $msg = sprintf(
+            _(
+                'The database for %s isn\'t responding correctly, '.
+                'so the site won\'t work properly. '.
+                'The site admins probably know about the problem, '.
+                'but you can contact them at %s to make sure. '.
+                'Otherwise, wait a few minutes and try again.'
+            ),
+            common_config('site', 'name'),
+            common_config('site', 'email')
+        );
     } else {
-        $msg = _('An important error occured, probably related to email setup. '.
-                 'Check logfiles for more info..');
+        $msg = _(
+            'An important error occured, probably related to email setup. '.
+            'Check logfiles for more info..'
+        );
     }
 
     $dac = new DBErrorAction($msg, 500);
@@ -112,6 +136,19 @@ function checkMirror($action_obj, $args)
     }
 }
 
+function isLoginAction($action)
+{
+    static $loginActions =  array('login', 'recoverpassword', 'api', 'doc', 'register');
+
+    $login = null;
+
+    if (Event::handle('LoginAction', array($action, &$login))) {
+        $login = in_array($action, $loginActions);
+    }
+
+    return $login;
+}
+
 function main()
 {
     // fake HTTP redirects using lighttpd's 404 redirects
@@ -120,10 +157,11 @@ function main()
         $_lighty_url = @parse_url($_lighty_url);
 
         if ($_lighty_url['path'] != '/index.php' && $_lighty_url['path'] != '/') {
-            $_lighty_path = preg_replace('/^'.preg_quote(common_config('site','path')).'\//', '', substr($_lighty_url['path'], 1));
+            $_lighty_path = preg_replace('/^'.preg_quote(common_config('site', 'path')).'\//', '', substr($_lighty_url['path'], 1));
             $_SERVER['QUERY_STRING'] = 'p='.$_lighty_path;
-            if ($_lighty_url['query'])
+            if ($_lighty_url['query']) {
                 $_SERVER['QUERY_STRING'] .= '&'.$_lighty_url['query'];
+            }
             parse_str($_lighty_url['query'], $_lighty_query);
             foreach ($_lighty_query as $key => $val) {
                 $_GET[$key] = $_REQUEST[$key] = $val;
@@ -134,7 +172,7 @@ function main()
     $_SERVER['REDIRECT_URL'] = preg_replace("/\?.+$/", "", $_SERVER['REQUEST_URI']);
 
     // quick check for fancy URL auto-detection support in installer.
-    if (isset($_SERVER['REDIRECT_URL']) && (preg_replace("/^\/$/","",(dirname($_SERVER['REQUEST_URI']))) . '/check-fancy') === $_SERVER['REDIRECT_URL']) {
+    if (isset($_SERVER['REDIRECT_URL']) && (preg_replace("/^\/$/", "", (dirname($_SERVER['REQUEST_URI']))) . '/check-fancy') === $_SERVER['REDIRECT_URL']) {
         die("Fancy URL support detection succeeded. We suggest you enable this to get fancy (pretty) URLs.");
     }
     global $user, $action;
@@ -142,8 +180,12 @@ function main()
     Snapshot::check();
 
     if (!_have_config()) {
-        $msg = sprintf(_("No configuration file found. Try running ".
-                         "the installation program first."));
+        $msg = sprintf(
+            _(
+                "No configuration file found. Try running ".
+                "the installation program first."
+            )
+        );
         $sac = new ServerErrorAction($msg);
         $sac->showPage();
         return;
@@ -189,36 +231,12 @@ function main()
     // If the site is private, and they're not on one of the "public"
     // parts of the site, redirect to login
 
-    if (!$user && common_config('site', 'private')) {
-        $public_actions = array('openidlogin', 'finishopenidlogin',
-                                'recoverpassword', 'api', 'doc',
-                                'opensearch');
-        $login_action = 'openidlogin';
-        if (!common_config('site', 'openidonly')) {
-            $public_actions[] = 'login';
-            $public_actions[] = 'register';
-            $login_action = 'login';
-        }
-        if (!in_array($action, $public_actions) &&
-            !preg_match('/rss$/', $action)) {
-
-            // set returnto
-            $rargs =& common_copy_args($args);
-            unset($rargs['action']);
-            if (common_config('site', 'fancy')) {
-                unset($rargs['p']);
-            }
-            if (array_key_exists('submit', $rargs)) {
-                unset($rargs['submit']);
-            }
-            foreach (array_keys($_COOKIE) as $cookie) {
-                unset($rargs[$cookie]);
-            }
-            common_set_returnto(common_local_url($action, $rargs));
-
-            common_redirect(common_local_url($login_action));
-            return;
-        }
+    if (!$user && common_config('site', 'private')
+        && !isLoginAction($action)
+        && !preg_match('/rss$/', $action)
+    ) {
+        common_redirect(common_local_url('login'));
+        return;
     }
 
     $action_class = ucfirst($action).'Action';
index 30dd34496b6e9d0c5068c47f9be7825af5992701..c2a5bb29ee394f392abe062ebf355f6d64b5fecc 100644 (file)
  *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category Installation
+ * @package  Installation
+ *
+ * @author   Adrian Lang <mail@adrianlang.de>
+ * @author   Brenda Wallace <shiny@cpan.org>
+ * @author   Brett Taylor <brett@webfroot.co.nz>
+ * @author   Brion Vibber <brion@pobox.com>
+ * @author   CiaranG <ciaran@ciarang.com>
+ * @author   Craig Andrews <candrews@integralblue.com>
+ * @author   Eric Helgeson <helfire@Erics-MBP.local>
+ * @author   Evan Prodromou <evan@status.net>
+ * @author   Robin Millette <millette@controlyourself.ca>
+ * @author   Sarven Capadisli <csarven@status.net>
+ * @author   Tom Adams <tom@holizz.com>
+ * @license  GNU Affero General Public License http://www.gnu.org/licenses/
+ * @version  0.9
+ * @link     http://status.net
  */
 
 define('INSTALLDIR', dirname(__FILE__));
@@ -181,15 +199,32 @@ $external_libraries=array(
         'check_class'=>'Validate'
     )
 );
+$dbModules = array(
+    'mysql' => array(
+        'name' => 'MySQL',
+        'check_module' => 'mysql', // mysqli?
+        'installer' => 'mysql_db_installer',
+    ),
+    'pgsql' => array(
+        'name' => 'PostgreSQL',
+        'check_module' => 'pgsql',
+        'installer' => 'pgsql_db_installer',
+    ),
+);
 
+/**
+ * the actual installation.
+ * If call libraries are present, then install
+ *
+ * @return void
+ */
 function main()
 {
-    if (!checkPrereqs())
-    {
+    if (!checkPrereqs()) {
         return;
     }
-    
-    if (isset($_GET['checklibs'])) {
+
+    if (!empty($_GET['checklibs'])) {
         showLibs();
     } else {
         if ($_SERVER['REQUEST_METHOD'] == 'POST') {
@@ -200,15 +235,22 @@ function main()
     }
 }
 
+/**
+ * checks if an external libary is present
+ *
+ * @param string $external_library Name of library
+ *
+ * @return boolean indicates if library present
+ */
 function haveExternalLibrary($external_library)
 {
-    if(isset($external_library['include']) && ! haveIncludeFile($external_library['include'])){
+    if (isset($external_library['include']) && !haveIncludeFile($external_library['include'])) {
         return false;
     }
-    if(isset($external_library['check_function']) && ! function_exists($external_library['check_function'])){
+    if (isset($external_library['check_function']) && ! function_exists($external_library['check_function'])) {
         return false;
     }
-    if(isset($external_library['check_class']) && ! class_exists($external_library['check_class'])){
+    if (isset($external_library['check_class']) && ! class_exists($external_library['check_class'])) {
         return false;
     }
     return true;
@@ -223,19 +265,23 @@ function haveIncludeFile($filename) {
     return $ok;
 }
 
+/**
+ * Check if all is ready for installation
+ *
+ * @return void
+ */
 function checkPrereqs()
 {
-       $pass = true;
+    $pass = true;
 
     if (file_exists(INSTALLDIR.'/config.php')) {
-         ?><p class="error">Config file &quot;config.php&quot; already exists.</p>
-         <?php
+         printf('<p class="error">Config file &quot;config.php&quot; already exists.</p>');
         $pass = false;
     }
 
     if (version_compare(PHP_VERSION, '5.2.3', '<')) {
-            ?><p class="error">Require PHP version 5.2.3 or greater.</p><?php
-                   $pass = false;
+        printf('<p class="error">Require PHP version 5.2.3 or greater.</p>');
+        $pass = false;
     }
 
     $reqs = array('gd', 'curl',
@@ -243,37 +289,52 @@ function checkPrereqs()
 
     foreach ($reqs as $req) {
         if (!checkExtension($req)) {
-            ?><p class="error">Cannot load required extension: <code><?php echo $req; ?></code></p><?php
-                   $pass = false;
+            printf('<p class="error">Cannot load required extension: <code>%s</code></p>', $req);
+            $pass = false;
+        }
+    }
+    // Make sure we have at least one database module available
+    global $dbModules;
+    $missingExtensions = array();
+    foreach ($dbModules as $type => $info) {
+        if (!checkExtension($info['check_module'])) {
+            $missingExtensions[] = $info['check_module'];
+        }
+    }
+
+    if (count($missingExtensions) == count($dbModules)) {
+        $req = implode(', ', $missingExtensions);
+        printf('<p class="error">Cannot find mysql or pgsql extension. You need one or the other.');
+        $pass = false;
+    }
+
+    if (!is_writable(INSTALLDIR)) {
+        printf('<p class="error">Cannot write config file to: <code>%s</code></p>', INSTALLDIR);
+        printf('<p>On your server, try this command: <code>chmod a+w %s</code>', INSTALLDIR);
+        $pass = false;
+    }
+
+    // Check the subdirs used for file uploads
+    $fileSubdirs = array('avatar', 'background', 'file');
+    foreach ($fileSubdirs as $fileSubdir) {
+        $fileFullPath = INSTALLDIR."/$fileSubdir/";
+        if (!is_writable($fileFullPath)) {
+            printf('<p class="error">Cannot write to %s directory: <code>%s</code></p>', $fileSubdir, $fileFullPath);
+            printf('<p>On your server, try this command: <code>chmod a+w %s</code></p>', $fileFullPath);
+            $pass = false;
         }
     }
-    if (!checkExtension('pgsql') && !checkExtension('mysql')) {
-      ?><p class="error">Cannot find mysql or pgsql extension. You need one or the other: <code><?php echo $req; ?></code></p><?php
-                    $pass = false;
-    }
-
-       if (!is_writable(INSTALLDIR)) {
-         ?><p class="error">Cannot write config file to: <code><?php echo INSTALLDIR; ?></code></p>
-              <p>On your server, try this command: <code>chmod a+w <?php echo INSTALLDIR; ?></code>
-         <?php
-            $pass = false;
-       }
-
-       // Check the subdirs used for file uploads
-       $fileSubdirs = array('avatar', 'background', 'file');
-       foreach ($fileSubdirs as $fileSubdir) {
-               $fileFullPath = INSTALLDIR."/$fileSubdir/";
-               if (!is_writable($fileFullPath)) {
-            ?><p class="error">Cannot write <?php echo $fileSubdir; ?> directory: <code><?php echo $fileFullPath; ?></code></p>
-                      <p>On your server, try this command: <code>chmod a+w <?php echo $fileFullPath; ?></code></p>
-            <?php
-                    $pass = false;
-               }
-       }
-
-       return $pass;
+
+    return $pass;
 }
 
+/**
+ * Checks if a php extension is both installed and loaded
+ *
+ * @param string $name of extension to check
+ *
+ * @return boolean whether extension is installed and loaded
+ */
 function checkExtension($name)
 {
     if (extension_loaded($name)) {
@@ -291,15 +352,20 @@ function checkExtension($name)
     }
 }
 
+/**
+ * Show list of libraries
+ *
+ * @return void
+ */
 function showLibs()
 {
     global $external_libraries;
     $present_libraries=array();
     $absent_libraries=array();
-    foreach($external_libraries as $external_library){
-        if(haveExternalLibrary($external_library)){
+    foreach ($external_libraries as $external_library) {
+        if (haveExternalLibrary($external_library)) {
             $present_libraries[]=$external_library;
-        }else{
+        } else {
             $absent_libraries[]=$external_library;
         }
     }
@@ -314,22 +380,21 @@ function showLibs()
     <h2>Absent Libraries</h2>
     <ul id="absent_libraries">
 E_O_T;
-    foreach($absent_libraries as $library)
-    {
+    foreach ($absent_libraries as $library) {
         echo '<li>';
-        if(isset($library['url'])){
+        if (isset($library['url'])) {
             echo '<a href=">'.$library['url'].'">'.htmlentities($library['name']).'</a>';
-        }else{
+        } else {
             echo htmlentities($library['name']);
         }
         echo '<ul>';
-        if(isset($library['deb'])){
+        if (isset($library['deb'])) {
             echo '<li class="deb package">deb: <a href="apt:' . urlencode($library['deb']) . '">' . htmlentities($library['deb']) . '</a></li>';
         }
-        if(isset($library['rpm'])){
+        if (isset($library['rpm'])) {
             echo '<li class="rpm package">rpm: ' . htmlentities($library['rpm']) . '</li>';
         }
-        if(isset($library['pear'])){
+        if (isset($library['pear'])) {
             echo '<li class="pear package">pear: ' . htmlentities($library['pear']) . '</li>';
         }
         echo '</ul>';
@@ -339,12 +404,11 @@ E_O_T;
     <h2>Installed Libraries</h2>
     <ul id="present_libraries">
 E_O_T;
-    foreach($present_libraries as $library)
-    {
+    foreach ($present_libraries as $library) {
         echo '<li>';
-        if(isset($library['url'])){
+        if (isset($library['url'])) {
             echo '<a href=">'.$library['url'].'">'.htmlentities($library['name']).'</a>';
-        }else{
+        } else {
             echo htmlentities($library['name']);
         }
         echo '</li>';
@@ -356,6 +420,15 @@ E_O_T;
 
 function showForm()
 {
+    global $dbModules;
+    $dbRadios = '';
+    $checked = 'checked="checked" '; // Check the first one which exists
+    foreach ($dbModules as $type => $info) {
+        if (checkExtension($info['check_module'])) {
+            $dbRadios .= "<input type=\"radio\" name=\"dbtype\" id=\"dbtype-$type\" value=\"$type\" $checked/> $info[name]<br />\n";
+            $checked = '';
+        }
+    }
     echo<<<E_O_T
         </ul>
     </dd>
@@ -392,8 +465,7 @@ function showForm()
             <li>
 
                 <label for="dbtype">Type</label>
-                <input type="radio" name="dbtype" id="fancy-mysql" value="mysql" checked='checked' /> MySQL<br />
-                <input type="radio" name="dbtype" id="dbtype-pgsql" value="pgsql" /> PostgreSQL<br />
+                $dbRadios
                 <p class="form_guide">Database type</p>
             </li>
 
@@ -422,17 +494,11 @@ E_O_T;
 
 function updateStatus($status, $error=false)
 {
-?>
-                <li <?php echo ($error) ? 'class="error"': ''; ?>><?php echo $status;?></li>
-
-<?php
+    echo '<li' . ($error ? ' class="error"': '' ) . ">$status</li>";
 }
 
 function handlePost()
 {
-?>
-
-<?php
     $host     = $_POST['host'];
     $dbtype   = $_POST['dbtype'];
     $database = $_POST['database'];
@@ -443,55 +509,41 @@ function handlePost()
     $server = $_SERVER['HTTP_HOST'];
     $path = substr(dirname($_SERVER['PHP_SELF']), 1);
 
-?>
+    echo <<<STR
     <dl class="system_notice">
         <dt>Page notice</dt>
         <dd>
             <ul>
-<?php
-       $fail = false;
+STR;
+    $fail = false;
 
     if (empty($host)) {
         updateStatus("No hostname specified.", true);
-               $fail = true;
+        $fail = true;
     }
 
     if (empty($database)) {
         updateStatus("No database specified.", true);
-               $fail = true;
+        $fail = true;
     }
 
     if (empty($username)) {
         updateStatus("No username specified.", true);
-               $fail = true;
+        $fail = true;
     }
 
-//     if (empty($password)) {
-//         updateStatus("No password specified.", true);
-//             $fail = true;
-//     }
-
     if (empty($sitename)) {
         updateStatus("No sitename specified.", true);
-               $fail = true;
+        $fail = true;
     }
 
-    if($fail){
-            showForm();
+    if ($fail) {
+        showForm();
         return;
     }
 
-    // FIXME: use PEAR::DB or PDO instead of our own switch
-
-    switch($dbtype) {
-        case 'mysql':
-            $db = mysql_db_installer($host, $database, $username, $password);
-            break;
-        case 'pgsql':
-            $db = pgsql_db_installer($host, $database, $username, $password);
-            break;
-        default:
-    }
+    global $dbModules;
+    $db = call_user_func($dbModules[$dbtype]['installer'], $host, $database, $username, $password);
 
     if (!$db) {
         // database connection failed, do not move on to create config file.
@@ -514,112 +566,110 @@ function handlePost()
 
     updateStatus("StatusNet has been installed at $link");
     updateStatus("You can visit your <a href='$link'>new StatusNet site</a>.");
-?>
-
-<?php
 }
 
-function pgsql_db_installer($host, $database, $username, $password) {
-  $connstring = "dbname=$database host=$host user=$username";
-
-  //No password would mean trust authentication used.
-  if (!empty($password)) {
-    $connstring .= " password=$password";
-  }
-  updateStatus("Starting installation...");
-  updateStatus("Checking database...");
-  $conn = pg_connect($connstring);
-
-  if ($conn ===false) {
-    updateStatus("Failed to connect to database: $connstring");
-    showForm();
-    return false;
-  }
-
-  //ensure database encoding is UTF8
-  $record = pg_fetch_object(pg_query($conn, 'SHOW server_encoding'));
-  if ($record->server_encoding != 'UTF8') {
-    updateStatus("StatusNet requires UTF8 character encoding. Your database is ". htmlentities($record->server_encoding));
-    showForm();
-    return false;
-  }
-
-  updateStatus("Running database script...");
-  //wrap in transaction;
-  pg_query($conn, 'BEGIN');
-  $res = runDbScript(INSTALLDIR.'/db/statusnet_pg.sql', $conn, 'pgsql');
-
-  if ($res === false) {
-      updateStatus("Can't run database script.", true);
-      showForm();
-      return false;
-  }
-  foreach (array('sms_carrier' => 'SMS carrier',
+function Pgsql_Db_installer($host, $database, $username, $password)
+{
+    $connstring = "dbname=$database host=$host user=$username";
+
+    //No password would mean trust authentication used.
+    if (!empty($password)) {
+        $connstring .= " password=$password";
+    }
+    updateStatus("Starting installation...");
+    updateStatus("Checking database...");
+    $conn = pg_connect($connstring);
+
+    if ($conn ===false) {
+        updateStatus("Failed to connect to database: $connstring");
+        showForm();
+        return false;
+    }
+
+    //ensure database encoding is UTF8
+    $record = pg_fetch_object(pg_query($conn, 'SHOW server_encoding'));
+    if ($record->server_encoding != 'UTF8') {
+        updateStatus("StatusNet requires UTF8 character encoding. Your database is ". htmlentities($record->server_encoding));
+        showForm();
+        return false;
+    }
+
+    updateStatus("Running database script...");
+    //wrap in transaction;
+    pg_query($conn, 'BEGIN');
+    $res = runDbScript(INSTALLDIR.'/db/statusnet_pg.sql', $conn, 'pgsql');
+
+    if ($res === false) {
+        updateStatus("Can't run database script.", true);
+        showForm();
+        return false;
+    }
+    foreach (array('sms_carrier' => 'SMS carrier',
                 'notice_source' => 'notice source',
                 'foreign_services' => 'foreign service')
           as $scr => $name) {
-      updateStatus(sprintf("Adding %s data to database...", $name));
-      $res = runDbScript(INSTALLDIR.'/db/'.$scr.'.sql', $conn, 'pgsql');
-      if ($res === false) {
-          updateStatus(sprintf("Can't run %d script.", $name), true);
-          showForm();
-          return false;
-      }
-  }
-  pg_query($conn, 'COMMIT');
-
-  if (empty($password)) {
-    $sqlUrl = "pgsql://$username@$host/$database";
-  }
-  else {
-    $sqlUrl = "pgsql://$username:$password@$host/$database";
-  }
-
-  $db = array('type' => 'pgsql', 'database' => $sqlUrl);
-
-  return $db;
+        updateStatus(sprintf("Adding %s data to database...", $name));
+        $res = runDbScript(INSTALLDIR.'/db/'.$scr.'.sql', $conn, 'pgsql');
+        if ($res === false) {
+            updateStatus(sprintf("Can't run %d script.", $name), true);
+            showForm();
+            return false;
+        }
+    }
+    pg_query($conn, 'COMMIT');
+
+    if (empty($password)) {
+        $sqlUrl = "pgsql://$username@$host/$database";
+    } else {
+        $sqlUrl = "pgsql://$username:$password@$host/$database";
+    }
+
+    $db = array('type' => 'pgsql', 'database' => $sqlUrl);
+
+    return $db;
 }
 
-function mysql_db_installer($host, $database, $username, $password) {
-  updateStatus("Starting installation...");
-  updateStatus("Checking database...");
-
-  $conn = mysql_connect($host, $username, $password);
-  if (!$conn) {
-      updateStatus("Can't connect to server '$host' as '$username'.", true);
-      showForm();
-      return false;
-  }
-  updateStatus("Changing to database...");
-  $res = mysql_select_db($database, $conn);
-  if (!$res) {
-      updateStatus("Can't change to database.", true);
-      showForm();
-      return false;
-  }
-  updateStatus("Running database script...");
-  $res = runDbScript(INSTALLDIR.'/db/statusnet.sql', $conn);
-  if ($res === false) {
-      updateStatus("Can't run database script.", true);
-      showForm();
-      return false;
-  }
-  foreach (array('sms_carrier' => 'SMS carrier',
+function Mysql_Db_installer($host, $database, $username, $password)
+{
+    updateStatus("Starting installation...");
+    updateStatus("Checking database...");
+
+    $conn = mysql_connect($host, $username, $password);
+    if (!$conn) {
+        updateStatus("Can't connect to server '$host' as '$username'.", true);
+        showForm();
+        return false;
+    }
+    updateStatus("Changing to database...");
+    $res = mysql_select_db($database, $conn);
+    if (!$res) {
+        updateStatus("Can't change to database.", true);
+        showForm();
+        return false;
+    }
+    updateStatus("Running database script...");
+    $res = runDbScript(INSTALLDIR.'/db/statusnet.sql', $conn);
+    if ($res === false) {
+        updateStatus("Can't run database script.", true);
+        showForm();
+        return false;
+    }
+    foreach (array('sms_carrier' => 'SMS carrier',
                 'notice_source' => 'notice source',
                 'foreign_services' => 'foreign service')
           as $scr => $name) {
-      updateStatus(sprintf("Adding %s data to database...", $name));
-      $res = runDbScript(INSTALLDIR.'/db/'.$scr.'.sql', $conn);
-      if ($res === false) {
-          updateStatus(sprintf("Can't run %d script.", $name), true);
-          showForm();
-          return false;
-      }
-  }
-
-      $sqlUrl = "mysqli://$username:$password@$host/$database";
-      $db = array('type' => 'mysql', 'database' => $sqlUrl);
-      return $db;
+        updateStatus(sprintf("Adding %s data to database...", $name));
+        $res = runDbScript(INSTALLDIR.'/db/'.$scr.'.sql', $conn);
+        if ($res === false) {
+            updateStatus(sprintf("Can't run %d script.", $name), true);
+            showForm();
+            return false;
+        }
+    }
+
+    $sqlUrl = "mysqli://$username:$password@$host/$database";
+    $db = array('type' => 'mysql', 'database' => $sqlUrl);
+    return $db;
 }
 
 function writeConf($sitename, $server, $path, $fancy, $db)
@@ -650,7 +700,16 @@ function writeConf($sitename, $server, $path, $fancy, $db)
     return $res;
 }
 
-function runDbScript($filename, $conn, $type = 'mysql')
+/**
+ * Install schema into the database
+ *
+ * @param string $filename location of database schema file
+ * @param dbconn $conn     connection to database
+ * @param string $type     type of database, currently mysql or pgsql
+ *
+ * @return boolean - indicating success or failure
+ */
+function runDbScript($filename, $conn, $type = 'mysqli')
 {
     $sql = trim(file_get_contents($filename));
     $stmts = explode(';', $sql);
@@ -661,7 +720,7 @@ function runDbScript($filename, $conn, $type = 'mysql')
         }
         // FIXME: use PEAR::DB or PDO instead of our own switch
         switch ($type) {
-        case 'mysql':
+        case 'mysqli':
             $res = mysql_query($stmt, $conn);
             if ($res === false) {
                 $error = mysql_error();
@@ -686,7 +745,9 @@ function runDbScript($filename, $conn, $type = 'mysql')
 
 ?>
 <?php echo"<?"; ?> xml version="1.0" encoding="UTF-8" <?php echo "?>"; ?>
-<!DOCTYPE html>
+<!DOCTYPE html
+PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en_US" lang="en_US">
     <head>
         <title>Install StatusNet</title>
index 2165957c3b925a929e121826811c8a541ea9e0dd..0a943512f2a33c4bd7c551a43c6f71d07e47c278 100644 (file)
@@ -21,7 +21,9 @@ $(document).ready(function(){
        
        // count character on keyup
        function counter(event){
-               var maxLength = 140;
+         if (maxLength <= 0) {
+              return;
+         }
                var currentLength = $("#notice_data-text").val().length;
                var remaining = maxLength - currentLength;
                var counter = $("#notice_text-count");
@@ -67,12 +69,20 @@ $(document).ready(function(){
                return true;
        }
 
+     // define maxLength if it wasn't defined already
+
+    if (typeof(maxLength) == "undefined") {
+         maxLength = 140;
+    }
+
        if ($("#notice_data-text").length) {
-               $("#notice_data-text").bind("keyup", counter);
-               $("#notice_data-text").bind("keydown", submitonreturn);
+         if (maxLength > 0) {
+              $("#notice_data-text").bind("keyup", counter);
+              // run once in case there's something in there
+              counter();
+         }
 
-               // run once in case there's something in there
-               counter();
+               $("#notice_data-text").bind("keydown", submitonreturn);
 
         if($('body')[0].id != 'conversation') {
             $("#notice_data-text").focus();
@@ -218,7 +228,9 @@ $(document).ready(function(){
                                                                                                                                                                }
                                                                                                                                                                else {
                                                                                                                                                                        $("#notice_data-text").val("");
-                                                                                                                                                                       counter();
+                                                                                     if (maxLength > 0) {
+                                                                                          counter();
+                                                                                     }
                                                                                                                                                                }
                                                                                                                                                        }
                                                                                                                                                }
@@ -258,7 +270,9 @@ $(document).ready(function(){
                                                                                                $("#notice_data-attach").val("");
                                                                                                $("#notice_in-reply-to").val("");
                                                     $('#notice_data-attach_selected').remove();
-                                                    counter();
+                                                     if (maxLength > 0) {
+                                                          counter();
+                                                     }
                                                                                                }
                                                                                                $("#form_notice").removeClass("processing");
                                                                                                $("#notice_action-submit").removeAttr("disabled");
index 6402dbc099615918d7ee777330b61abfe3c8873b..18ae7719b25c9df14146079ece46afade3fd362f 100644 (file)
@@ -19,7 +19,7 @@
 
 if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
 
-class ShortUrlApi
+abstract class ShortUrlApi
 {
     protected $service_url;
     protected $long_limit = 27;
@@ -35,11 +35,9 @@ class ShortUrlApi
         return $url;
     }
 
-    protected function shorten_imp($url) {
-        return "To Override";
-    }
+    protected  abstract function shorten_imp($url);
 
-    private function is_long($url) {
+    protected function is_long($url) {
         return strlen($url) >= common_config('site', 'shorturllength');
     }
 
@@ -71,61 +69,3 @@ class ShortUrlApi
     }
 }
 
-class LilUrl extends ShortUrlApi
-{
-    function __construct()
-    {
-        parent::__construct('http://ur1.ca/');
-    }
-
-    protected function shorten_imp($url) {
-        $data['longurl'] = $url;
-        $response = $this->http_post($data);
-        if (!$response) return $url;
-        $y = @simplexml_load_string($response);
-        if (!isset($y->body)) return $url;
-        $x = $y->body->p[0]->a->attributes();
-        if (isset($x['href'])) return $x['href'];
-        return $url;
-    }
-}
-
-
-class PtitUrl extends ShortUrlApi
-{
-    function __construct()
-    {
-        parent::__construct('http://ptiturl.com/?creer=oui&action=Reduire&url=');
-    }
-
-    protected function shorten_imp($url) {
-        $response = $this->http_get($url);
-        if (!$response) return $url;
-        $response = $this->tidy($response);
-        $y = @simplexml_load_string($response);
-        if (!isset($y->body)) return $url;
-        $xml = $y->body->center->table->tr->td->pre->a->attributes();
-        if (isset($xml['href'])) return $xml['href'];
-        return $url;
-    }
-}
-
-class TightUrl extends ShortUrlApi
-{
-    function __construct()
-    {
-        parent::__construct('http://2tu.us/?save=y&url=');
-    }
-
-    protected function shorten_imp($url) {
-        $response = $this->http_get($url);
-        if (!$response) return $url;
-        $response = $this->tidy($response);
-        $y = @simplexml_load_string($response);
-        if (!isset($y->body)) return $url;
-        $xml = $y->body->p[0]->code[0]->a->attributes();
-        if (isset($xml['href'])) return $xml['href'];
-        return $url;
-    }
-}
-
index 798116163798b728fd2df6ae335dd57c2b944629..a004a3ed992f08a507e71763ab4f61887df302ba 100644 (file)
@@ -98,42 +98,39 @@ class AccountSettingsNav extends Widget
 
     function show()
     {
-        # action => array('prompt', 'title')
-        $menu =
-          array('profilesettings' =>
-                array(_('Profile'),
-                      _('Change your profile settings')),
-                'avatarsettings' =>
-                array(_('Avatar'),
-                      _('Upload an avatar')),
-                'passwordsettings' =>
-                array(_('Password'),
-                      _('Change your password')),
-                'emailsettings' =>
-                array(_('Email'),
-                      _('Change email handling')),
-                'openidsettings' =>
-                array(_('OpenID'),
-                      _('Add or remove OpenIDs')),
-                'userdesignsettings' =>
-                array(_('Design'),
-                      _('Design your profile')),
-                'othersettings' =>
-                array(_('Other'),
-                      _('Other options')));
-
         $action_name = $this->action->trimmed('action');
         $this->action->elementStart('ul', array('class' => 'nav'));
 
-        foreach ($menu as $menuaction => $menudesc) {
-            if ($menuaction == 'openidsettings' &&
-                !common_config('openid', 'enabled')) {
-                continue;
+        if (Event::handle('StartAccountSettingsNav', array(&$this->action))) {
+
+            $menu =
+              array('profilesettings' =>
+                    array(_('Profile'),
+                          _('Change your profile settings')),
+                    'avatarsettings' =>
+                    array(_('Avatar'),
+                          _('Upload an avatar')),
+                    'passwordsettings' =>
+                    array(_('Password'),
+                          _('Change your password')),
+                    'emailsettings' =>
+                    array(_('Email'),
+                          _('Change email handling')),
+                    'userdesignsettings' =>
+                    array(_('Design'),
+                          _('Design your profile')),
+                    'othersettings' =>
+                    array(_('Other'),
+                          _('Other options')));
+
+            foreach ($menu as $menuaction => $menudesc) {
+                $this->action->menuItem(common_local_url($menuaction),
+                                        $menudesc[0],
+                                        $menudesc[1],
+                                        $action_name === $menuaction);
             }
-            $this->action->menuItem(common_local_url($menuaction),
-                                   $menudesc[0],
-                                   $menudesc[1],
-                                   $action_name === $menuaction);
+
+            Event::handle('EndAccountSettingsNav', array(&$this->action));
         }
 
         $this->action->elementEnd('ul');
index 670eb498c1a63375af9c37b882c4a61b87f8af45..02793f0694ed05e5e123a64fca2d122afe541678 100644 (file)
@@ -442,17 +442,12 @@ class Action extends HTMLOutputter // lawsuit
                                 _('Logout'), _('Logout from the site'), false, 'nav_logout');
             }
             else {
-                if (!common_config('site', 'openidonly')) {
-                    if (!common_config('site', 'closed')) {
-                        $this->menuItem(common_local_url('register'),
-                                        _('Register'), _('Create an account'), false, 'nav_register');
-                    }
-                    $this->menuItem(common_local_url('login'),
-                                    _('Login'), _('Login to the site'), false, 'nav_login');
-                } else {
-                    $this->menuItem(common_local_url('openidlogin'),
-                                    _('OpenID'), _('Login with OpenID'), false, 'nav_openid');
+                if (!common_config('site', 'closed')) {
+                    $this->menuItem(common_local_url('register'),
+                                    _('Register'), _('Create an account'), false, 'nav_register');
                 }
+                $this->menuItem(common_local_url('login'),
+                                _('Login'), _('Login to the site'), false, 'nav_login');
             }
             $this->menuItem(common_local_url('doc', array('title' => 'help')),
                             _('Help'), _('Help me!'), false, 'nav_help');
index 01b14f83e7a367cbe2a9ab8544a690e7a111f783..11d40b8e156da190df5b33aa0a45f8aa36a34041 100644 (file)
@@ -312,16 +312,20 @@ class MessageCommand extends Command
     function execute($channel)
     {
         $other = User::staticGet('nickname', common_canonical_nickname($this->other));
+
         $len = mb_strlen($this->text);
+
         if ($len == 0) {
             $channel->error($this->user, _('No content!'));
             return;
-        } else if ($len > 140) {
-            $content = common_shorten_links($content);
-            if (mb_strlen($content) > 140) {
-                $channel->error($this->user, sprintf(_('Message too long - maximum is 140 characters, you sent %d'), $len));
-                return;
-            }
+        }
+
+        $this->text = common_shorten_links($this->text);
+
+        if (Message::contentTooLong($this->text)) {
+            $channel->error($this->user, sprintf(_('Message too long - maximum is %d characters, you sent %d'),
+                                                 Message::maxContent(), mb_strlen($this->text)));
+            return;
         }
 
         if (!$other) {
index 6e4340e5dcac05529218c6246b2aff02e35a62fa..60fc4c3c44802755af6e8bc1c6a5e4ea54bd92f1 100644 (file)
@@ -28,7 +28,7 @@ class CommandInterpreter
         # XXX: localise
 
         $text = preg_replace('/\s+/', ' ', trim($text));
-        list($cmd, $arg) = explode(' ', $text, 2);
+        list($cmd, $arg) = $this->split_arg($text);
 
         # We try to support all the same commands as Twitter, see
         # http://getsatisfaction.com/twitter/topics/what_are_the_twitter_commands
@@ -43,7 +43,7 @@ class CommandInterpreter
             return new HelpCommand($user);
          case 'on':
             if ($arg) {
-                list($other, $extra) = explode(' ', $arg, 2);
+                list($other, $extra) = $this->split_arg($arg);
                 if ($extra) {
                     return null;
                 } else {
@@ -54,7 +54,7 @@ class CommandInterpreter
             }
          case 'off':
             if ($arg) {
-                list($other, $extra) = explode(' ', $arg, 2);
+                list($other, $extra) = $this->split_arg($arg);
                 if ($extra) {
                     return null;
                 } else {
@@ -74,7 +74,7 @@ class CommandInterpreter
              if (!$arg) {
                 return null;
             }
-            list($other, $extra) = explode(' ', $arg, 2);
+            list($other, $extra) = $this->split_arg($arg);
             if ($extra) {
                 return null;
             } else {
@@ -84,7 +84,7 @@ class CommandInterpreter
             if (!$arg) {
                 return null;
             }
-            list($other, $extra) = explode(' ', $arg, 2);
+            list($other, $extra) = $this->split_arg($arg);
             if ($extra) {
                 return null;
             } else {
@@ -95,7 +95,7 @@ class CommandInterpreter
             if (!$arg) {
                 return null;
             }
-            list($other, $extra) = explode(' ', $arg, 2);
+            list($other, $extra) = $this->split_arg($arg);
             if ($extra) {
                 return null;
             } else {
@@ -106,7 +106,7 @@ class CommandInterpreter
             if (!$arg) {
                 return null;
             }
-            list($other, $extra) = explode(' ', $arg, 2);
+            list($other, $extra) = $this->split_arg($arg);
             if ($extra) {
                 return null;
             } else {
@@ -117,7 +117,7 @@ class CommandInterpreter
             if (!$arg) {
                 return null;
             }
-            list($other, $extra) = explode(' ', $arg, 2);
+            list($other, $extra) = $this->split_arg($arg);
             if ($extra) {
                 return null;
             } else {
@@ -128,7 +128,7 @@ class CommandInterpreter
             if (!$arg) {
                 return null;
             }
-            list($other, $extra) = explode(' ', $arg, 2);
+            list($other, $extra) = $this->split_arg($arg);
             if (!$extra) {
                 return null;
             } else {
@@ -138,7 +138,7 @@ class CommandInterpreter
             if (!$arg) {
                 return null;
             }
-            list($other, $extra) = explode(' ', $arg, 2);
+            list($other, $extra) = $this->split_arg($arg);
             if ($extra) {
                 return null;
             } else {
@@ -148,7 +148,7 @@ class CommandInterpreter
             if (!$arg) {
                 return null;
             }
-            list($other, $extra) = explode(' ', $arg, 2);
+            list($other, $extra) = $this->split_arg($arg);
             if ($extra) {
                 return null;
             } else {
@@ -158,7 +158,7 @@ class CommandInterpreter
             if (!$arg) {
                 return null;
             }
-            list($other, $extra) = explode(' ', $arg, 2);
+            list($other, $extra) = $this->split_arg($arg);
             if ($extra) {
                 return null;
             } else {
@@ -173,7 +173,7 @@ class CommandInterpreter
             if (!$arg) {
                 return null;
             }
-            list($other, $extra) = explode(' ', $arg, 2);
+            list($other, $extra) = $this->split_arg($arg);
             if ($extra) {
                 return null;
             } else {
@@ -183,7 +183,7 @@ class CommandInterpreter
             if (!$arg) {
                 return null;
             }
-            list($word, $extra) = explode(' ', $arg, 2);
+            list($word, $extra) = $this->split_arg($arg);
             if ($extra) {
                 return null;
             } else if ($word == 'off') {
@@ -195,7 +195,7 @@ class CommandInterpreter
             if (!$arg) {
                 return null;
             }
-            list($word, $extra) = explode(' ', $arg, 2);
+            list($word, $extra) = $this->split_arg($arg);
             if ($extra) {
                 return null;
             } else if ($word == 'all') {
@@ -213,5 +213,17 @@ class CommandInterpreter
             return false;
         }
     }
+    
+    /**
+     * Split arguments without triggering a PHP notice warning
+     */
+    function split_arg($text)
+    {
+        $pieces = explode(' ', $text, 2);
+        if (count($pieces) == 1) {
+            $pieces[] = null;
+        }
+        return $pieces;
+    }
 }
 
index 88d77732f4cda50508fe33e94585048baf56454c..58e208a4e92b98620fb540c7ffbafb67904d21d3 100644 (file)
 
 if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
 
-define('STATUSNET_VERSION', '0.8.2dev');
+define('STATUSNET_VERSION', '0.9.0dev');
 define('LACONICA_VERSION', STATUSNET_VERSION); // compatibility
 
-define('STATUSNET_CODENAME', 'Second Guessing');
+define('STATUSNET_CODENAME', 'Stand');
 
 define('AVATAR_PROFILE_SIZE', 96);
 define('AVATAR_STREAM_SIZE', 48);
@@ -53,6 +53,7 @@ require_once('DB/DataObject/Cast.php'); # for dates
 if (!function_exists('gettext')) {
     require_once("php-gettext/gettext.inc");
 }
+
 require_once(INSTALLDIR.'/lib/language.php');
 
 // This gets included before the config file, so that admin code and plugins
@@ -93,207 +94,17 @@ if (isset($path)) {
     null;
 }
 
-// default configuration, overwritten in config.php
+require_once(INSTALLDIR.'/lib/default.php');
+
+// Set config values initially to default values
 
-$config =
-  array('site' =>
-        array('name' => 'Just another StatusNet microblog',
-              'server' => $_server,
-              'theme' => 'default',
-              'path' => $_path,
-              'logfile' => null,
-              'logo' => null,
-              'logdebug' => false,
-              'fancy' => false,
-              'locale_path' => INSTALLDIR.'/locale',
-              'language' => 'en_US',
-              'languages' => get_all_languages(),
-              'email' =>
-              array_key_exists('SERVER_ADMIN', $_SERVER) ? $_SERVER['SERVER_ADMIN'] : null,
-              'broughtby' => null,
-              'timezone' => 'UTC',
-              'broughtbyurl' => null,
-              'closed' => false,
-              'inviteonly' => false,
-              'openidonly' => false,
-              'private' => false,
-              'ssl' => 'never',
-              'sslserver' => null,
-              'shorturllength' => 30,
-              'dupelimit' => 60), # default for same person saying the same thing
-        'syslog' =>
-        array('appname' => 'statusnet', # for syslog
-              'priority' => 'debug', # XXX: currently ignored
-              'facility' => LOG_USER),
-        'queue' =>
-        array('enabled' => false,
-              'subsystem' => 'db', # default to database, or 'stomp'
-              'stomp_server' => null,
-              'queue_basename' => 'statusnet',
-              'stomp_username' => null,
-              'stomp_password' => null,
-              ),
-        'license' =>
-        array('url' => 'http://creativecommons.org/licenses/by/3.0/',
-              'title' => 'Creative Commons Attribution 3.0',
-              'image' => 'http://i.creativecommons.org/l/by/3.0/80x15.png'),
-        'mail' =>
-        array('backend' => 'mail',
-              'params' => null),
-        'nickname' =>
-        array('blacklist' => array(),
-              'featured' => array()),
-        'profile' =>
-        array('banned' => array()),
-        'avatar' =>
-        array('server' => null,
-              'dir' => INSTALLDIR . '/avatar/',
-              'path' => $_path . '/avatar/'),
-        'background' =>
-        array('server' => null,
-              'dir' => INSTALLDIR . '/background/',
-              'path' => $_path . '/background/'),
-        'public' =>
-        array('localonly' => true,
-              'blacklist' => array(),
-              'autosource' => array()),
-        'theme' =>
-        array('server' => null,
-              'dir' => null,
-              'path'=> null),
-        'throttle' =>
-        array('enabled' => false, // whether to throttle edits; false by default
-              'count' => 20, // number of allowed messages in timespan
-              'timespan' => 600), // timespan for throttling
-        'xmpp' =>
-        array('enabled' => false,
-              'server' => 'INVALID SERVER',
-              'port' => 5222,
-              'user' => 'update',
-              'encryption' => true,
-              'resource' => 'uniquename',
-              'password' => 'blahblahblah',
-              'host' => null, # only set if != server
-              'debug' => false, # print extra debug info
-              'public' => array()), # JIDs of users who want to receive the public stream
-        'openid' =>
-        array('enabled' => true),
-        'invite' =>
-        array('enabled' => true),
-        'sphinx' =>
-        array('enabled' => false,
-              'server' => 'localhost',
-              'port' => 3312),
-        'tag' =>
-        array('dropoff' => 864000.0),
-        'popular' =>
-        array('dropoff' => 864000.0),
-        'daemon' =>
-        array('piddir' => '/var/run',
-              'user' => false,
-              'group' => false),
-        'emailpost' =>
-        array('enabled' => true),
-        'sms' =>
-        array('enabled' => true),
-        'twitter' =>
-        array('enabled' => true),
-        'twitterbridge' =>
-        array('enabled' => false),
-        'integration' =>
-        array('source' => 'StatusNet', # source attribute for Twitter
-              'taguri' => $_server.',2009'), # base for tag URIs
-       'twitter' =>
-       array('consumer_key'    => null,
-             'consumer_secret' => null),
-        'memcached' =>
-        array('enabled' => false,
-              'server' => 'localhost',
-              'base' => null,
-              'port' => 11211),
-               'ping' =>
-        array('notify' => array()),
-        'inboxes' =>
-        array('enabled' => true), # on by default for new sites
-        'newuser' =>
-        array('default' => null,
-              'welcome' => null),
-        'snapshot' =>
-        array('run' => 'web',
-              'frequency' => 10000,
-              'reporturl' => 'http://status.net/stats/report'),
-        'attachments' =>
-        array('server' => null,
-              'dir' => INSTALLDIR . '/file/',
-              'path' => $_path . '/file/',
-              'supported' => array('image/png',
-                                   'image/jpeg',
-                                   'image/gif',
-                                   'image/svg+xml',
-                                   'audio/mpeg',
-                                   'audio/x-speex',
-                                   '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/x-zip',
-                                   'application/zip',
-                                   'text/plain',
-                                   'video/mpeg',
-                                   'video/mp4',
-                                   'video/quicktime',
-                                   'video/mpeg'),
-        'file_quota' => 5000000,
-        'user_quota' => 50000000,
-        'monthly_quota' => 15000000,
-        'uploads' => true,
-        'filecommand' => '/usr/bin/file',
-        ),
-        'group' =>
-        array('maxaliases' => 3),
-        'oohembed' => array('endpoint' => 'http://oohembed.com/oohembed/'),
-        'search' =>
-        array('type' => 'fulltext'),
-        'sessions' =>
-        array('handle' => false, // whether to handle sessions ourselves
-              'debug' => false), // debugging output for sessions
-        'design' =>
-        array('backgroundcolor' => null, // null -> 'use theme default'
-              'contentcolor' => null,
-              'sidebarcolor' => null,
-              'textcolor' => null,
-              'linkcolor' => null,
-              'backgroundimage' => null,
-              'disposition' => null),
-        );
+$config = $default;
+
+// default configuration, overwritten in config.php
 
 $config['db'] = &PEAR::getStaticProperty('DB_DataObject','options');
 
-$config['db'] =
-  array('database' => 'YOU HAVE TO SET THIS IN config.php',
-        'schema_location' => INSTALLDIR . '/classes',
-        'class_location' => INSTALLDIR . '/classes',
-        'require_prefix' => 'classes/',
-        'class_prefix' => '',
-        'mirror' => null,
-        'utf8' => true,
-        'db_driver' => 'DB', # XXX: JanRain libs only work with DB
-        'quote_identifiers' => false,
-        'type' => 'mysql' );
+$config['db'] = $default['db'];
 
 // Backward compatibility
 
@@ -383,13 +194,25 @@ if ($_db_name != 'statusnet' && !array_key_exists('ini_'.$_db_name, $config['db'
     $config['db']['ini_'.$_db_name] = INSTALLDIR.'/classes/statusnet.ini';
 }
 
-// Ignore openidonly if OpenID is disabled
-
-if (!$config['openid']['enabled']) {
-    $config['site']['openidonly'] = false;
+function __autoload($cls)
+{
+    if (file_exists(INSTALLDIR.'/classes/' . $cls . '.php')) {
+        require_once(INSTALLDIR.'/classes/' . $cls . '.php');
+    } else if (file_exists(INSTALLDIR.'/lib/' . strtolower($cls) . '.php')) {
+        require_once(INSTALLDIR.'/lib/' . strtolower($cls) . '.php');
+    } else if (mb_substr($cls, -6) == 'Action' &&
+               file_exists(INSTALLDIR.'/actions/' . strtolower(mb_substr($cls, 0, -6)) . '.php')) {
+        require_once(INSTALLDIR.'/actions/' . strtolower(mb_substr($cls, 0, -6)) . '.php');
+    } else if ($cls == 'OAuthRequest') {
+        require_once('OAuth.php');
+    } else {
+        Event::handle('Autoload', array(&$cls));
+    }
 }
 
 // XXX: how many of these could be auto-loaded on use?
+// XXX: note that these files should not use config options
+// at compile time since DB config options are not yet loaded.
 
 require_once 'Validate.php';
 require_once 'markdown.php';
@@ -405,24 +228,14 @@ require_once INSTALLDIR.'/lib/twitter.php';
 require_once INSTALLDIR.'/lib/clientexception.php';
 require_once INSTALLDIR.'/lib/serverexception.php';
 
+// Load settings from database; note we need autoload for this
+
+Config::loadSettings();
+
 // XXX: other formats here
 
 define('NICKNAME_FMT', VALIDATE_NUM.VALIDATE_ALPHA_LOWER);
 
-function __autoload($class)
-{
-    if ($class == 'OAuthRequest') {
-        require_once('OAuth.php');
-    } else if (file_exists(INSTALLDIR.'/classes/' . $class . '.php')) {
-        require_once(INSTALLDIR.'/classes/' . $class . '.php');
-    } else if (file_exists(INSTALLDIR.'/lib/' . strtolower($class) . '.php')) {
-        require_once(INSTALLDIR.'/lib/' . strtolower($class) . '.php');
-    } else if (mb_substr($class, -6) == 'Action' &&
-               file_exists(INSTALLDIR.'/actions/' . strtolower(mb_substr($class, 0, -6)) . '.php')) {
-        require_once(INSTALLDIR.'/actions/' . strtolower(mb_substr($class, 0, -6)) . '.php');
-    }
-}
-
 // Give plugins a chance to initialize in a fully-prepared environment
 
 Event::handle('InitializePlugin');
diff --git a/lib/curlclient.php b/lib/curlclient.php
new file mode 100644 (file)
index 0000000..36fc7d1
--- /dev/null
@@ -0,0 +1,179 @@
+n<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Utility class for wrapping Curl
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  HTTP
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+define(CURLCLIENT_VERSION, "0.1");
+
+/**
+ * Wrapper for Curl
+ *
+ * Makes Curl HTTP client calls within our HTTPClient framework
+ *
+ * @category HTTP
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ */
+
+class CurlClient extends HTTPClient
+{
+    function __construct()
+    {
+    }
+
+    function head($url, $headers=null)
+    {
+        $ch = curl_init($url);
+
+        $this->setup($ch);
+
+        curl_setopt_array($ch,
+                          array(CURLOPT_NOBODY => true));
+
+        if (!is_null($headers)) {
+            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
+        }
+
+        $result = curl_exec($ch);
+
+        curl_close($ch);
+
+        return $this->parseResults($result);
+    }
+
+    function get($url, $headers=null)
+    {
+        $ch = curl_init($url);
+
+        $this->setup($ch);
+
+        if (!is_null($headers)) {
+            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
+        }
+
+        $result = curl_exec($ch);
+
+        curl_close($ch);
+
+        return $this->parseResults($result);
+    }
+
+    function post($url, $headers=null, $body=null)
+    {
+        $ch = curl_init($url);
+
+        $this->setup($ch);
+
+        curl_setopt($ch, CURLOPT_POST, true);
+
+        if (!is_null($body)) {
+            curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
+        }
+
+        if (!is_null($headers)) {
+            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
+        }
+
+        $result = curl_exec($ch);
+
+        curl_close($ch);
+
+        return $this->parseResults($result);
+    }
+
+    function setup($ch)
+    {
+        curl_setopt_array($ch,
+                          array(CURLOPT_USERAGENT => $this->userAgent(),
+                                CURLOPT_HEADER => true,
+                                CURLOPT_RETURNTRANSFER => true));
+    }
+
+    function userAgent()
+    {
+        $version = curl_version();
+        return parent::userAgent() . " CurlClient/".CURLCLIENT_VERSION . " cURL/" . $version['version'];
+    }
+
+    function parseResults($results)
+    {
+        $resp = new HTTPResponse();
+
+        $lines = explode("\r\n", $results);
+
+        if (preg_match("#^HTTP/1.[01] (\d\d\d) .+$#", $lines[0], $match)) {
+            $resp->code = $match[1];
+        } else {
+            throw Exception("Bad format: initial line is not HTTP status line");
+        }
+
+        $lastk = null;
+
+        for ($i = 1; $i < count($lines); $i++) {
+            $l =& $lines[$i];
+            if (mb_strlen($l) == 0) {
+                $resp->body = implode("\r\n", array_slice($lines, $i + 1));
+                break;
+            }
+            if (preg_match("#^(\S+):\s+(.*)$#", $l, $match)) {
+                $k = $match[1];
+                $v = $match[2];
+
+                if (array_key_exists($k, $resp->headers)) {
+                    if (is_array($resp->headers[$k])) {
+                        $resp->headers[$k][] = $v;
+                    } else {
+                        $resp->headers[$k] = array($resp->headers[$k], $v);
+                    }
+                } else {
+                    $resp->headers[$k] = $v;
+                }
+                $lastk = $k;
+            } else if (preg_match("#^\s+(.*)$#", $l, $match)) {
+                // continuation line
+                if (is_null($lastk)) {
+                    throw Exception("Bad format: initial whitespace in headers");
+                }
+                $h =& $resp->headers[$lastk];
+                if (is_array($h)) {
+                    $n = count($h);
+                    $h[$n-1] .= $match[1];
+                } else {
+                    $h .= $match[1];
+                }
+            }
+        }
+
+        return $resp;
+    }
+}
diff --git a/lib/default.php b/lib/default.php
new file mode 100644 (file)
index 0000000..7af94d2
--- /dev/null
@@ -0,0 +1,232 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Default settings for core configuration
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Config
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2008-9 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+$default =
+  array('site' =>
+        array('name' => 'Just another StatusNet microblog',
+              'server' => $_server,
+              'theme' => 'default',
+              'path' => $_path,
+              'logfile' => null,
+              'logo' => null,
+              'logdebug' => false,
+              'fancy' => false,
+              'locale_path' => INSTALLDIR.'/locale',
+              'language' => 'en_US',
+              'languages' => get_all_languages(),
+              'email' =>
+              array_key_exists('SERVER_ADMIN', $_SERVER) ? $_SERVER['SERVER_ADMIN'] : null,
+              'broughtby' => null,
+              'timezone' => 'UTC',
+              'broughtbyurl' => null,
+              'closed' => false,
+              'inviteonly' => false,
+              'private' => false,
+              'ssl' => 'never',
+              'sslserver' => null,
+              'shorturllength' => 30,
+              'dupelimit' => 60, # default for same person saying the same thing
+              'textlimit' => 140,
+              ),
+        'db' =>
+        array('database' => 'YOU HAVE TO SET THIS IN config.php',
+              'schema_location' => INSTALLDIR . '/classes',
+              'class_location' => INSTALLDIR . '/classes',
+              'require_prefix' => 'classes/',
+              'class_prefix' => '',
+              'mirror' => null,
+              'utf8' => true,
+              'db_driver' => 'DB', # XXX: JanRain libs only work with DB
+              'quote_identifiers' => false,
+              'type' => 'mysql' ),
+        'syslog' =>
+        array('appname' => 'statusnet', # for syslog
+              'priority' => 'debug', # XXX: currently ignored
+              'facility' => LOG_USER),
+        'queue' =>
+        array('enabled' => false,
+              'subsystem' => 'db', # default to database, or 'stomp'
+              'stomp_server' => null,
+              'queue_basename' => 'statusnet',
+              'stomp_username' => null,
+              'stomp_password' => null,
+              ),
+        'license' =>
+        array('url' => 'http://creativecommons.org/licenses/by/3.0/',
+              'title' => 'Creative Commons Attribution 3.0',
+              'image' => 'http://i.creativecommons.org/l/by/3.0/80x15.png'),
+        'mail' =>
+        array('backend' => 'mail',
+              'params' => null),
+        'nickname' =>
+        array('blacklist' => array(),
+              'featured' => array()),
+        'profile' =>
+        array('banned' => array(),
+              'biolimit' => null),
+        'avatar' =>
+        array('server' => null,
+              'dir' => INSTALLDIR . '/avatar/',
+              'path' => $_path . '/avatar/'),
+        'background' =>
+        array('server' => null,
+              'dir' => INSTALLDIR . '/background/',
+              'path' => $_path . '/background/'),
+        'public' =>
+        array('localonly' => true,
+              'blacklist' => array(),
+              'autosource' => array()),
+        'theme' =>
+        array('server' => null,
+              'dir' => null,
+              'path'=> null),
+        'throttle' =>
+        array('enabled' => false, // whether to throttle edits; false by default
+              'count' => 20, // number of allowed messages in timespan
+              'timespan' => 600), // timespan for throttling
+        'xmpp' =>
+        array('enabled' => false,
+              'server' => 'INVALID SERVER',
+              'port' => 5222,
+              'user' => 'update',
+              'encryption' => true,
+              'resource' => 'uniquename',
+              'password' => 'blahblahblah',
+              'host' => null, # only set if != server
+              'debug' => false, # print extra debug info
+              'public' => array()), # JIDs of users who want to receive the public stream
+        'invite' =>
+        array('enabled' => true),
+        'sphinx' =>
+        array('enabled' => false,
+              'server' => 'localhost',
+              'port' => 3312),
+        'tag' =>
+        array('dropoff' => 864000.0),
+        'popular' =>
+        array('dropoff' => 864000.0),
+        'daemon' =>
+        array('piddir' => '/var/run',
+              'user' => false,
+              'group' => false),
+        'emailpost' =>
+        array('enabled' => true),
+        'sms' =>
+        array('enabled' => true),
+        'twitter' =>
+        array('enabled' => true),
+        'twitterbridge' =>
+        array('enabled' => false),
+        'integration' =>
+        array('source' => 'StatusNet', # source attribute for Twitter
+              'taguri' => $_server.',2009'), # base for tag URIs
+       'twitter' =>
+       array('consumer_key'    => null,
+             'consumer_secret' => null),
+        'memcached' =>
+        array('enabled' => false,
+              'server' => 'localhost',
+              'base' => null,
+              'port' => 11211),
+               'ping' =>
+        array('notify' => array()),
+        'inboxes' =>
+        array('enabled' => true), # on by default for new sites
+        'newuser' =>
+        array('default' => null,
+              'welcome' => null),
+        'snapshot' =>
+        array('run' => 'web',
+              'frequency' => 10000,
+              'reporturl' => 'http://status.net/stats/report'),
+        'attachments' =>
+        array('server' => null,
+              'dir' => INSTALLDIR . '/file/',
+              'path' => $_path . '/file/',
+              'supported' => array('image/png',
+                                   'image/jpeg',
+                                   'image/gif',
+                                   'image/svg+xml',
+                                   'audio/mpeg',
+                                   'audio/x-speex',
+                                   '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/x-zip',
+                                   'application/zip',
+                                   'text/plain',
+                                   'video/mpeg',
+                                   'video/mp4',
+                                   'video/quicktime',
+                                   'video/mpeg'),
+        'file_quota' => 5000000,
+        'user_quota' => 50000000,
+        'monthly_quota' => 15000000,
+        'uploads' => true,
+        'filecommand' => '/usr/bin/file',
+        ),
+        'group' =>
+        array('maxaliases' => 3,
+              'desclimit' => null),
+        'oohembed' => array('endpoint' => 'http://oohembed.com/oohembed/'),
+        'search' =>
+        array('type' => 'fulltext'),
+        'sessions' =>
+        array('handle' => false, // whether to handle sessions ourselves
+              'debug' => false), // debugging output for sessions
+        'design' =>
+        array('backgroundcolor' => null, // null -> 'use theme default'
+              'contentcolor' => null,
+              'sidebarcolor' => null,
+              'textcolor' => null,
+              'linkcolor' => null,
+              'backgroundimage' => null,
+              'disposition' => null),
+        'notice' =>
+        array('contentlimit' => null),
+        'message' =>
+        array('contentlimit' => null),
+        'http' =>
+        array('client' => 'curl'), // XXX: should this be the default?
+        );
diff --git a/lib/deleteaction.php b/lib/deleteaction.php
deleted file mode 100644 (file)
index f702820..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Base class for deleting things
- *
- * PHP version 5
- *
- * LICENCE: This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Personal
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @author    Sarven Capadisli <csarven@status.net>
- * @copyright 2008 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
-     exit(1);
-}
-
-class DeleteAction extends Action
-{
-    var $user         = null;
-    var $notice       = null;
-    var $profile      = null;
-    var $user_profile = null;
-
-    function prepare($args)
-    {
-        parent::prepare($args);
-
-        $this->user   = common_current_user();
-        $notice_id    = $this->trimmed('notice');
-        $this->notice = Notice::staticGet($notice_id);
-
-        if (!$this->notice) {
-            common_user_error(_('No such notice.'));
-            exit;
-        }
-
-        $this->profile      = $this->notice->getProfile();
-        $this->user_profile = $this->user->getProfile();
-
-        return true;
-    }
-
-    function handle($args)
-    {
-        parent::handle($args);
-
-        if (!common_logged_in()) {
-            common_user_error(_('Not logged in.'));
-            exit;
-        } else if ($this->notice->profile_id != $this->user_profile->id) {
-            common_user_error(_('Can\'t delete this notice.'));
-            exit;
-        }
-    }
-
-}
index 5cbb9be5312bf2ff50907cbc06dd41b31cc5a342..3f3a8d3b094b44cde2f94385d60247e49257eef1 100644 (file)
@@ -35,7 +35,6 @@ if (!defined('STATUSNET') && !defined('LACONICA'))
 require_once INSTALLDIR.'/lib/facebookutil.php';
 require_once INSTALLDIR.'/lib/noticeform.php';
 
-
 class FacebookAction extends Action
 {
 
@@ -181,7 +180,6 @@ class FacebookAction extends Action
 
     }
 
-
     // Make this into a widget later
     function showLocalNav()
     {
@@ -241,7 +239,6 @@ class FacebookAction extends Action
         $this->endHTML();
     }
 
-
     function showInstructions()
     {
 
@@ -257,13 +254,8 @@ class FacebookAction extends Action
         $this->elementStart('dd');
         $this->elementStart('p');
         $this->text(sprintf($loginmsg_part1, common_config('site', 'name')));
-        if (!common_config('site', 'openidonly')) {
-            $this->element('a',
-                array('href' => common_local_url('register')), _('Register'));
-        } else {
-            $this->element('a',
-                array('href' => common_local_url('openidlogin')), _('Register'));
-        }
+        $this->element('a',
+            array('href' => common_local_url('register')), _('Register'));
         $this->text($loginmsg_part2);
     $this->elementEnd('p');
         $this->elementEnd('dd');
@@ -272,7 +264,6 @@ class FacebookAction extends Action
         $this->elementEnd('div');
     }
 
-
     function showLoginForm($msg = null)
     {
 
@@ -317,7 +308,6 @@ class FacebookAction extends Action
 
     }
 
-
     function updateProfileBox($notice)
     {
 
@@ -399,7 +389,6 @@ class FacebookAction extends Action
         $this->xw->openURI('php://output');
     }
 
-
     /**
      * Generate pagination links
      *
@@ -458,8 +447,9 @@ class FacebookAction extends Action
         } else {
             $content_shortened = common_shorten_links($content);
 
-            if (mb_strlen($content_shortened) > 140) {
-                $this->showPage(_('That\'s too long. Max notice size is 140 chars.'));
+            if (Notice::contentTooLong($content_shortened)) {
+                $this->showPage(sprintf(_('That\'s too long. Max notice size is %d chars.'),
+                                        Notice::maxContent()));
                 return;
             }
         }
@@ -478,11 +468,11 @@ class FacebookAction extends Action
 
         $replyto = $this->trimmed('inreplyto');
 
-        $notice = Notice::saveNew($user->id, $content,
-            'web', 1, ($replyto == 'false') ? null : $replyto);
-
-        if (is_string($notice)) {
-            $this->showPage($notice);
+        try {
+            $notice = Notice::saveNew($user->id, $content,
+                                      'web', 1, ($replyto == 'false') ? null : $replyto);
+        } catch (Exception $e) {
+            $this->showPage($e->getMessage());
             return;
         }
 
index ad61b6f0a545e5f976ea413da6c799b95b21f865..f5121609d6fbc078ec3fb8ec1235d07bca3b6aae 100644 (file)
@@ -109,7 +109,6 @@ function facebookBroadcastNotice($notice)
 
             $can_update  = $facebook->api_client->users_hasAppPermission('status_update',
                                                                          $fbuid);
-
             if (!empty($attachments) && $can_publish == 1) {
                 $fbattachment = format_attachments($attachments);
                 $facebook->api_client->stream_publish($status, $fbattachment,
@@ -180,7 +179,11 @@ function format_attachments($attachments)
 
     foreach($attachments as $attachment)
     {
-        $fbmedia = get_fbmedia_for_attachment($attachment);
+        if($enclosure = $attachment->getEnclosure()){
+            $fbmedia = get_fbmedia_for_attachment($enclosure);
+        }else{
+            $fbmedia = get_fbmedia_for_attachment($attachment);
+        }
         if($fbmedia){
             $fbattachment['media'][]=$fbmedia;
         }else{
index a649c2ee3b258cecdecc67eb6bd2dea69064fab4..433f6a13871ccec4de2ad2d01837e95412df59ed 100644 (file)
@@ -150,27 +150,33 @@ class GroupEditForm extends Form
         $this->out->elementStart('li');
         $this->out->hidden('groupid', $id);
         $this->out->input('nickname', _('Nickname'),
-                     ($this->out->arg('nickname')) ? $this->out->arg('nickname') : $nickname,
-                     _('1-64 lowercase letters or numbers, no punctuation or spaces'));
+                          ($this->out->arg('nickname')) ? $this->out->arg('nickname') : $nickname,
+                          _('1-64 lowercase letters or numbers, no punctuation or spaces'));
         $this->out->elementEnd('li');
         $this->out->elementStart('li');
         $this->out->input('fullname', _('Full name'),
-                     ($this->out->arg('fullname')) ? $this->out->arg('fullname') : $fullname);
+                          ($this->out->arg('fullname')) ? $this->out->arg('fullname') : $fullname);
         $this->out->elementEnd('li');
         $this->out->elementStart('li');
         $this->out->input('homepage', _('Homepage'),
-                     ($this->out->arg('homepage')) ? $this->out->arg('homepage') : $homepage,
-                     _('URL of the homepage or blog of the group or topic'));
+                          ($this->out->arg('homepage')) ? $this->out->arg('homepage') : $homepage,
+                          _('URL of the homepage or blog of the group or topic'));
         $this->out->elementEnd('li');
         $this->out->elementStart('li');
+        $desclimit = User_group::maxDescription();
+        if ($desclimit == 0) {
+            $descinstr = _('Describe the group or topic');
+        } else {
+            $descinstr = sprintf(_('Describe the group or topic in %d characters'), $desclimit);
+        }
         $this->out->textarea('description', _('Description'),
-                        ($this->out->arg('description')) ? $this->out->arg('description') : $description,
-                        _('Describe the group or topic in 140 chars'));
+                             ($this->out->arg('description')) ? $this->out->arg('description') : $description,
+                             $descinstr);
         $this->out->elementEnd('li');
         $this->out->elementStart('li');
         $this->out->input('location', _('Location'),
-                     ($this->out->arg('location')) ? $this->out->arg('location') : $location,
-                     _('Location for the group, if any, like "City, State (or Region), Country"'));
+                          ($this->out->arg('location')) ? $this->out->arg('location') : $location,
+                          _('Location for the group, if any, like "City, State (or Region), Country"'));
         $this->out->elementEnd('li');
         if (common_config('group', 'maxaliases') > 0) {
             $aliases = (empty($this->group)) ? array() : $this->group->getAliases();
diff --git a/lib/httpclient.php b/lib/httpclient.php
new file mode 100644 (file)
index 0000000..c8c8ae5
--- /dev/null
@@ -0,0 +1,122 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Utility for doing HTTP-related things
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Action
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Useful structure for HTTP responses
+ *
+ * We make HTTP calls in several places, and we have several different
+ * ways of doing them. This class hides the specifics of what underlying
+ * library (curl or PHP-HTTP or whatever) that's used.
+ *
+ * @category HTTP
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ */
+
+class HTTPResponse
+{
+    public $code = null;
+    public $headers = null;
+    public $body = null;
+}
+
+/**
+ * Utility class for doing HTTP client stuff
+ *
+ * We make HTTP calls in several places, and we have several different
+ * ways of doing them. This class hides the specifics of what underlying
+ * library (curl or PHP-HTTP or whatever) that's used.
+ *
+ * @category HTTP
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ */
+
+class HTTPClient
+{
+    static $_client = null;
+
+    static function start()
+    {
+        if (!is_null(self::$_client)) {
+            return self::$_client;
+        }
+
+        $type = common_config('http', 'client');
+
+        switch ($type) {
+         case 'curl':
+            self::$_client = new CurlClient();
+            break;
+         default:
+            throw new Exception("Unknown HTTP client type '$type'");
+            break;
+        }
+
+        return self::$_client;
+    }
+
+    function head($url, $headers)
+    {
+        throw new Exception("HEAD method unimplemented");
+    }
+
+    function get($url, $headers)
+    {
+        throw new Exception("GET method unimplemented");
+    }
+
+    function post($url, $headers, $body)
+    {
+        throw new Exception("POST method unimplemented");
+    }
+
+    function put($url, $headers, $body)
+    {
+        throw new Exception("PUT method unimplemented");
+    }
+
+    function delete($url, $headers)
+    {
+        throw new Exception("DELETE method unimplemented");
+    }
+
+    function userAgent()
+    {
+        return "StatusNet/".STATUSNET_VERSION." (".STATUSNET_CODENAME.")";
+    }
+}
index f740e329a4e0e4b787cede75577c0f0124a07079..b545fbf26914e7fbc44d6e2c83468b6fa9630f0b 100644 (file)
@@ -69,30 +69,25 @@ class LoginGroupNav extends Widget
 
     function show()
     {
-        // action => array('prompt', 'title')
-        $menu = array();
+        $action_name = $this->action->trimmed('action');
+
+        $this->action->elementStart('ul', array('class' => 'nav'));
+
+        if (Event::handle('StartLoginGroupNav', array(&$this->action))) {
+
+            $this->action->menuItem(common_local_url('login'),
+                                    _('Login'),
+                                    _('Login with a username and password'),
+                                    $action_name === 'login');
 
-        if (!common_config('site','openidonly')) {
-            $menu['login'] = array(_('Login'),
-                             _('Login with a username and password'));
             if (!(common_config('site','closed') || common_config('site','inviteonly'))) {
-                $menu['register'] = array(_('Register'),
-                                    _('Sign up for a new account'));
+                $this->action->menuItem(common_local_url('register'),
+                                        _('Register'),
+                                        _('Sign up for a new account'),
+                                        $action_name === 'register');
             }
-        }
-        if (common_config('openid', 'enabled')) {
-            $menu['openidlogin'] = array(_('OpenID'),
-                                   _('Login or register with OpenID'));
-        }
-
-        $action_name = $this->action->trimmed('action');
-        $this->action->elementStart('ul', array('class' => 'nav'));
 
-        foreach ($menu as $menuaction => $menudesc) {
-            $this->action->menuItem(common_local_url($menuaction),
-                                    $menudesc[0],
-                                    $menudesc[1],
-                                    $action_name === $menuaction);
+            Event::handle('EndLoginGroupNav', array(&$this->action));
         }
 
         $this->action->elementEnd('ul');
index 6431bfdcc8297c880c05f23fb3cf4a38d12109db..e25ebfa08f29a2ecaf7a47ae17ff66156c0a34ca 100644 (file)
@@ -140,12 +140,19 @@ class MessageForm extends Form
                                               'rows' => 4,
                                               'name' => 'content'),
                             ($this->content) ? $this->content : '');
-        $this->out->elementStart('dl', 'form_note');
-        $this->out->element('dt', null, _('Available characters'));
-        $this->out->element('dd', array('id' => 'notice_text-count'),
-                            '140');
-        $this->out->elementEnd('dl');
 
+        $contentLimit = Message::maxContent();
+
+        $this->out->element('script', array('type' => 'text/javascript'),
+                            'maxLength = ' . $contentLimit . ';');
+
+        if ($contentLimit > 0) {
+            $this->out->elementStart('dl', 'form_note');
+            $this->out->element('dt', null, _('Available characters'));
+            $this->out->element('dd', array('id' => 'notice_text-count'),
+                                $contentLimit);
+            $this->out->elementEnd('dl');
+        }
     }
 
     /**
index 350e37db8cc90f78c11843fef7adf80aefd880a9..186330bf1c783f430449ff046b0f2d79d1ada9ca 100644 (file)
@@ -90,7 +90,7 @@ class NoticeForm extends Form
         $this->action  = $action;
         $this->content = $content;
         $this->inreplyto = $inreplyto;
-        
+
         if ($user) {
             $this->user = $user;
         } else {
@@ -124,7 +124,6 @@ class NoticeForm extends Form
         return common_local_url('newnotice');
     }
 
-
     /**
      * Legend of the Form
      *
@@ -135,7 +134,6 @@ class NoticeForm extends Form
         $this->out->element('legend', null, _('Send a notice'));
     }
 
-
     /**
      * Data elements
      *
@@ -152,11 +150,20 @@ class NoticeForm extends Form
                                               'rows' => 4,
                                               'name' => 'status_textarea'),
                             ($this->content) ? $this->content : '');
-        $this->out->elementStart('dl', 'form_note');
-        $this->out->element('dt', null, _('Available characters'));
-        $this->out->element('dd', array('id' => 'notice_text-count'),
-                            '140');
-        $this->out->elementEnd('dl');
+
+        $contentLimit = Notice::maxContent();
+
+        $this->out->element('script', array('type' => 'text/javascript'),
+                            'maxLength = ' . $contentLimit . ';');
+
+        if ($contentLimit > 0) {
+            $this->out->elementStart('dl', 'form_note');
+            $this->out->element('dt', null, _('Available characters'));
+            $this->out->element('dd', array('id' => 'notice_text-count'),
+                                $contentLimit);
+            $this->out->elementEnd('dl');
+        }
+
         if (common_config('attachments', 'uploads')) {
             $this->out->element('label', array('for' => 'notice_data-attach'),_('Attach'));
             $this->out->element('input', array('id' => 'notice_data-attach',
index d4cd3ff6e5d21caf08c05e00633f1ded4d00d997..6c296f82a7bfb154db87c28686b3a3592236de74 100644 (file)
@@ -178,9 +178,12 @@ class NoticeListItem extends Widget
     function show()
     {
         $this->showStart();
-        $this->showNotice();
-        $this->showNoticeInfo();
-        $this->showNoticeOptions();
+        if (Event::handle('StartShowNoticeItem', array($this))) {
+            $this->showNotice();
+            $this->showNoticeInfo();
+            $this->showNoticeOptions();
+            Event::handle('EndShowNoticeItem', array($this));
+        }
         $this->showEnd();
     }
 
@@ -469,7 +472,10 @@ class NoticeListItem extends Widget
     function showDeleteLink()
     {
         $user = common_current_user();
-        if ($user && $this->notice->profile_id == $user->id) {
+
+        if (!empty($user) &&
+            ($this->notice->profile_id == $user->id || $user->hasRight(Right::deleteOthersNotice))) {
+
             $deleteurl = common_local_url('deletenotice',
                                           array('notice' => $this->notice->id));
             $this->out->element('a', array('href' => $deleteurl,
index 6db07b20f781c9e2cf901a6e631760b681e80f41..d617a7df7e6216f52d72d0beec8892487256f94b 100644 (file)
 
 if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
 
-require_once(INSTALLDIR.'/lib/omb.php');
+require_once 'libomb/datastore.php';
 
 class StatusNetOAuthDataStore extends OAuthDataStore
 {
 
     // We keep a record of who's contacted us
-
     function lookup_consumer($consumer_key)
     {
         $con = Consumer::staticGet('consumer_key', $consumer_key);
@@ -44,7 +43,9 @@ class StatusNetOAuthDataStore extends OAuthDataStore
     function lookup_token($consumer, $token_type, $token_key)
     {
         $t = new Token();
-        $t->consumer_key = $consumer->key;
+        if (!is_null($consumer)) {
+            $t->consumer_key = $consumer->key;
+        }
         $t->tok = $token_key;
         $t->type = ($token_type == 'access') ? 1 : 0;
         if ($t->find(true)) {
@@ -154,4 +155,345 @@ class StatusNetOAuthDataStore extends OAuthDataStore
     {
         return $this->new_access_token($consumer);
     }
+
+    /**
+     * Revoke specified OAuth token
+     *
+     * Revokes the authorization token specified by $token_key.
+     * Throws exceptions in case of error.
+     *
+     * @param string $token_key The token to be revoked
+     *
+     * @access public
+     **/
+    public function revoke_token($token_key) {
+        $rt = new Token();
+        $rt->tok = $token_key;
+        $rt->type = 0;
+        $rt->state = 0;
+        if (!$rt->find(true)) {
+            throw new Exception('Tried to revoke unknown token');
+        }
+        if (!$rt->delete()) {
+            throw new Exception('Failed to delete revoked token');
+        }
+    }
+
+    /**
+     * Authorize specified OAuth token
+     *
+     * Authorizes the authorization token specified by $token_key.
+     * Throws exceptions in case of error.
+     *
+     * @param string $token_key The token to be authorized
+     *
+     * @access public
+     **/
+    public function authorize_token($token_key) {
+        $rt = new Token();
+        $rt->tok = $token_key;
+        $rt->type = 0;
+        $rt->state = 0;
+        if (!$rt->find(true)) {
+            throw new Exception('Tried to authorize unknown token');
+        }
+        $orig_rt = clone($rt);
+        $rt->state = 1; # Authorized but not used
+        if (!$rt->update($orig_rt)) {
+            throw new Exception('Failed to authorize token');
+        }
+    }
+
+    /**
+     * Get profile by identifying URI
+     *
+     * Returns an OMB_Profile object representing the OMB profile identified by
+     * $identifier_uri.
+     * Returns null if there is no such OMB profile.
+     * Throws exceptions in case of other error.
+     *
+     * @param string $identifier_uri The OMB identifier URI specifying the
+     *                               requested profile
+     *
+     * @access public
+     *
+     * @return OMB_Profile The corresponding profile
+     **/
+    public function getProfile($identifier_uri) {
+        /* getProfile is only used for remote profiles by libomb.
+           TODO: Make it work with local ones anyway. */
+        $remote = Remote_profile::staticGet('uri', $identifier_uri);
+        if (!$remote) throw new Exception('No such remote profile');
+        $profile = Profile::staticGet('id', $remote->id);
+        if (!$profile) throw new Exception('No profile for remote user');
+
+        require_once INSTALLDIR.'/lib/omb.php';
+        return profile_to_omb_profile($identifier_uri, $profile);
+    }
+
+    /**
+     * Save passed profile
+     *
+     * Stores the OMB profile $profile. Overwrites an existing entry.
+     * Throws exceptions in case of error.
+     *
+     * @param OMB_Profile $profile   The OMB profile which should be saved
+     *
+     * @access public
+     **/
+    public function saveProfile($omb_profile) {
+        if (common_profile_url($omb_profile->getNickname()) ==
+                                                $omb_profile->getProfileURL()) {
+            throw new Exception('Not implemented');
+        } else {
+            $remote = Remote_profile::staticGet('uri', $omb_profile->getIdentifierURI());
+
+            if ($remote) {
+                $exists = true;
+                $profile = Profile::staticGet($remote->id);
+                $orig_remote = clone($remote);
+                $orig_profile = clone($profile);
+                # XXX: compare current postNotice and updateProfile URLs to the ones
+                # stored in the DB to avoid (possibly...) above attack
+            } else {
+                $exists = false;
+                $remote = new Remote_profile();
+                $remote->uri = $omb_profile->getIdentifierURI();
+                $profile = new Profile();
+            }
+
+            $profile->nickname = $omb_profile->getNickname();
+            $profile->profileurl = $omb_profile->getProfileURL();
+
+            $fullname = $omb_profile->getFullname();
+            $profile->fullname = is_null($fullname) ? '' : $fullname;
+            $homepage = $omb_profile->getHomepage();
+            $profile->homepage = is_null($homepage) ? '' : $homepage;
+            $bio = $omb_profile->getBio();
+            $profile->bio = is_null($bio) ? '' : $bio;
+            $location = $omb_profile->getLocation();
+            $profile->location = is_null($location) ? '' : $location;
+
+            if ($exists) {
+                $profile->update($orig_profile);
+            } else {
+                $profile->created = DB_DataObject_Cast::dateTime(); # current time
+                $id = $profile->insert();
+                if (!$id) {
+                    throw new Exception(_('Error inserting new profile'));
+                }
+                $remote->id = $id;
+            }
+
+            $avatar_url = $omb_profile->getAvatarURL();
+            if ($avatar_url) {
+                if (!$this->add_avatar($profile, $avatar_url)) {
+                    throw new Exception(_('Error inserting avatar'));
+                }
+            } else {
+                $avatar = $profile->getOriginalAvatar();
+                if($avatar) $avatar->delete();
+                $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
+                if($avatar) $avatar->delete();
+                $avatar = $profile->getAvatar(AVATAR_STREAM_SIZE);
+                if($avatar) $avatar->delete();
+                $avatar = $profile->getAvatar(AVATAR_MINI_SIZE);
+                if($avatar) $avatar->delete();
+            }
+
+            if ($exists) {
+                if (!$remote->update($orig_remote)) {
+                    throw new Exception(_('Error updating remote profile'));
+                }
+            } else {
+                $remote->created = DB_DataObject_Cast::dateTime(); # current time
+                if (!$remote->insert()) {
+                    throw new Exception(_('Error inserting remote profile'));
+                }
+            }
+        }
+    }
+
+    function add_avatar($profile, $url)
+    {
+        $temp_filename = tempnam(sys_get_temp_dir(), 'listener_avatar');
+        copy($url, $temp_filename);
+        $imagefile = new ImageFile($profile->id, $temp_filename);
+        $filename = Avatar::filename($profile->id,
+                                     image_type_to_extension($imagefile->type),
+                                     null,
+                                     common_timestamp());
+        rename($temp_filename, Avatar::path($filename));
+        return $profile->setOriginal($filename);
+    }
+
+    /**
+     * Save passed notice
+     *
+     * Stores the OMB notice $notice. The datastore may change the passed notice.
+     * This might by neccessary for URIs depending on a database key. Note that
+     * it is the user’s duty to present a mechanism for his OMB_Datastore to
+     * appropriately change his OMB_Notice.
+     * Throws exceptions in case of error.
+     *
+     * @param OMB_Notice $notice The OMB notice which should be saved
+     *
+     * @access public
+     **/
+    public function saveNotice(&$omb_notice) {
+        if (Notice::staticGet('uri', $omb_notice->getIdentifierURI())) {
+            throw new Exception(_('Duplicate notice'));
+        }
+        $author_uri = $omb_notice->getAuthor()->getIdentifierURI();
+        common_log(LOG_DEBUG, $author_uri, __FILE__);
+        $author = Remote_profile::staticGet('uri', $author_uri);
+        if (!$author) {
+            $author = User::staticGet('uri', $author_uri);
+        }
+        if (!$author) {
+            throw new Exception('No such user');
+        }
+
+        common_log(LOG_DEBUG, print_r($author, true), __FILE__);
+
+        $notice = Notice::saveNew($author->id,
+                                  $omb_notice->getContent(),
+                                  'omb',
+                                  false,
+                                  null,
+                                  $omb_notice->getIdentifierURI());
+
+        common_broadcast_notice($notice, true);
+    }
+
+    /**
+     * Get subscriptions of a given profile
+     *
+     * Returns an array containing subscription informations for the specified
+     * profile. Every array entry should in turn be an array with keys
+     *   'uri´: The identifier URI of the subscriber
+     *   'token´: The subscribe token
+     *   'secret´: The secret token
+     * Throws exceptions in case of error.
+     *
+     * @param string $subscribed_user_uri The OMB identifier URI specifying the
+     *                                    subscribed profile
+     *
+     * @access public
+     *
+     * @return mixed An array containing the subscriptions or 0 if no
+     *               subscription has been found.
+     **/
+    public function getSubscriptions($subscribed_user_uri) {
+        $sub = new Subscription();
+
+        $user = $this->_getAnyProfile($subscribed_user_uri);
+
+        $sub->subscribed = $user->id;
+
+        if (!$sub->find(true)) {
+            return 0;
+        }
+
+        /* Since we do not use OMB_Service_Provider’s action methods, there
+           is no need to actually return the subscriptions. */
+        return 1;
+    }
+
+    private function _getAnyProfile($uri)
+    {
+        $user = Remote_profile::staticGet('uri', $uri);
+        if (!$user) {
+            $user = User::staticGet('uri', $uri);
+        }
+        if (!$user) {
+            throw new Exception('No such user');
+        }
+        return $user;
+    }
+
+    /**
+     * Delete a subscription
+     *
+     * Deletes the subscription from $subscriber_uri to $subscribed_user_uri.
+     * Throws exceptions in case of error.
+     *
+     * @param string $subscriber_uri      The OMB identifier URI specifying the
+     *                                    subscribing profile
+     *
+     * @param string $subscribed_user_uri The OMB identifier URI specifying the
+     *                                    subscribed profile
+     *
+     * @access public
+     **/
+    public function deleteSubscription($subscriber_uri, $subscribed_user_uri)
+    {
+        $sub = new Subscription();
+
+        $subscribed = $this->_getAnyProfile($subscribed_user_uri);
+        $subscriber = $this->_getAnyProfile($subscriber_uri);
+
+        $sub->subscribed = $subscribed->id;
+        $sub->subscriber = $subscriber->id;
+
+        $sub->delete();
+    }
+
+    /**
+     * Save a subscription
+     *
+     * Saves the subscription from $subscriber_uri to $subscribed_user_uri.
+     * Throws exceptions in case of error.
+     *
+     * @param string     $subscriber_uri      The OMB identifier URI specifying
+     *                                        the subscribing profile
+     *
+     * @param string     $subscribed_user_uri The OMB identifier URI specifying
+     *                                        the subscribed profile
+     * @param OAuthToken $token               The access token
+     *
+     * @access public
+     **/
+    public function saveSubscription($subscriber_uri, $subscribed_user_uri,
+                                                                       $token)
+    {
+        $sub = new Subscription();
+
+        $subscribed = $this->_getAnyProfile($subscribed_user_uri);
+        $subscriber = $this->_getAnyProfile($subscriber_uri);
+
+        $sub->subscribed = $subscribed->id;
+        $sub->subscriber = $subscriber->id;
+
+        $sub_exists = $sub->find(true);
+
+        if ($sub_exists) {
+            $orig_sub = clone($sub);
+        } else {
+            $sub->created = DB_DataObject_Cast::dateTime();
+        }
+
+        $sub->token  = $token->key;
+        $sub->secret = $token->secret;
+
+        if ($sub_exists) {
+            $result = $sub->update($orig_sub);
+        } else {
+            $result = $sub->insert();
+        }
+
+        if (!$result) {
+            common_log_db_error($sub, ($sub_exists) ? 'UPDATE' : 'INSERT', __FILE__);
+            throw new Exception(_('Couldn\'t insert new subscription.'));
+            return;
+        }
+
+        /* Notify user, if necessary. */
+
+        if ($subscribed instanceof User) {
+            mail_subscribe_notify_profile($subscribed,
+                                          Profile::staticGet($subscriber->id));
+        }
+    }
 }
+?>
index 7dca760c6951da3c72e2e71132d7c6c133d12400..0566701ff16990fbc45b6e3003b6e4f67455c5f7 100644 (file)
 
 if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
 
-require_once('OAuth.php');
-require_once(INSTALLDIR.'/lib/oauthstore.php');
-
-require_once(INSTALLDIR.'/classes/Consumer.php');
-require_once(INSTALLDIR.'/classes/Nonce.php');
-require_once(INSTALLDIR.'/classes/Token.php');
-
-require_once('Auth/Yadis/Yadis.php');
-
-define('OAUTH_NAMESPACE', 'http://oauth.net/core/1.0/');
-define('OMB_NAMESPACE', 'http://openmicroblogging.org/protocol/0.1');
-define('OMB_VERSION_01', 'http://openmicroblogging.org/protocol/0.1');
-define('OAUTH_DISCOVERY', 'http://oauth.net/discovery/1.0');
-
-define('OMB_ENDPOINT_UPDATEPROFILE', OMB_NAMESPACE.'/updateProfile');
-define('OMB_ENDPOINT_POSTNOTICE', OMB_NAMESPACE.'/postNotice');
-define('OAUTH_ENDPOINT_REQUEST', OAUTH_NAMESPACE.'endpoint/request');
-define('OAUTH_ENDPOINT_AUTHORIZE', OAUTH_NAMESPACE.'endpoint/authorize');
-define('OAUTH_ENDPOINT_ACCESS', OAUTH_NAMESPACE.'endpoint/access');
-define('OAUTH_ENDPOINT_RESOURCE', OAUTH_NAMESPACE.'endpoint/resource');
-define('OAUTH_AUTH_HEADER', OAUTH_NAMESPACE.'parameters/auth-header');
-define('OAUTH_POST_BODY', OAUTH_NAMESPACE.'parameters/post-body');
-define('OAUTH_HMAC_SHA1', OAUTH_NAMESPACE.'signature/HMAC-SHA1');
+require_once INSTALLDIR.'/lib/oauthstore.php';
+require_once 'OAuth.php';
+require_once 'libomb/constants.php';
+require_once 'libomb/service_consumer.php';
+require_once 'libomb/notice.php';
+require_once 'libomb/profile.php';
+require_once 'Auth/Yadis/Yadis.php';
 
 function omb_oauth_consumer()
 {
     static $con = null;
-    if (!$con) {
+    if (is_null($con)) {
         $con = new OAuthConsumer(common_root_url(), '');
     }
     return $con;
@@ -55,7 +39,7 @@ function omb_oauth_consumer()
 function omb_oauth_server()
 {
     static $server = null;
-    if (!$server) {
+    if (is_null($server)) {
         $server = new OAuthServer(omb_oauth_datastore());
         $server->add_signature_method(omb_hmac_sha1());
     }
@@ -65,7 +49,7 @@ function omb_oauth_server()
 function omb_oauth_datastore()
 {
     static $store = null;
-    if (!$store) {
+    if (is_null($store)) {
         $store = new StatusNetOAuthDataStore();
     }
     return $store;
@@ -74,57 +58,18 @@ function omb_oauth_datastore()
 function omb_hmac_sha1()
 {
     static $hmac_method = null;
-    if (!$hmac_method) {
+    if (is_null($hmac_method)) {
         $hmac_method = new OAuthSignatureMethod_HMAC_SHA1();
     }
     return $hmac_method;
 }
 
-function omb_get_services($xrd, $type)
+function omb_broadcast_notice($notice)
 {
-    return $xrd->services(array(omb_service_filter($type)));
-}
-
-function omb_service_filter($type)
-{
-    return create_function('$s',
-                           'return omb_match_service($s, \''.$type.'\');');
-}
-
-function omb_match_service($service, $type)
-{
-    return in_array($type, $service->getTypes());
-}
-
-function omb_service_uri($service)
-{
-    if (!$service) {
-        return null;
-    }
-    $uris = $service->getURIs();
-    if (!$uris) {
-        return null;
-    }
-    return $uris[0];
-}
-
-function omb_local_id($service)
-{
-    if (!$service) {
-        return null;
-    }
-    $els = $service->getElements('xrd:LocalID');
-    if (!$els) {
-        return null;
-    }
-    $el = $els[0];
-    return $service->parser->content($el);
-}
 
-function omb_broadcast_remote_subscribers($notice)
-{
+    $omb_notice = notice_to_omb_notice($notice);
 
-    # First, get remote users subscribed to this profile
+    /* Get remote users subscribed to this profile. */
     $rp = new Remote_profile();
 
     $rp->query('SELECT postnoticeurl, token, secret ' .
@@ -135,170 +80,148 @@ function omb_broadcast_remote_subscribers($notice)
     $posted = array();
 
     while ($rp->fetch()) {
-        if (!array_key_exists($rp->postnoticeurl, $posted)) {
-            common_log(LOG_DEBUG, 'Posting to ' . $rp->postnoticeurl);
-            if (omb_post_notice_keys($notice, $rp->postnoticeurl, $rp->token, $rp->secret)) {
-                common_log(LOG_DEBUG, 'Finished to ' . $rp->postnoticeurl);
-                $posted[$rp->postnoticeurl] = true;
-            } else {
-                common_log(LOG_DEBUG, 'Failed posting to ' . $rp->postnoticeurl);
-            }
+        if (isset($posted[$rp->postnoticeurl])) {
+            /* We already posted to this url. */
+            continue;
         }
-    }
-
-    $rp->free();
-    unset($rp);
+        common_debug('Posting to ' . $rp->postnoticeurl, __FILE__);
+
+        /* Post notice. */
+        $service = new Laconica_OMB_Service_Consumer(
+                     array(OMB_ENDPOINT_POSTNOTICE => $rp->postnoticeurl));
+        try {
+            $service->setToken($rp->token, $rp->secret);
+            $service->postNotice($omb_notice);
+        } catch (Exception $e) {
+            common_log(LOG_ERR, 'Failed posting to ' . $rp->postnoticeurl);
+            common_log(LOG_ERR, 'Error status '.$e);
+            continue;
+        }
+        $posted[$rp->postnoticeurl] = true;
 
-    return true;
-}
+        common_debug('Finished to ' . $rp->postnoticeurl, __FILE__);
+    }
 
-function omb_post_notice($notice, $remote_profile, $subscription)
-{
-    return omb_post_notice_keys($notice, $remote_profile->postnoticeurl, $subscription->token, $subscription->secret);
+    return;
 }
 
-function omb_post_notice_keys($notice, $postnoticeurl, $tk, $secret)
+function omb_broadcast_profile($profile)
 {
-    $user = User::staticGet('id', $notice->profile_id);
+    $user = User::staticGet('id', $profile->id);
 
     if (!$user) {
         return false;
     }
 
-    $con = omb_oauth_consumer();
+    $profile = $user->getProfile();
 
-    $token = new OAuthToken($tk, $secret);
-
-    $url = $postnoticeurl;
-    $parsed = parse_url($url);
-    $params = array();
-    parse_str($parsed['query'], $params);
-
-    $req = OAuthRequest::from_consumer_and_token($con, $token,
-                                                 'POST', $url, $params);
-
-    $req->set_parameter('omb_version', OMB_VERSION_01);
-    $req->set_parameter('omb_listenee', $user->uri);
-    $req->set_parameter('omb_notice', $notice->uri);
-    $req->set_parameter('omb_notice_content', $notice->content);
-    $req->set_parameter('omb_notice_url', common_local_url('shownotice',
-                                                           array('notice' =>
-                                                                 $notice->id)));
-    $req->set_parameter('omb_notice_license', common_config('license', 'url'));
+    $omb_profile = profile_to_omb_profile($user->uri, $profile, true);
 
-    $user->free();
-    unset($user);
+    /* Get remote users subscribed to this profile. */
+    $rp = new Remote_profile();
 
-    $req->sign_request(omb_hmac_sha1(), $con, $token);
+    $rp->query('SELECT updateprofileurl, token, secret ' .
+               'FROM subscription JOIN remote_profile ' .
+               'ON subscription.subscriber = remote_profile.id ' .
+               'WHERE subscription.subscribed = ' . $profile->id . ' ');
 
-    # We re-use this tool's fetcher, since it's pretty good
+    $posted = array();
 
-    $fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
+    while ($rp->fetch()) {
+        if (isset($posted[$rp->updateprofileurl])) {
+            /* We already posted to this url. */
+            continue;
+        }
+        common_debug('Posting to ' . $rp->updateprofileurl, __FILE__);
+
+        /* Update profile. */
+        $service = new StatusNet_OMB_Service_Consumer(
+                     array(OMB_ENDPOINT_UPDATEPROFILE => $rp->updateprofileurl));
+        try {
+            $service->setToken($rp->token, $rp->secret);
+            $service->updateProfile($omb_profile);
+        } catch (Exception $e) {
+            common_log(LOG_ERR, 'Failed posting to ' . $rp->updateprofileurl);
+            common_log(LOG_ERR, 'Error status '.$e);
+            continue;
+        }
+        $posted[$rp->updateprofileurl] = true;
 
-    if (!$fetcher) {
-        common_log(LOG_WARNING, 'Failed to initialize Yadis fetcher.', __FILE__);
-        return false;
+        common_debug('Finished to ' . $rp->updateprofileurl, __FILE__);
     }
 
-    $result = $fetcher->post($req->get_normalized_http_url(),
-                             $req->to_postdata(),
-                             array('User-Agent: StatusNet/' . STATUSNET_VERSION));
-
-    if ($result->status == 403) { # not authorized, don't send again
-        common_debug('403 result, deleting subscription', __FILE__);
-        # FIXME: figure out how to delete this
-        # $subscription->delete();
-        return false;
-    } else if ($result->status != 200) {
-        common_debug('Error status '.$result->status, __FILE__);
-        return false;
-    } else { # success!
-        parse_str($result->body, $return);
-        if ($return['omb_version'] == OMB_VERSION_01) {
-            return true;
-        } else {
-            return false;
-        }
-    }
+    return;
 }
 
-function omb_broadcast_profile($profile)
-{
-    # First, get remote users subscribed to this profile
-    # XXX: use a join here rather than looping through results
-    $sub = new Subscription();
-    $sub->subscribed = $profile->id;
-    if ($sub->find()) {
-        $updated = array();
-        while ($sub->fetch()) {
-            $rp = Remote_profile::staticGet('id', $sub->subscriber);
-            if ($rp) {
-                if (!array_key_exists($rp->updateprofileurl, $updated)) {
-                    if (omb_update_profile($profile, $rp, $sub)) {
-                        $updated[$rp->updateprofileurl] = true;
-                    }
-                }
-            }
-        }
+class StatusNet_OMB_Service_Consumer extends OMB_Service_Consumer {
+    public function __construct($urls)
+    {
+        $this->services       = $urls;
+        $this->datastore      = omb_oauth_datastore();
+        $this->oauth_consumer = omb_oauth_consumer();
+        $this->fetcher        = Auth_Yadis_Yadis::getHTTPFetcher();
     }
+
 }
 
-function omb_update_profile($profile, $remote_profile, $subscription)
+function profile_to_omb_profile($uri, $profile, $force = false)
 {
-    $user = User::staticGet($profile->id);
-    $con = omb_oauth_consumer();
-    $token = new OAuthToken($subscription->token, $subscription->secret);
-    $url = $remote_profile->updateprofileurl;
-    $parsed = parse_url($url);
-    $params = array();
-    parse_str($parsed['query'], $params);
-    $req = OAuthRequest::from_consumer_and_token($con, $token,
-                                                 "POST", $url, $params);
-    $req->set_parameter('omb_version', OMB_VERSION_01);
-    $req->set_parameter('omb_listenee', $user->uri);
-    $req->set_parameter('omb_listenee_profile', common_profile_url($profile->nickname));
-    $req->set_parameter('omb_listenee_nickname', $profile->nickname);
-
-    # We use blanks to force emptying any existing values in these optional fields
-
-    $req->set_parameter('omb_listenee_fullname',
-                        ($profile->fullname) ? $profile->fullname : '');
-    $req->set_parameter('omb_listenee_homepage',
-                        ($profile->homepage) ? $profile->homepage : '');
-    $req->set_parameter('omb_listenee_bio',
-                        ($profile->bio) ? $profile->bio : '');
-    $req->set_parameter('omb_listenee_location',
-                        ($profile->location) ? $profile->location : '');
+    $omb_profile = new OMB_Profile($uri);
+    $omb_profile->setNickname($profile->nickname);
+    $omb_profile->setLicenseURL(common_config('license', 'url'));
+    if (!is_null($profile->fullname)) {
+        $omb_profile->setFullname($profile->fullname);
+    } elseif ($force) {
+        $omb_profile->setFullname('');
+    }
+    if (!is_null($profile->homepage)) {
+        $omb_profile->setHomepage($profile->homepage);
+    } elseif ($force) {
+        $omb_profile->setHomepage('');
+    }
+    if (!is_null($profile->bio)) {
+        $omb_profile->setBio($profile->bio);
+    } elseif ($force) {
+        $omb_profile->setBio('');
+    }
+    if (!is_null($profile->location)) {
+        $omb_profile->setLocation($profile->location);
+    } elseif ($force) {
+        $omb_profile->setLocation('');
+    }
+    if (!is_null($profile->profileurl)) {
+        $omb_profile->setProfileURL($profile->profileurl);
+    } elseif ($force) {
+        $omb_profile->setProfileURL('');
+    }
 
     $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
-    $req->set_parameter('omb_listenee_avatar',
-                        ($avatar) ? $avatar->url : '');
+    if ($avatar) {
+        $omb_profile->setAvatarURL($avatar->url);
+    } elseif ($force) {
+        $omb_profile->setAvatarURL('');
+    }
+    return $omb_profile;
+}
 
-    $req->sign_request(omb_hmac_sha1(), $con, $token);
+function notice_to_omb_notice($notice)
+{
+    /* Create an OMB_Notice for $notice. */
+    $user = User::staticGet('id', $notice->profile_id);
 
-    # We re-use this tool's fetcher, since it's pretty good
+    if (!$user) {
+        return null;
+    }
 
-    $fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
+    $profile = $user->getProfile();
 
-    $result = $fetcher->post($req->get_normalized_http_url(),
-                             $req->to_postdata(),
-                             array('User-Agent: StatusNet/' . STATUSNET_VERSION));
+    $omb_notice = new OMB_Notice(profile_to_omb_profile($user->uri, $profile),
+                                 $notice->uri,
+                                 $notice->content);
+    $omb_notice->setURL(common_local_url('shownotice', array('notice' =>
+                                                                 $notice->id)));
+    $omb_notice->setLicenseURL(common_config('license', 'url'));
 
-    if (empty($result) || !$result) {
-        common_debug("Unable to contact " . $req->get_normalized_http_url());
-    } else if ($result->status == 403) { # not authorized, don't send again
-        common_debug('403 result, deleting subscription', __FILE__);
-        $subscription->delete();
-        return false;
-    } else if ($result->status != 200) {
-        common_debug('Error status '.$result->status, __FILE__);
-        return false;
-    } else { # success!
-        parse_str($result->body, $return);
-        if (isset($return['omb_version']) && $return['omb_version'] === OMB_VERSION_01) {
-            return true;
-        } else {
-            return false;
-        }
-    }
+    return $omb_notice;
 }
+?>
diff --git a/lib/openid.php b/lib/openid.php
deleted file mode 100644 (file)
index 7a2c46f..0000000
+++ /dev/null
@@ -1,280 +0,0 @@
-<?php
-/*
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2008, 2009, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
-
-require_once(INSTALLDIR.'/classes/User_openid.php');
-
-require_once('Auth/OpenID.php');
-require_once('Auth/OpenID/Consumer.php');
-require_once('Auth/OpenID/SReg.php');
-require_once('Auth/OpenID/MySQLStore.php');
-
-# About one year cookie expiry
-
-define('OPENID_COOKIE_EXPIRY', round(365.25 * 24 * 60 * 60));
-define('OPENID_COOKIE_KEY', 'lastusedopenid');
-
-function oid_store()
-{
-    static $store = null;
-    if (!$store) {
-        # Can't be called statically
-        $user = new User();
-        $conn = $user->getDatabaseConnection();
-        $store = new Auth_OpenID_MySQLStore($conn);
-    }
-    return $store;
-}
-
-function oid_consumer()
-{
-    $store = oid_store();
-    $consumer = new Auth_OpenID_Consumer($store);
-    return $consumer;
-}
-
-function oid_clear_last()
-{
-    oid_set_last('');
-}
-
-function oid_set_last($openid_url)
-{
-    common_set_cookie(OPENID_COOKIE_KEY,
-                     $openid_url,
-                     time() + OPENID_COOKIE_EXPIRY);
-}
-
-function oid_get_last()
-{
-    if (empty($_COOKIE[OPENID_COOKIE_KEY])) {
-        return null;
-    }
-    $openid_url = $_COOKIE[OPENID_COOKIE_KEY];
-    if ($openid_url && strlen($openid_url) > 0) {
-        return $openid_url;
-    } else {
-        return null;
-    }
-}
-
-function oid_link_user($id, $canonical, $display)
-{
-
-    $oid = new User_openid();
-    $oid->user_id = $id;
-    $oid->canonical = $canonical;
-    $oid->display = $display;
-    $oid->created = DB_DataObject_Cast::dateTime();
-
-    if (!$oid->insert()) {
-        $err = PEAR::getStaticProperty('DB_DataObject','lastError');
-        common_debug('DB error ' . $err->code . ': ' . $err->message, __FILE__);
-        return false;
-    }
-
-    return true;
-}
-
-function oid_get_user($openid_url)
-{
-    $user = null;
-    $oid = User_openid::staticGet('canonical', $openid_url);
-    if ($oid) {
-        $user = User::staticGet('id', $oid->user_id);
-    }
-    return $user;
-}
-
-function oid_check_immediate($openid_url, $backto=null)
-{
-    if (!$backto) {
-        $action = $_REQUEST['action'];
-        $args = common_copy_args($_GET);
-        unset($args['action']);
-        $backto = common_local_url($action, $args);
-    }
-    common_debug('going back to "' . $backto . '"', __FILE__);
-
-    common_ensure_session();
-
-    $_SESSION['openid_immediate_backto'] = $backto;
-    common_debug('passed-in variable is "' . $backto . '"', __FILE__);
-    common_debug('session variable is "' . $_SESSION['openid_immediate_backto'] . '"', __FILE__);
-
-    oid_authenticate($openid_url,
-                     'finishimmediate',
-                     true);
-}
-
-function oid_authenticate($openid_url, $returnto, $immediate=false)
-{
-
-    $consumer = oid_consumer();
-
-    if (!$consumer) {
-        common_server_error(_('Cannot instantiate OpenID consumer object.'));
-        return false;
-    }
-
-    common_ensure_session();
-
-    $auth_request = $consumer->begin($openid_url);
-
-    // Handle failure status return values.
-    if (!$auth_request) {
-        return _('Not a valid OpenID.');
-    } else if (Auth_OpenID::isFailure($auth_request)) {
-        return sprintf(_('OpenID failure: %s'), $auth_request->message);
-    }
-
-    $sreg_request = Auth_OpenID_SRegRequest::build(// Required
-                                                   array(),
-                                                   // Optional
-                                                   array('nickname',
-                                                         'email',
-                                                         'fullname',
-                                                         'language',
-                                                         'timezone',
-                                                         'postcode',
-                                                         'country'));
-
-    if ($sreg_request) {
-        $auth_request->addExtension($sreg_request);
-    }
-
-    $trust_root = common_root_url(true);
-    $process_url = common_local_url($returnto);
-
-    if ($auth_request->shouldSendRedirect()) {
-        $redirect_url = $auth_request->redirectURL($trust_root,
-                                                   $process_url,
-                                                   $immediate);
-        if (!$redirect_url) {
-        } else if (Auth_OpenID::isFailure($redirect_url)) {
-            return sprintf(_('Could not redirect to server: %s'), $redirect_url->message);
-        } else {
-            common_redirect($redirect_url, 303);
-        }
-    } else {
-        // Generate form markup and render it.
-        $form_id = 'openid_message';
-        $form_html = $auth_request->formMarkup($trust_root, $process_url,
-                                               $immediate, array('id' => $form_id));
-
-        # XXX: This is cheap, but things choke if we don't escape ampersands
-        # in the HTML attributes
-
-        $form_html = preg_replace('/&/', '&amp;', $form_html);
-
-        // Display an error if the form markup couldn't be generated;
-        // otherwise, render the HTML.
-        if (Auth_OpenID::isFailure($form_html)) {
-            common_server_error(sprintf(_('Could not create OpenID form: %s'), $form_html->message));
-        } else {
-            $action = new AutosubmitAction(); // see below
-            $action->form_html = $form_html;
-            $action->form_id = $form_id;
-            $action->prepare(array('action' => 'autosubmit'));
-            $action->handle(array('action' => 'autosubmit'));
-        }
-    }
-}
-
-# Half-assed attempt at a module-private function
-
-function _oid_print_instructions()
-{
-    common_element('div', 'instructions',
-                   _('This form should automatically submit itself. '.
-                      'If not, click the submit button to go to your '.
-                      'OpenID provider.'));
-}
-
-# update a user from sreg parameters
-
-function oid_update_user(&$user, &$sreg)
-{
-
-    $profile = $user->getProfile();
-
-    $orig_profile = clone($profile);
-
-    if ($sreg['fullname'] && strlen($sreg['fullname']) <= 255) {
-        $profile->fullname = $sreg['fullname'];
-    }
-
-    if ($sreg['country']) {
-        if ($sreg['postcode']) {
-            # XXX: use postcode to get city and region
-            # XXX: also, store postcode somewhere -- it's valuable!
-            $profile->location = $sreg['postcode'] . ', ' . $sreg['country'];
-        } else {
-            $profile->location = $sreg['country'];
-        }
-    }
-
-    # XXX save language if it's passed
-    # XXX save timezone if it's passed
-
-    if (!$profile->update($orig_profile)) {
-        common_server_error(_('Error saving the profile.'));
-        return false;
-    }
-
-    $orig_user = clone($user);
-
-    if ($sreg['email'] && Validate::email($sreg['email'], true)) {
-        $user->email = $sreg['email'];
-    }
-
-    if (!$user->update($orig_user)) {
-        common_server_error(_('Error saving the user.'));
-        return false;
-    }
-
-    return true;
-}
-
-class AutosubmitAction extends Action
-{
-    var $form_html = null;
-    var $form_id = null;
-
-    function handle($args)
-    {
-        parent::handle($args);
-        $this->showPage();
-    }
-
-    function title()
-    {
-        return _('OpenID Auto-Submit');
-    }
-
-    function showContent()
-    {
-        $this->raw($this->form_html);
-        $this->element('script', null,
-                       '$(document).ready(function() { ' .
-                       '    $(\'#'. $this->form_id .'\').submit(); '.
-                       '});');
-    }
-}
index 87d7be5a7597296bb2c2d777358f7365455beb5d..59bf3ba9d695914c6544dd76dade659e97bd2dad 100644 (file)
@@ -76,4 +76,18 @@ class Plugin
     {
         return true;
     }
+
+    /* 
+    * the name of the shortener
+    * shortenerInfo associative array with additional information. One possible element is 'freeService' which can be true or false
+    * shortener array, first element is the name of the class, second element is an array to be passed as constructor parameters to the class
+    */
+    function registerUrlShortener($name, $shortenerInfo, $shortener)
+    {
+        global $_shorteners;
+        if(!is_array($_shorteners)){
+            $_shorteners=array();
+        }
+        $_shorteners[$name]=array('info'=>$shortenerInfo, 'callInfo'=>$shortener);
+    }
 }
diff --git a/lib/right.php b/lib/right.php
new file mode 100644 (file)
index 0000000..4e0096d
--- /dev/null
@@ -0,0 +1,50 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Class for user rights
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Authorization
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+    exit(1);
+}
+
+/**
+ * class for rights
+ *
+ * Mostly for holding the rights constants
+ *
+ * @category Authorization
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ */
+
+class Right
+{
+    const deleteOthersNotice = 'deleteothersnotice';
+}
+
index 5529e60acb82f8a59f71d079823dff6bba6086d7..c18f273ed06ae6b948953d75ab0a392caad8d2f1 100644 (file)
@@ -50,8 +50,7 @@ class Router
     var $m = null;
     static $inst = null;
     static $bare = array('requesttoken', 'accesstoken', 'userauthorization',
-                         'postnotice', 'updateprofile', 'finishremotesubscribe',
-                         'finishopenidlogin', 'finishaddopenid');
+                         'postnotice', 'updateprofile', 'finishremotesubscribe');
 
     static function get()
     {
@@ -76,7 +75,6 @@ class Router
 
         $m->connect('', array('action' => 'public'));
         $m->connect('rss', array('action' => 'publicrss'));
-        $m->connect('xrds', array('action' => 'publicxrds'));
         $m->connect('featuredrss', array('action' => 'featuredrss'));
         $m->connect('favoritedrss', array('action' => 'favoritedrss'));
         $m->connect('opensearch/people', array('action' => 'opensearch',
@@ -128,7 +126,6 @@ class Router
 
         // exceptional
 
-        $m->connect('main/openid', array('action' => 'openidlogin'));
         $m->connect('main/remote', array('action' => 'remotesubscribe'));
         $m->connect('main/remote?nickname=:nickname', array('action' => 'remotesubscribe'), array('nickname' => '[A-Za-z0-9_-]+'));
 
@@ -138,7 +135,7 @@ class Router
 
         // settings
 
-        foreach (array('profile', 'avatar', 'password', 'openid', 'im',
+        foreach (array('profile', 'avatar', 'password', 'im',
                        'email', 'sms', 'twitter', 'userdesign', 'other') as $s) {
             $m->connect('settings/'.$s, array('action' => $s.'settings'));
         }
@@ -463,7 +460,7 @@ class Router
         // user stuff
 
         foreach (array('subscriptions', 'subscribers',
-                       'nudge', 'xrds', 'all', 'foaf',
+                       'nudge', 'all', 'foaf', 'xrds',
                        'replies', 'inbox', 'outbox', 'microsummary') as $a) {
             $m->connect(':nickname/'.$a,
                         array('action' => $a),
index 60611e48d0f0a22a9fc87434a74e90773f532dbe..faf6bec7dec4618d8ebc4888c4e55353f8aff99a 100644 (file)
@@ -78,25 +78,12 @@ class Rss10Action extends Action
     function prepare($args)
     {
         parent::prepare($args);
+
         $this->limit = (int) $this->trimmed('limit');
+
         if ($this->limit == 0) {
             $this->limit = DEFAULT_RSS_LIMIT;
         }
-        return true;
-    }
-
-    /**
-     * Handle a request
-     *
-     * @param array $args Arguments from $_REQUEST
-     *
-     * @return void
-     */
-
-    function handle($args)
-    {
-        // Parent handling, including cache check
-        parent::handle($args);
 
         if (common_config('site', 'private')) {
             if (!isset($_SERVER['PHP_AUTH_USER'])) {
@@ -122,8 +109,21 @@ class Rss10Action extends Action
             }
         }
 
-        // Get the list of notices
-        $this->notices = $this->getNotices($this->limit);
+        return true;
+    }
+
+    /**
+     * Handle a request
+     *
+     * @param array $args Arguments from $_REQUEST
+     *
+     * @return void
+     */
+
+    function handle($args)
+    {
+        // Parent handling, including cache check
+        parent::handle($args);
         $this->showRss();
     }
 
@@ -140,7 +140,7 @@ class Rss10Action extends Action
     }
 
     /**
-     * Get the notices to output in this stream
+     * Get the notices to output in this stream.
      *
      * @return array an array of Notice objects sorted in reverse chron
      */
@@ -258,26 +258,27 @@ class Rss10Action extends Action
         $attachments = $notice->attachments();
         if($attachments){
             foreach($attachments as $attachment){
-                if ($attachment->isEnclosure()) {
+                $enclosure=$attachment->getEnclosure();
+                if ($enclosure) {
                     // DO NOT move xmlns declaration to root element. Making it
                     // the default namespace here improves compatibility with
                     // real-world feed readers.
                     $attribs = array(
-                        'rdf:resource' => $attachment->url,
-                        'url' => $attachment->url,
+                        'rdf:resource' => $enclosure->url,
+                        'url' => $enclosure->url,
                         'xmlns' => 'http://purl.oclc.org/net/rss_2.0/enc#'
                         );
-                    if ($attachment->title) {
-                        $attribs['dc:title'] = $attachment->title;
+                    if ($enclosure->title) {
+                        $attribs['dc:title'] = $enclosure->title;
                     }
-                    if ($attachment->modified) {
-                        $attribs['dc:date'] = common_date_w3dtf($attachment->modified);
+                    if ($enclosure->modified) {
+                        $attribs['dc:date'] = common_date_w3dtf($enclosure->modified);
                     }
-                    if ($attachment->size) {
-                        $attribs['length'] = $attachment->size;
+                    if ($enclosure->size) {
+                        $attribs['length'] = $enclosure->size;
                     }
-                    if ($attachment->mimetype) {
-                        $attribs['type'] = $attachment->mimetype;
+                    if ($enclosure->mimetype) {
+                        $attribs['type'] = $enclosure->mimetype;
                     }
                     $this->element('enclosure', $attribs);
                 }
index a1f305f5b74e076cdda6256b8bb04a6e1d1cbdae..c3669868d4d3cb177401e1f1747b7f7c06cf6c0d 100644 (file)
@@ -77,9 +77,7 @@ class SettingsAction extends CurrentUserDesignAction
             // _all_ our settings are important
             common_set_returnto($this->selfUrl());
             $user = common_current_user();
-            if ($user->hasOpenID()) {
-                common_redirect(common_local_url('openidlogin'), 303);
-            } else {
+            if (Event::handle('RedirectToLogin', array($this, $user))) {
                 common_redirect(common_local_url('login'), 303);
             }
         } else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
index 676c9b20a2522f37fafbcd0092a7c88dd1e1f778..b49e2e11902997bdfb1681a48729361397f956f1 100644 (file)
@@ -165,9 +165,10 @@ function broadcast_twitter($notice)
 }
 
 function broadcast_oauth($notice, $flink) {
-
     $user = $flink->getUser();
     $statustxt = format_status($notice);
+    // Convert !groups to #hashes
+    $statustxt = preg_replace('/(^|\s)!([A-Za-z0-9]{1,64})/', "\\1#\\2", $statustxt);
     $token = TwitterOAuthClient::unpackToken($flink->credentials);
     $client = new TwitterOAuthClient($token->key, $token->secret);
     $status = null;
@@ -222,6 +223,10 @@ function broadcast_basicauth($notice, $flink)
                           $user->nickname, $user->id);
         common_log(LOG_WARNING, $errmsg);
 
+            $errmsg = sprintf('No data returned by Twitter API when ' .
+                             'trying to send update for %1$s (user id %2$s).',
+                             $user->nickname, $user->id);
+            common_log(LOG_WARNING, $errmsg);
         return false;
     }
 
index 3bac400e2f678a1ad95001816f769f1dfeca846d..4a5de6ab3af99573badeac5441c88055db71580a 100644 (file)
@@ -274,11 +274,12 @@ class TwitterapiAction extends Action
         $enclosures = array();
 
         foreach ($attachments as $attachment) {
-            if ($attachment->isEnclosure()) {
+            $enclosure_o=$attachment->getEnclosure();
+            if ($enclosure_o) {
                  $enclosure = array();
-                 $enclosure['url'] = $attachment->url;
-                 $enclosure['mimetype'] = $attachment->mimetype;
-                 $enclosure['size'] = $attachment->size;
+                 $enclosure['url'] = $enclosure_o->url;
+                 $enclosure['mimetype'] = $enclosure_o->mimetype;
+                 $enclosure['size'] = $enclosure_o->size;
                  $enclosures[] = $enclosure;
             }
         }
@@ -594,7 +595,6 @@ class TwitterapiAction extends Action
 
         $this->init_document('rss');
 
-        $this->elementStart('channel');
         $this->element('title', null, $title);
         $this->element('link', null, $link);
         if (!is_null($suplink)) {
@@ -620,7 +620,6 @@ class TwitterapiAction extends Action
             }
         }
 
-        $this->elementEnd('channel');
         $this->end_twitter_rss();
     }
 
@@ -667,7 +666,6 @@ class TwitterapiAction extends Action
 
         $this->init_document('rss');
 
-        $this->elementStart('channel');
         $this->element('title', null, $title);
         $this->element('link', null, $link);
         $this->element('description', null, $subtitle);
@@ -686,7 +684,6 @@ class TwitterapiAction extends Action
             }
         }
 
-        $this->elementEnd('channel');
         $this->end_twitter_rss();
     }
 
@@ -999,11 +996,14 @@ class TwitterapiAction extends Action
     function init_twitter_rss()
     {
         $this->startXML();
-        $this->elementStart('rss', array('version' => '2.0'));
+        $this->elementStart('rss', array('version' => '2.0', 'xmlns:atom'=>'http://www.w3.org/2005/Atom'));
+        $this->elementStart('channel');
+        Event::handle('StartApiRss', array($this));
     }
 
     function end_twitter_rss()
     {
+        $this->elementEnd('channel');
         $this->elementEnd('rss');
         $this->endXML();
     }
@@ -1015,6 +1015,7 @@ class TwitterapiAction extends Action
         $this->elementStart('feed', array('xmlns' => 'http://www.w3.org/2005/Atom',
                                           'xml:lang' => 'en-US',
                                           'xmlns:thr' => 'http://purl.org/syndication/thread/1.0'));
+        Event::handle('StartApiAtom', array($this));
     }
 
     function end_twitter_atom()
index e37fa05f0a6214c140ab71dab264c9f200702496..bad2b74ca324101388eaa6feae1e787e65c72892 100644 (file)
@@ -118,7 +118,7 @@ class TwitterOAuthClient extends OAuthClient
     }
 
     /**
-     * Calls Twitter's /stutuses/update API method
+     * Calls Twitter's /statuses/update API method
      *
      * @param string $status                text of the status
      * @param int    $in_reply_to_status_id optional id of the status it's
@@ -137,7 +137,7 @@ class TwitterOAuthClient extends OAuthClient
     }
 
     /**
-     * Calls Twitter's /stutuses/friends_timeline API method
+     * Calls Twitter's /statuses/friends_timeline API method
      *
      * @param int $since_id show statuses after this id
      * @param int $max_id   show statuses before this id
@@ -167,7 +167,7 @@ class TwitterOAuthClient extends OAuthClient
     }
 
     /**
-     * Calls Twitter's /stutuses/friends API method
+     * Calls Twitter's /statuses/friends API method
      *
      * @param int $id          id of the user whom you wish to see friends of
      * @param int $user_id     numerical user id
@@ -197,7 +197,7 @@ class TwitterOAuthClient extends OAuthClient
     }
 
     /**
-     * Calls Twitter's /stutuses/friends/ids API method
+     * Calls Twitter's /statuses/friends/ids API method
      *
      * @param int $id          id of the user whom you wish to see friends of
      * @param int $user_id     numerical user id
index 3cdad0b54a7473a1478571f6dc8dd44ef1f4c34f..6cfe5bcbd31e9c7d518f6f8929a76be52583e6ab 100644 (file)
@@ -39,7 +39,7 @@ class UnQueueManager
          case 'omb':
             if ($this->_isLocal($notice)) {
                 require_once(INSTALLDIR.'/lib/omb.php');
-                omb_broadcast_remote_subscribers($notice);
+                omb_broadcast_notice($notice);
             }
             break;
          case 'public':
@@ -72,8 +72,13 @@ class UnQueueManager
             require_once(INSTALLDIR.'/lib/jabber.php');
             jabber_broadcast_notice($notice);
             break;
+         case 'plugin':
+            Event::handle('HandleQueuedNotice', array(&$notice));
+            break;
          default:
-            throw ServerException("UnQueueManager: Unknown queue: $type");
+            if (Event::handle('UnqueueHandleNotice', array(&$notice, $queue))) {
+                throw ServerException("UnQueueManager: Unknown queue: $queue");
+            }
         }
     }
 
index a069ccf7b36ae5dca3e19fcb1dd092ffb5c006f3..44a377220016ea08347426f5b1bdc97241b930db 100644 (file)
@@ -391,10 +391,10 @@ function common_render_content($text, $notice)
 {
     $r = common_render_text($text);
     $id = $notice->profile_id;
-    $r = preg_replace('/(^|\s+)@([A-Za-z0-9]{1,64})/e', "'\\1@'.common_at_link($id, '\\2')", $r);
+    $r = preg_replace('/(^|[\s\.\,\:\;]+)@([A-Za-z0-9]{1,64})/e', "'\\1@'.common_at_link($id, '\\2')", $r);
     $r = preg_replace('/^T ([A-Z0-9]{1,64}) /e', "'T '.common_at_link($id, '\\1').' '", $r);
-    $r = preg_replace('/(^|\s+)@#([A-Za-z0-9]{1,64})/e', "'\\1@#'.common_at_hash_link($id, '\\2')", $r);
-    $r = preg_replace('/(^|\s)!([A-Za-z0-9]{1,64})/e', "'\\1!'.common_group_link($id, '\\2')", $r);
+    $r = preg_replace('/(^|[\s\.\,\:\;]+)@#([A-Za-z0-9]{1,64})/e', "'\\1@#'.common_at_hash_link($id, '\\2')", $r);
+    $r = preg_replace('/(^|[\s\.\,\:\;]+)!([A-Za-z0-9]{1,64})/e', "'\\1!'.common_group_link($id, '\\2')", $r);
     return $r;
 }
 
@@ -493,7 +493,7 @@ function callback_helper($matches, $callback, $notice_id) {
     }while($original_url!=$url);
 
     if(empty($notice_id)){
-        $result = call_user_func_array($callback,$url);
+        $result = call_user_func_array($callback, array($url));
     }else{
         $result = call_user_func_array($callback, array(array($url,$notice_id)) );
     }
@@ -522,21 +522,22 @@ function common_linkify($url) {
 
    if(strpos($url, '@') !== false && strpos($url, ':') === false) {
        //url is an email address without the mailto: protocol
-       return XMLStringer::estring('a', array('href' => "mailto:$url", 'rel' => 'external'), $url);
-   }
+       $canon = "mailto:$url";
+       $longurl = "mailto:$url";
+   }else{
 
-    $canon = File_redirection::_canonUrl($url);
+        $canon = File_redirection::_canonUrl($url);
 
-    $longurl_data = File_redirection::where($url);
-    if (is_array($longurl_data)) {
-        $longurl = $longurl_data['url'];
-    } elseif (is_string($longurl_data)) {
-        $longurl = $longurl_data;
-    } else {
-        throw new ServerException("Can't linkify url '$url'");
+        $longurl_data = File_redirection::where($canon);
+        if (is_array($longurl_data)) {
+            $longurl = $longurl_data['url'];
+        } elseif (is_string($longurl_data)) {
+            $longurl = $longurl_data;
+        } else {
+            throw new ServerException("Can't linkify url '$url'");
+        }
     }
-
-    $attrs = array('href' => $canon, 'rel' => 'external');
+    $attrs = array('href' => $canon, 'title' => $longurl, 'rel' => 'external');
 
     $is_attachment = false;
     $attachment_id = null;
@@ -584,7 +585,8 @@ function common_linkify($url) {
 
 function common_shorten_links($text)
 {
-    if (mb_strlen($text) <= 140) return $text;
+    $maxLength = Notice::maxContent();
+    if ($maxLength == 0 || mb_strlen($text) <= $maxLength) return $text;
     return common_replace_urls_callback($text, array('File_redirection', 'makeShort'));
 }
 
@@ -729,14 +731,10 @@ function common_relative_profile($sender, $nickname, $dt=null)
 
 function common_local_url($action, $args=null, $params=null, $fragment=null)
 {
-    static $sensitive = array('login', 'register', 'passwordsettings',
-                              'twittersettings', 'finishopenidlogin',
-                              'finishaddopenid', 'api');
-
     $r = Router::get();
     $path = $r->build($action, $args, $params, $fragment);
 
-    $ssl = in_array($action, $sensitive);
+    $ssl = common_is_sensitive($action);
 
     if (common_config('site','fancy')) {
         $url = common_path(mb_substr($path, 1), $ssl);
@@ -750,6 +748,19 @@ function common_local_url($action, $args=null, $params=null, $fragment=null)
     return $url;
 }
 
+function common_is_sensitive($action)
+{
+    static $sensitive = array('login', 'register', 'passwordsettings',
+                              'twittersettings', 'api');
+    $ssl = null;
+
+    if (Event::handle('SensitiveAction', array($action, &$ssl))) {
+        $ssl = in_array($action, $sensitive);
+    }
+
+    return $ssl;
+}
+
 function common_path($relative, $ssl=false)
 {
     $pathpart = (common_config('site', 'path')) ? common_config('site', 'path')."/" : '';
@@ -888,7 +899,8 @@ function common_enqueue_notice($notice)
                                     'twitter',
                                     'facebook',
                                     'ping');
-    static $allTransports = array('sms');
+
+    static $allTransports = array('sms', 'plugin');
 
     $transports = $allTransports;
 
@@ -906,11 +918,16 @@ function common_enqueue_notice($notice)
         }
     }
 
-    $qm = QueueManager::get();
+    if (Event::handle('StartEnqueueNotice', array($notice, &$transports))) {
+
+        $qm = QueueManager::get();
+
+        foreach ($transports as $transport)
+        {
+            $qm->enqueue($notice, $transport);
+        }
 
-    foreach ($transports as $transport)
-    {
-        $qm->enqueue($notice, $transport);
+        Event::handle('EndEnqueueNotice', array($notice, $transports));
     }
 
     return true;
@@ -1156,7 +1173,8 @@ function common_negotiate_type($cprefs, $sprefs)
 function common_config($main, $sub)
 {
     global $config;
-    return isset($config[$main][$sub]) ? $config[$main][$sub] : false;
+    return (array_key_exists($main, $config) &&
+            array_key_exists($sub, $config[$main])) ? $config[$main][$sub] : false;
 }
 
 function common_copy_args($from)
@@ -1363,57 +1381,19 @@ function common_shorten_url($long_url)
     } else {
         $svc = $user->urlshorteningservice;
     }
-
-    $curlh = curl_init();
-    curl_setopt($curlh, CURLOPT_CONNECTTIMEOUT, 20); // # seconds to wait
-    curl_setopt($curlh, CURLOPT_USERAGENT, 'StatusNet');
-    curl_setopt($curlh, CURLOPT_RETURNTRANSFER, true);
-
-    switch($svc) {
-     case 'ur1.ca':
-        require_once INSTALLDIR.'/lib/Shorturl_api.php';
-        $short_url_service = new LilUrl;
-        $short_url = $short_url_service->shorten($long_url);
-        break;
-
-     case '2tu.us':
-        $short_url_service = new TightUrl;
-        require_once INSTALLDIR.'/lib/Shorturl_api.php';
-        $short_url = $short_url_service->shorten($long_url);
-        break;
-
-     case 'ptiturl.com':
-        require_once INSTALLDIR.'/lib/Shorturl_api.php';
-        $short_url_service = new PtitUrl;
-        $short_url = $short_url_service->shorten($long_url);
-        break;
-
-     case 'bit.ly':
-        curl_setopt($curlh, CURLOPT_URL, 'http://bit.ly/api?method=shorten&long_url='.urlencode($long_url));
-        $short_url = current(json_decode(curl_exec($curlh))->results)->hashUrl;
-        break;
-
-     case 'is.gd':
-        curl_setopt($curlh, CURLOPT_URL, 'http://is.gd/api.php?longurl='.urlencode($long_url));
-        $short_url = curl_exec($curlh);
-        break;
-     case 'snipr.com':
-        curl_setopt($curlh, CURLOPT_URL, 'http://snipr.com/site/snip?r=simple&link='.urlencode($long_url));
-        $short_url = curl_exec($curlh);
-        break;
-     case 'metamark.net':
-        curl_setopt($curlh, CURLOPT_URL, 'http://metamark.net/api/rest/simple?long_url='.urlencode($long_url));
-        $short_url = curl_exec($curlh);
-        break;
-     case 'tinyurl.com':
-        curl_setopt($curlh, CURLOPT_URL, 'http://tinyurl.com/api-create.php?url='.urlencode($long_url));
-        $short_url = curl_exec($curlh);
-        break;
-     default:
-        $short_url = false;
+    global $_shorteners;
+    if (!isset($_shorteners[$svc])) {
+        //the user selected service doesn't exist, so default to ur1.ca
+        $svc = 'ur1.ca';
+    }
+    if (!isset($_shorteners[$svc])) {
+       // no shortener plugins installed.
+       return $long_url;
     }
 
-    curl_close($curlh);
+    $reflectionObj = new ReflectionClass($_shorteners[$svc]['callInfo'][0]);
+    $short_url_service = $reflectionObj->newInstanceArgs($_shorteners[$svc]['callInfo'][1]);
+    $short_url = $short_url_service->shorten($long_url);
 
     return $short_url;
 }
diff --git a/plugins/LilUrl/LilUrlPlugin.php b/plugins/LilUrl/LilUrlPlugin.php
new file mode 100644 (file)
index 0000000..7665b6c
--- /dev/null
@@ -0,0 +1,64 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Plugin to push RSS/Atom updates to a PubSubHubBub hub
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Plugin
+ * @package   StatusNet
+ * @author    Craig Andrews <candrews@integralblue.com>
+ * @copyright 2009 Craig Andrews http://candrews.integralblue.com
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+require_once(INSTALLDIR.'/lib/Shorturl_api.php');
+
+class LilUrlPlugin extends Plugin
+{
+    function __construct()
+    {
+        parent::__construct();
+    }
+
+    function onInitializePlugin(){
+        $this->registerUrlShortener(
+            'ur1.ca',
+            array('freeService'=>true),
+            array('LilUrl',array('http://ur1.ca/'))
+        );
+    }
+}
+
+class LilUrl extends ShortUrlApi
+{
+    protected function shorten_imp($url) {
+        $data['longurl'] = $url;
+        $response = $this->http_post($data);
+        if (!$response) return $url;
+        $y = @simplexml_load_string($response);
+        if (!isset($y->body)) return $url;
+        $x = $y->body->p[0]->a->attributes();
+        if (isset($x['href'])) return $x['href'];
+        return $url;
+    }
+}
diff --git a/plugins/OpenID/OpenIDPlugin.php b/plugins/OpenID/OpenIDPlugin.php
new file mode 100644 (file)
index 0000000..91bddf3
--- /dev/null
@@ -0,0 +1,225 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Plugin
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Plugin for OpenID authentication and identity
+ *
+ * This class enables consumer support for OpenID, the distributed authentication
+ * and identity system.
+ *
+ * @category Plugin
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ * @link     http://openid.net/
+ */
+
+class OpenIDPlugin extends Plugin
+{
+    /**
+     * Initializer for the plugin.
+     */
+
+    function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Add OpenID-related paths to the router table
+     *
+     * Hook for RouterInitialized event.
+     *
+     * @return boolean hook return
+     */
+
+    function onRouterInitialized(&$m)
+    {
+        $m->connect('main/openid', array('action' => 'openidlogin'));
+        $m->connect('settings/openid', array('action' => 'openidsettings'));
+        $m->connect('xrds', array('action' => 'publicxrds'));
+        $m->connect('index.php?action=finishopenidlogin', array('action' => 'finishopenidlogin'));
+        $m->connect('index.php?action=finishaddopenid', array('action' => 'finishaddopenid'));
+
+        return true;
+    }
+
+    function onEndLoginGroupNav(&$action)
+    {
+        $action_name = $action->trimmed('action');
+
+        $action->menuItem(common_local_url('openidlogin'),
+                          _('OpenID'),
+                          _('Login or register with OpenID'),
+                          $action_name === 'openidlogin');
+
+        return true;
+    }
+
+    function onEndAccountSettingsNav(&$action)
+    {
+        $action_name = $action->trimmed('action');
+
+        $action->menuItem(common_local_url('openidsettings'),
+                          _('OpenID'),
+                          _('Add or remove OpenIDs'),
+                          $action_name === 'openidsettings');
+
+        return true;
+    }
+
+    function onAutoload($cls)
+    {
+        switch ($cls)
+        {
+         case 'OpenidloginAction':
+         case 'FinishopenidloginAction':
+         case 'FinishaddopenidAction':
+         case 'XrdsAction':
+         case 'PublicxrdsAction':
+         case 'OpenidsettingsAction':
+            require_once(INSTALLDIR.'/plugins/OpenID/' . strtolower(mb_substr($cls, 0, -6)) . '.php');
+            return false;
+         case 'User_openid':
+            require_once(INSTALLDIR.'/plugins/OpenID/User_openid.php');
+            return false;
+         default:
+            return true;
+        }
+    }
+
+    function onSensitiveAction($action, &$ssl)
+    {
+        switch ($action)
+        {
+         case 'finishopenidlogin':
+         case 'finishaddopenid':
+            $ssl = true;
+            return false;
+         default:
+            return true;
+        }
+    }
+
+    function onLoginAction($action, &$login)
+    {
+        switch ($action)
+        {
+         case 'openidlogin':
+         case 'finishopenidlogin':
+            $login = true;
+            return false;
+         default:
+            return true;
+        }
+    }
+
+    /**
+     * We include a <meta> element linking to the publicxrds page, for OpenID
+     * client-side authentication.
+     *
+     * @return void
+     */
+
+    function onEndHeadChildren($action)
+    {
+        // for client side of OpenID authentication
+        $action->element('meta', array('http-equiv' => 'X-XRDS-Location',
+                                       'content' => common_local_url('publicxrds')));
+    }
+
+    /**
+     * Redirect to OpenID login if they have an OpenID
+     *
+     * @return boolean whether to continue
+     */
+
+    function onRedirectToLogin($action, $user)
+    {
+        if (!empty($user) && User_openid::hasOpenID($user->id)) {
+            common_redirect(common_local_url('openidlogin'), 303);
+            return false;
+        }
+        return true;
+    }
+
+    function onEndShowPageNotice($action)
+    {
+        $name = $action->trimmed('action');
+
+        switch ($name)
+        {
+         case 'register':
+            $instr = '(Have an [OpenID](http://openid.net/)? ' .
+              'Try our [OpenID registration]'.
+              '(%%action.openidlogin%%)!)';
+            break;
+         case 'login':
+            $instr = '(Have an [OpenID](http://openid.net/)? ' .
+              'Try our [OpenID login]'.
+              '(%%action.openidlogin%%)!)';
+            break;
+         default:
+            return true;
+        }
+
+        $output = common_markup_to_html($instr);
+        $action->raw($output);
+        return true;
+    }
+
+    function onStartLoadDoc(&$title, &$output)
+    {
+        if ($title == 'openid')
+        {
+            $filename = INSTALLDIR.'/plugins/OpenID/doc-src/openid';
+
+            $c = file_get_contents($filename);
+            $output = common_markup_to_html($c);
+            return false; // success!
+        }
+
+        return true;
+    }
+
+    function onEndLoadDoc($title, &$output)
+    {
+        if ($title == 'help')
+        {
+            $menuitem = '* [OpenID](%%doc.openid%%) - what OpenID is and how to use it with this service';
+
+            $output .= common_markup_to_html($menuitem);
+        }
+
+        return true;
+    }
+}
diff --git a/plugins/OpenID/User_openid.php b/plugins/OpenID/User_openid.php
new file mode 100644 (file)
index 0000000..338e0f6
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Table Definition for user_openid
+ */
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class User_openid extends Memcached_DataObject
+{
+    ###START_AUTOCODE
+    /* the code below is auto generated do not remove the above tag */
+
+    public $__table = 'user_openid';                     // table name
+    public $canonical;                       // varchar(255)  primary_key not_null
+    public $display;                         // varchar(255)  unique_key not_null
+    public $user_id;                         // int(4)   not_null
+    public $created;                         // datetime()   not_null
+    public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
+
+    /* Static get */
+    function staticGet($k,$v=null)
+    { return Memcached_DataObject::staticGet('User_openid',$k,$v); }
+
+    /* the code above is auto generated do not remove the tag below */
+    ###END_AUTOCODE
+
+    static function hasOpenID($user_id)
+    {
+        $oid = new User_openid();
+
+        $oid->user_id = $user_id;
+
+        $cnt = $oid->find();
+
+        return ($cnt > 0);
+    }
+}
diff --git a/plugins/OpenID/doc-src/openid b/plugins/OpenID/doc-src/openid
new file mode 100644 (file)
index 0000000..c741e36
--- /dev/null
@@ -0,0 +1,11 @@
+%%site.name%% supports the [OpenID](http://openid.net/) standard for single signon between Web sites. OpenID lets you log into many different Web sites without using a different password for each. (See [Wikipedia's OpenID article](http://en.wikipedia.org/wiki/OpenID) for more information.)
+
+If you already have an account on %%site.name%%, you can [login](%%action.login%%) with your username and password as usual.
+To use OpenID in the future, you can [add an OpenID to your account](%%action.openidsettings%%) after you have logged in normally.
+
+There are many [Public OpenID providers](http://wiki.openid.net/Public_OpenID_providers), and you may already have an OpenID-enabled account on another service.
+
+* On wikis: If you have an account on an OpenID-enabled wiki, like [Wikitravel](http://wikitravel.org/), [wikiHow](http://www.wikihow.com/), [Vinismo](http://vinismo.com/), [AboutUs](http://aboutus.org/) or [Keiki](http://kei.ki/), you can log in to %%site.name%% by entering the **full URL** of your user page on that other wiki in the box above. For example, *http://kei.ki/en/User:Evan*.
+* [Yahoo!](http://openid.yahoo.com/) : If you have an account with Yahoo!, you can log in to this site by entering your Yahoo!-provided OpenID in the box above. Yahoo! OpenID URLs have the form *https://me.yahoo.com/yourusername*.
+* [AOL](http://dev.aol.com/aol-and-63-million-openids) : If you have an account with [AOL](http://www.aol.com/), like an [AIM](http://www.aim.com/) account, you can log in to %%site.name%% by entering your AOL-provided OpenID in the box above. AOL OpenID URLs have the form *http://openid.aol.com/yourusername*. Your username should be all lowercase, no spaces.
+* [Blogger](http://bloggerindraft.blogspot.com/2008/01/new-feature-blogger-as-openid-provider.html), [Wordpress.com](http://faq.wordpress.com/2007/03/06/what-is-openid/), [LiveJournal](http://www.livejournal.com/openid/about.bml), [Vox](http://bradfitz.vox.com/library/post/openid-for-vox.html) : If you have a blog on any of these services, enter your blog URL in the box above. For example, *http://yourusername.blogspot.com/*, *http://yourusername.wordpress.com/*, *http://yourusername.livejournal.com/*, or *http://yourusername.vox.com/*.
diff --git a/plugins/OpenID/finishaddopenid.php b/plugins/OpenID/finishaddopenid.php
new file mode 100644 (file)
index 0000000..6e88920
--- /dev/null
@@ -0,0 +1,185 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Complete adding an OpenID
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Settings
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2008-2009 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+    exit(1);
+}
+
+require_once INSTALLDIR.'/plugins/OpenID/openid.php';
+
+/**
+ * Complete adding an OpenID
+ *
+ * Handle the return from an OpenID verification
+ *
+ * @category Settings
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ */
+
+class FinishaddopenidAction extends Action
+{
+    var $msg = null;
+
+    /**
+     * Handle the redirect back from OpenID confirmation
+     *
+     * Check to see if the user's logged in, and then try
+     * to use the OpenID login system.
+     *
+     * @param array $args $_REQUEST arguments
+     *
+     * @return void
+     */
+
+    function handle($args)
+    {
+        parent::handle($args);
+        if (!common_logged_in()) {
+            $this->clientError(_('Not logged in.'));
+        } else {
+            $this->tryLogin();
+        }
+    }
+
+    /**
+     * Try to log in using OpenID
+     *
+     * Check the OpenID for validity; potentially store it.
+     *
+     * @return void
+     */
+
+    function tryLogin()
+    {
+        $consumer =& oid_consumer();
+
+        $response = $consumer->complete(common_local_url('finishaddopenid'));
+
+        if ($response->status == Auth_OpenID_CANCEL) {
+            $this->message(_('OpenID authentication cancelled.'));
+            return;
+        } else if ($response->status == Auth_OpenID_FAILURE) {
+            // Authentication failed; display the error message.
+            $this->message(sprintf(_('OpenID authentication failed: %s'),
+                                   $response->message));
+        } else if ($response->status == Auth_OpenID_SUCCESS) {
+
+            $display   = $response->getDisplayIdentifier();
+            $canonical = ($response->endpoint && $response->endpoint->canonicalID) ?
+              $response->endpoint->canonicalID : $display;
+
+            $sreg_resp = Auth_OpenID_SRegResponse::fromSuccessResponse($response);
+
+            if ($sreg_resp) {
+                $sreg = $sreg_resp->contents();
+            }
+
+            $cur =& common_current_user();
+
+            $other = oid_get_user($canonical);
+
+            if ($other) {
+                if ($other->id == $cur->id) {
+                    $this->message(_('You already have this OpenID!'));
+                } else {
+                    $this->message(_('Someone else already has this OpenID.'));
+                }
+                return;
+            }
+
+            // start a transaction
+
+            $cur->query('BEGIN');
+
+            $result = oid_link_user($cur->id, $canonical, $display);
+
+            if (!$result) {
+                $this->message(_('Error connecting user.'));
+                return;
+            }
+            if ($sreg) {
+                if (!oid_update_user($cur, $sreg)) {
+                    $this->message(_('Error updating profile'));
+                    return;
+                }
+            }
+
+            // success!
+
+            $cur->query('COMMIT');
+
+            oid_set_last($display);
+
+            common_redirect(common_local_url('openidsettings'), 303);
+        }
+    }
+
+    /**
+     * Show a failure message
+     *
+     * Something went wrong. Save the message, and show the page.
+     *
+     * @param string $msg Error message to show
+     *
+     * @return void
+     */
+
+    function message($msg)
+    {
+        $this->message = $msg;
+        $this->showPage();
+    }
+
+    /**
+     * Title of the page
+     *
+     * @return string title
+     */
+
+    function title()
+    {
+        return _('OpenID Login');
+    }
+
+    /**
+     * Show error message
+     *
+     * @return void
+     */
+
+    function showPageNotice()
+    {
+        if ($this->message) {
+            $this->element('p', 'error', $this->message);
+        }
+    }
+}
diff --git a/plugins/OpenID/finishopenidlogin.php b/plugins/OpenID/finishopenidlogin.php
new file mode 100644 (file)
index 0000000..50a9c15
--- /dev/null
@@ -0,0 +1,495 @@
+<?php
+/*
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
+
+require_once INSTALLDIR.'/plugins/OpenID/openid.php';
+
+class FinishopenidloginAction extends Action
+{
+    var $error = null;
+    var $username = null;
+    var $message = null;
+
+    function handle($args)
+    {
+        parent::handle($args);
+        if (common_is_real_login()) {
+            $this->clientError(_('Already logged in.'));
+        } else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
+            $token = $this->trimmed('token');
+            if (!$token || $token != common_session_token()) {
+                $this->showForm(_('There was a problem with your session token. Try again, please.'));
+                return;
+            }
+            if ($this->arg('create')) {
+                if (!$this->boolean('license')) {
+                    $this->showForm(_('You can\'t register if you don\'t agree to the license.'),
+                                    $this->trimmed('newname'));
+                    return;
+                }
+                $this->createNewUser();
+            } else if ($this->arg('connect')) {
+                $this->connectUser();
+            } else {
+                common_debug(print_r($this->args, true), __FILE__);
+                $this->showForm(_('Something weird happened.'),
+                                $this->trimmed('newname'));
+            }
+        } else {
+            $this->tryLogin();
+        }
+    }
+
+    function showPageNotice()
+    {
+        if ($this->error) {
+            $this->element('div', array('class' => 'error'), $this->error);
+        } else {
+            $this->element('div', 'instructions',
+                           sprintf(_('This is the first time you\'ve logged into %s so we must connect your OpenID to a local account. You can either create a new account, or connect with your existing account, if you have one.'), common_config('site', 'name')));
+        }
+    }
+
+    function title()
+    {
+        return _('OpenID Account Setup');
+    }
+
+    function showForm($error=null, $username=null)
+    {
+        $this->error = $error;
+        $this->username = $username;
+
+        $this->showPage();
+    }
+
+    function showContent()
+    {
+        if (!empty($this->message_text)) {
+            $this->element('div', array('class' => 'error'), $this->message_text);
+            return;
+        }
+
+        $this->elementStart('form', array('method' => 'post',
+                                          'id' => 'account_connect',
+                                          'action' => common_local_url('finishopenidlogin')));
+        $this->hidden('token', common_session_token());
+        $this->element('h2', null,
+                       _('Create new account'));
+        $this->element('p', null,
+                       _('Create a new user with this nickname.'));
+        $this->input('newname', _('New nickname'),
+                     ($this->username) ? $this->username : '',
+                     _('1-64 lowercase letters or numbers, no punctuation or spaces'));
+        $this->elementStart('p');
+        $this->element('input', array('type' => 'checkbox',
+                                      'id' => 'license',
+                                      'name' => 'license',
+                                      'value' => 'true'));
+        $this->text(_('My text and files are available under '));
+        $this->element('a', array('href' => common_config('license', 'url')),
+                       common_config('license', 'title'));
+        $this->text(_(' except this private data: password, email address, IM address, phone number.'));
+        $this->elementEnd('p');
+        $this->submit('create', _('Create'));
+        $this->element('h2', null,
+                       _('Connect existing account'));
+        $this->element('p', null,
+                       _('If you already have an account, login with your username and password to connect it to your OpenID.'));
+        $this->input('nickname', _('Existing nickname'));
+        $this->password('password', _('Password'));
+        $this->submit('connect', _('Connect'));
+        $this->elementEnd('form');
+    }
+
+    function tryLogin()
+    {
+        $consumer = oid_consumer();
+
+        $response = $consumer->complete(common_local_url('finishopenidlogin'));
+
+        if ($response->status == Auth_OpenID_CANCEL) {
+            $this->message(_('OpenID authentication cancelled.'));
+            return;
+        } else if ($response->status == Auth_OpenID_FAILURE) {
+            // Authentication failed; display the error message.
+            $this->message(sprintf(_('OpenID authentication failed: %s'), $response->message));
+        } else if ($response->status == Auth_OpenID_SUCCESS) {
+            // This means the authentication succeeded; extract the
+            // identity URL and Simple Registration data (if it was
+            // returned).
+            $display = $response->getDisplayIdentifier();
+            $canonical = ($response->endpoint->canonicalID) ?
+              $response->endpoint->canonicalID : $response->getDisplayIdentifier();
+
+            $sreg_resp = Auth_OpenID_SRegResponse::fromSuccessResponse($response);
+
+            if ($sreg_resp) {
+                $sreg = $sreg_resp->contents();
+            }
+
+            $user = oid_get_user($canonical);
+
+            if ($user) {
+                oid_set_last($display);
+                # XXX: commented out at @edd's request until better
+                # control over how data flows from OpenID provider.
+                # oid_update_user($user, $sreg);
+                common_set_user($user);
+                common_real_login(true);
+                if (isset($_SESSION['openid_rememberme']) && $_SESSION['openid_rememberme']) {
+                    common_rememberme($user);
+                }
+                unset($_SESSION['openid_rememberme']);
+                $this->goHome($user->nickname);
+            } else {
+                $this->saveValues($display, $canonical, $sreg);
+                $this->showForm(null, $this->bestNewNickname($display, $sreg));
+            }
+        }
+    }
+
+    function message($msg)
+    {
+        $this->message_text = $msg;
+        $this->showPage();
+    }
+
+    function saveValues($display, $canonical, $sreg)
+    {
+        common_ensure_session();
+        $_SESSION['openid_display'] = $display;
+        $_SESSION['openid_canonical'] = $canonical;
+        $_SESSION['openid_sreg'] = $sreg;
+    }
+
+    function getSavedValues()
+    {
+        return array($_SESSION['openid_display'],
+                     $_SESSION['openid_canonical'],
+                     $_SESSION['openid_sreg']);
+    }
+
+    function createNewUser()
+    {
+        # FIXME: save invite code before redirect, and check here
+
+        if (common_config('site', 'closed')) {
+            $this->clientError(_('Registration not allowed.'));
+            return;
+        }
+
+        $invite = null;
+
+        if (common_config('site', 'inviteonly')) {
+            $code = $_SESSION['invitecode'];
+            if (empty($code)) {
+                $this->clientError(_('Registration not allowed.'));
+                return;
+            }
+
+            $invite = Invitation::staticGet($code);
+
+            if (empty($invite)) {
+                $this->clientError(_('Not a valid invitation code.'));
+                return;
+            }
+        }
+
+        $nickname = $this->trimmed('newname');
+
+        if (!Validate::string($nickname, array('min_length' => 1,
+                                               'max_length' => 64,
+                                               'format' => NICKNAME_FMT))) {
+            $this->showForm(_('Nickname must have only lowercase letters and numbers and no spaces.'));
+            return;
+        }
+
+        if (!User::allowed_nickname($nickname)) {
+            $this->showForm(_('Nickname not allowed.'));
+            return;
+        }
+
+        if (User::staticGet('nickname', $nickname)) {
+            $this->showForm(_('Nickname already in use. Try another one.'));
+            return;
+        }
+
+        list($display, $canonical, $sreg) = $this->getSavedValues();
+
+        if (!$display || !$canonical) {
+            $this->serverError(_('Stored OpenID not found.'));
+            return;
+        }
+
+        # Possible race condition... let's be paranoid
+
+        $other = oid_get_user($canonical);
+
+        if ($other) {
+            $this->serverError(_('Creating new account for OpenID that already has a user.'));
+            return;
+        }
+
+        $location = '';
+        if (!empty($sreg['country'])) {
+            if ($sreg['postcode']) {
+                # XXX: use postcode to get city and region
+                # XXX: also, store postcode somewhere -- it's valuable!
+                $location = $sreg['postcode'] . ', ' . $sreg['country'];
+            } else {
+                $location = $sreg['country'];
+            }
+        }
+
+        if (!empty($sreg['fullname']) && mb_strlen($sreg['fullname']) <= 255) {
+            $fullname = $sreg['fullname'];
+        } else {
+            $fullname = '';
+        }
+
+        if (!empty($sreg['email']) && Validate::email($sreg['email'], true)) {
+            $email = $sreg['email'];
+        } else {
+            $email = '';
+        }
+
+        # XXX: add language
+        # XXX: add timezone
+
+        $args = array('nickname' => $nickname,
+                      'email' => $email,
+                      'fullname' => $fullname,
+                      'location' => $location);
+
+        if (!empty($invite)) {
+            $args['code'] = $invite->code;
+        }
+
+        $user = User::register($args);
+
+        $result = oid_link_user($user->id, $canonical, $display);
+
+        oid_set_last($display);
+        common_set_user($user);
+        common_real_login(true);
+        if (isset($_SESSION['openid_rememberme']) && $_SESSION['openid_rememberme']) {
+            common_rememberme($user);
+        }
+        unset($_SESSION['openid_rememberme']);
+        common_redirect(common_local_url('showstream', array('nickname' => $user->nickname)),
+                        303);
+    }
+
+    function connectUser()
+    {
+        $nickname = $this->trimmed('nickname');
+        $password = $this->trimmed('password');
+
+        if (!common_check_user($nickname, $password)) {
+            $this->showForm(_('Invalid username or password.'));
+            return;
+        }
+
+        # They're legit!
+
+        $user = User::staticGet('nickname', $nickname);
+
+        list($display, $canonical, $sreg) = $this->getSavedValues();
+
+        if (!$display || !$canonical) {
+            $this->serverError(_('Stored OpenID not found.'));
+            return;
+        }
+
+        $result = oid_link_user($user->id, $canonical, $display);
+
+        if (!$result) {
+            $this->serverError(_('Error connecting user to OpenID.'));
+            return;
+        }
+
+        oid_update_user($user, $sreg);
+        oid_set_last($display);
+        common_set_user($user);
+        common_real_login(true);
+        if (isset($_SESSION['openid_rememberme']) && $_SESSION['openid_rememberme']) {
+            common_rememberme($user);
+        }
+        unset($_SESSION['openid_rememberme']);
+        $this->goHome($user->nickname);
+    }
+
+    function goHome($nickname)
+    {
+        $url = common_get_returnto();
+        if ($url) {
+            # We don't have to return to it again
+            common_set_returnto(null);
+        } else {
+            $url = common_local_url('all',
+                                    array('nickname' =>
+                                          $nickname));
+        }
+        common_redirect($url, 303);
+    }
+
+    function bestNewNickname($display, $sreg)
+    {
+
+        # Try the passed-in nickname
+
+        if (!empty($sreg['nickname'])) {
+            $nickname = $this->nicknamize($sreg['nickname']);
+            if ($this->isNewNickname($nickname)) {
+                return $nickname;
+            }
+        }
+
+        # Try the full name
+
+        if (!empty($sreg['fullname'])) {
+            $fullname = $this->nicknamize($sreg['fullname']);
+            if ($this->isNewNickname($fullname)) {
+                return $fullname;
+            }
+        }
+
+        # Try the URL
+
+        $from_url = $this->openidToNickname($display);
+
+        if ($from_url && $this->isNewNickname($from_url)) {
+            return $from_url;
+        }
+
+        # XXX: others?
+
+        return null;
+    }
+
+    function isNewNickname($str)
+    {
+        if (!Validate::string($str, array('min_length' => 1,
+                                          'max_length' => 64,
+                                          'format' => NICKNAME_FMT))) {
+            return false;
+        }
+        if (!User::allowed_nickname($str)) {
+            return false;
+        }
+        if (User::staticGet('nickname', $str)) {
+            return false;
+        }
+        return true;
+    }
+
+    function openidToNickname($openid)
+    {
+        if (Auth_Yadis_identifierScheme($openid) == 'XRI') {
+            return $this->xriToNickname($openid);
+        } else {
+            return $this->urlToNickname($openid);
+        }
+    }
+
+    # We try to use an OpenID URL as a legal StatusNet user name in this order
+    # 1. Plain hostname, like http://evanp.myopenid.com/
+    # 2. One element in path, like http://profile.typekey.com/EvanProdromou/
+    #    or http://getopenid.com/evanprodromou
+
+    function urlToNickname($openid)
+    {
+        static $bad = array('query', 'user', 'password', 'port', 'fragment');
+
+        $parts = parse_url($openid);
+
+        # If any of these parts exist, this won't work
+
+        foreach ($bad as $badpart) {
+            if (array_key_exists($badpart, $parts)) {
+                return null;
+            }
+        }
+
+        # We just have host and/or path
+
+        # If it's just a host...
+        if (array_key_exists('host', $parts) &&
+            (!array_key_exists('path', $parts) || strcmp($parts['path'], '/') == 0))
+        {
+            $hostparts = explode('.', $parts['host']);
+
+            # Try to catch common idiom of nickname.service.tld
+
+            if ((count($hostparts) > 2) &&
+                (strlen($hostparts[count($hostparts) - 2]) > 3) && # try to skip .co.uk, .com.au
+                (strcmp($hostparts[0], 'www') != 0))
+            {
+                return $this->nicknamize($hostparts[0]);
+            } else {
+                # Do the whole hostname
+                return $this->nicknamize($parts['host']);
+            }
+        } else {
+            if (array_key_exists('path', $parts)) {
+                # Strip starting, ending slashes
+                $path = preg_replace('@/$@', '', $parts['path']);
+                $path = preg_replace('@^/@', '', $path);
+                if (strpos($path, '/') === false) {
+                    return $this->nicknamize($path);
+                }
+            }
+        }
+
+        return null;
+    }
+
+    function xriToNickname($xri)
+    {
+        $base = $this->xriBase($xri);
+
+        if (!$base) {
+            return null;
+        } else {
+            # =evan.prodromou
+            # or @gratis*evan.prodromou
+            $parts = explode('*', substr($base, 1));
+            return $this->nicknamize(array_pop($parts));
+        }
+    }
+
+    function xriBase($xri)
+    {
+        if (substr($xri, 0, 6) == 'xri://') {
+            return substr($xri, 6);
+        } else {
+            return $xri;
+        }
+    }
+
+    # Given a string, try to make it work as a nickname
+
+    function nicknamize($str)
+    {
+        $str = preg_replace('/\W/', '', $str);
+        return strtolower($str);
+    }
+}
diff --git a/plugins/OpenID/openid.php b/plugins/OpenID/openid.php
new file mode 100644 (file)
index 0000000..0944117
--- /dev/null
@@ -0,0 +1,280 @@
+<?php
+/*
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
+
+require_once(INSTALLDIR.'/plugins/OpenID/User_openid.php');
+
+require_once('Auth/OpenID.php');
+require_once('Auth/OpenID/Consumer.php');
+require_once('Auth/OpenID/SReg.php');
+require_once('Auth/OpenID/MySQLStore.php');
+
+# About one year cookie expiry
+
+define('OPENID_COOKIE_EXPIRY', round(365.25 * 24 * 60 * 60));
+define('OPENID_COOKIE_KEY', 'lastusedopenid');
+
+function oid_store()
+{
+    static $store = null;
+    if (!$store) {
+        # Can't be called statically
+        $user = new User();
+        $conn = $user->getDatabaseConnection();
+        $store = new Auth_OpenID_MySQLStore($conn);
+    }
+    return $store;
+}
+
+function oid_consumer()
+{
+    $store = oid_store();
+    $consumer = new Auth_OpenID_Consumer($store);
+    return $consumer;
+}
+
+function oid_clear_last()
+{
+    oid_set_last('');
+}
+
+function oid_set_last($openid_url)
+{
+    common_set_cookie(OPENID_COOKIE_KEY,
+                     $openid_url,
+                     time() + OPENID_COOKIE_EXPIRY);
+}
+
+function oid_get_last()
+{
+    if (empty($_COOKIE[OPENID_COOKIE_KEY])) {
+        return null;
+    }
+    $openid_url = $_COOKIE[OPENID_COOKIE_KEY];
+    if ($openid_url && strlen($openid_url) > 0) {
+        return $openid_url;
+    } else {
+        return null;
+    }
+}
+
+function oid_link_user($id, $canonical, $display)
+{
+
+    $oid = new User_openid();
+    $oid->user_id = $id;
+    $oid->canonical = $canonical;
+    $oid->display = $display;
+    $oid->created = DB_DataObject_Cast::dateTime();
+
+    if (!$oid->insert()) {
+        $err = PEAR::getStaticProperty('DB_DataObject','lastError');
+        common_debug('DB error ' . $err->code . ': ' . $err->message, __FILE__);
+        return false;
+    }
+
+    return true;
+}
+
+function oid_get_user($openid_url)
+{
+    $user = null;
+    $oid = User_openid::staticGet('canonical', $openid_url);
+    if ($oid) {
+        $user = User::staticGet('id', $oid->user_id);
+    }
+    return $user;
+}
+
+function oid_check_immediate($openid_url, $backto=null)
+{
+    if (!$backto) {
+        $action = $_REQUEST['action'];
+        $args = common_copy_args($_GET);
+        unset($args['action']);
+        $backto = common_local_url($action, $args);
+    }
+    common_debug('going back to "' . $backto . '"', __FILE__);
+
+    common_ensure_session();
+
+    $_SESSION['openid_immediate_backto'] = $backto;
+    common_debug('passed-in variable is "' . $backto . '"', __FILE__);
+    common_debug('session variable is "' . $_SESSION['openid_immediate_backto'] . '"', __FILE__);
+
+    oid_authenticate($openid_url,
+                     'finishimmediate',
+                     true);
+}
+
+function oid_authenticate($openid_url, $returnto, $immediate=false)
+{
+
+    $consumer = oid_consumer();
+
+    if (!$consumer) {
+        common_server_error(_('Cannot instantiate OpenID consumer object.'));
+        return false;
+    }
+
+    common_ensure_session();
+
+    $auth_request = $consumer->begin($openid_url);
+
+    // Handle failure status return values.
+    if (!$auth_request) {
+        return _('Not a valid OpenID.');
+    } else if (Auth_OpenID::isFailure($auth_request)) {
+        return sprintf(_('OpenID failure: %s'), $auth_request->message);
+    }
+
+    $sreg_request = Auth_OpenID_SRegRequest::build(// Required
+                                                   array(),
+                                                   // Optional
+                                                   array('nickname',
+                                                         'email',
+                                                         'fullname',
+                                                         'language',
+                                                         'timezone',
+                                                         'postcode',
+                                                         'country'));
+
+    if ($sreg_request) {
+        $auth_request->addExtension($sreg_request);
+    }
+
+    $trust_root = common_root_url(true);
+    $process_url = common_local_url($returnto);
+
+    if ($auth_request->shouldSendRedirect()) {
+        $redirect_url = $auth_request->redirectURL($trust_root,
+                                                   $process_url,
+                                                   $immediate);
+        if (!$redirect_url) {
+        } else if (Auth_OpenID::isFailure($redirect_url)) {
+            return sprintf(_('Could not redirect to server: %s'), $redirect_url->message);
+        } else {
+            common_redirect($redirect_url, 303);
+        }
+    } else {
+        // Generate form markup and render it.
+        $form_id = 'openid_message';
+        $form_html = $auth_request->formMarkup($trust_root, $process_url,
+                                               $immediate, array('id' => $form_id));
+
+        # XXX: This is cheap, but things choke if we don't escape ampersands
+        # in the HTML attributes
+
+        $form_html = preg_replace('/&/', '&amp;', $form_html);
+
+        // Display an error if the form markup couldn't be generated;
+        // otherwise, render the HTML.
+        if (Auth_OpenID::isFailure($form_html)) {
+            common_server_error(sprintf(_('Could not create OpenID form: %s'), $form_html->message));
+        } else {
+            $action = new AutosubmitAction(); // see below
+            $action->form_html = $form_html;
+            $action->form_id = $form_id;
+            $action->prepare(array('action' => 'autosubmit'));
+            $action->handle(array('action' => 'autosubmit'));
+        }
+    }
+}
+
+# Half-assed attempt at a module-private function
+
+function _oid_print_instructions()
+{
+    common_element('div', 'instructions',
+                   _('This form should automatically submit itself. '.
+                      'If not, click the submit button to go to your '.
+                      'OpenID provider.'));
+}
+
+# update a user from sreg parameters
+
+function oid_update_user(&$user, &$sreg)
+{
+
+    $profile = $user->getProfile();
+
+    $orig_profile = clone($profile);
+
+    if ($sreg['fullname'] && strlen($sreg['fullname']) <= 255) {
+        $profile->fullname = $sreg['fullname'];
+    }
+
+    if ($sreg['country']) {
+        if ($sreg['postcode']) {
+            # XXX: use postcode to get city and region
+            # XXX: also, store postcode somewhere -- it's valuable!
+            $profile->location = $sreg['postcode'] . ', ' . $sreg['country'];
+        } else {
+            $profile->location = $sreg['country'];
+        }
+    }
+
+    # XXX save language if it's passed
+    # XXX save timezone if it's passed
+
+    if (!$profile->update($orig_profile)) {
+        common_server_error(_('Error saving the profile.'));
+        return false;
+    }
+
+    $orig_user = clone($user);
+
+    if ($sreg['email'] && Validate::email($sreg['email'], true)) {
+        $user->email = $sreg['email'];
+    }
+
+    if (!$user->update($orig_user)) {
+        common_server_error(_('Error saving the user.'));
+        return false;
+    }
+
+    return true;
+}
+
+class AutosubmitAction extends Action
+{
+    var $form_html = null;
+    var $form_id = null;
+
+    function handle($args)
+    {
+        parent::handle($args);
+        $this->showPage();
+    }
+
+    function title()
+    {
+        return _('OpenID Auto-Submit');
+    }
+
+    function showContent()
+    {
+        $this->raw($this->form_html);
+        $this->element('script', null,
+                       '$(document).ready(function() { ' .
+                       '    $(\'#'. $this->form_id .'\').submit(); '.
+                       '});');
+    }
+}
diff --git a/plugins/OpenID/openidlogin.php b/plugins/OpenID/openidlogin.php
new file mode 100644 (file)
index 0000000..29e8923
--- /dev/null
@@ -0,0 +1,137 @@
+<?php
+/*
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
+
+require_once INSTALLDIR.'/plugins/OpenID/openid.php';
+
+class OpenidloginAction extends Action
+{
+    function handle($args)
+    {
+        parent::handle($args);
+        if (common_is_real_login()) {
+            $this->clientError(_('Already logged in.'));
+        } else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
+            $openid_url = $this->trimmed('openid_url');
+
+            # CSRF protection
+            $token = $this->trimmed('token');
+            if (!$token || $token != common_session_token()) {
+                $this->showForm(_('There was a problem with your session token. Try again, please.'), $openid_url);
+                return;
+            }
+
+            $rememberme = $this->boolean('rememberme');
+
+            common_ensure_session();
+
+            $_SESSION['openid_rememberme'] = $rememberme;
+
+            $result = oid_authenticate($openid_url,
+                                       'finishopenidlogin');
+
+            if (is_string($result)) { # error message
+                unset($_SESSION['openid_rememberme']);
+                $this->showForm($result, $openid_url);
+            }
+        } else {
+            $openid_url = oid_get_last();
+            $this->showForm(null, $openid_url);
+        }
+    }
+
+    function getInstructions()
+    {
+        if (common_logged_in() && !common_is_real_login() &&
+            common_get_returnto()) {
+            // rememberme logins have to reauthenticate before
+            // changing any profile settings (cookie-stealing protection)
+            return _('For security reasons, please re-login with your ' .
+                     '[OpenID](%%doc.openid%%) ' .
+                     'before changing your settings.');
+        } else {
+            return _('Login with an [OpenID](%%doc.openid%%) account.');
+        }
+    }
+
+    function showPageNotice()
+    {
+        if ($this->error) {
+            $this->element('div', array('class' => 'error'), $this->error);
+        } else {
+            $instr = $this->getInstructions();
+            $output = common_markup_to_html($instr);
+            $this->elementStart('div', 'instructions');
+            $this->raw($output);
+            $this->elementEnd('div');
+        }
+    }
+
+    function showScripts()
+    {
+        parent::showScripts();
+        $this->autofocus('openid_url');
+    }
+
+    function title()
+    {
+        return _('OpenID Login');
+    }
+
+    function showForm($error=null, $openid_url)
+    {
+        $this->error = $error;
+        $this->openid_url = $openid_url;
+        $this->showPage();
+    }
+
+    function showContent() {
+        $formaction = common_local_url('openidlogin');
+        $this->elementStart('form', array('method' => 'post',
+                                           'id' => 'form_openid_login',
+                                           'class' => 'form_settings',
+                                           'action' => $formaction));
+        $this->elementStart('fieldset');
+        $this->element('legend', null, _('OpenID login'));
+        $this->hidden('token', common_session_token());
+
+        $this->elementStart('ul', 'form_data');
+        $this->elementStart('li');
+        $this->input('openid_url', _('OpenID URL'),
+                     $this->openid_url,
+                     _('Your OpenID URL'));
+        $this->elementEnd('li');
+        $this->elementStart('li', array('id' => 'settings_rememberme'));
+        $this->checkbox('rememberme', _('Remember me'), false,
+                        _('Automatically login in the future; ' .
+                           'not for shared computers!'));
+        $this->elementEnd('li');
+        $this->elementEnd('ul');
+        $this->submit('submit', _('Login'));
+        $this->elementEnd('fieldset');
+        $this->elementEnd('form');
+    }
+
+    function showLocalNav()
+    {
+        $nav = new LoginGroupNav($this);
+        $nav->show();
+    }
+}
diff --git a/plugins/OpenID/openidsettings.php b/plugins/OpenID/openidsettings.php
new file mode 100644 (file)
index 0000000..3ad46f5
--- /dev/null
@@ -0,0 +1,240 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Settings for OpenID
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Settings
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2008-2009 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+    exit(1);
+}
+
+require_once INSTALLDIR.'/lib/accountsettingsaction.php';
+require_once INSTALLDIR.'/plugins/OpenID/openid.php';
+
+/**
+ * Settings for OpenID
+ *
+ * Lets users add, edit and delete OpenIDs from their account
+ *
+ * @category Settings
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ */
+
+class OpenidsettingsAction extends AccountSettingsAction
+{
+    /**
+     * Title of the page
+     *
+     * @return string Page title
+     */
+
+    function title()
+    {
+        return _('OpenID settings');
+    }
+
+    /**
+     * Instructions for use
+     *
+     * @return string Instructions for use
+     */
+
+    function getInstructions()
+    {
+        return _('[OpenID](%%doc.openid%%) lets you log into many sites' .
+                 ' with the same user account.'.
+                 ' Manage your associated OpenIDs from here.');
+    }
+
+    function showScripts()
+    {
+        parent::showScripts();
+        $this->autofocus('openid_url');
+    }
+
+    /**
+     * Show the form for OpenID management
+     *
+     * We have one form with a few different submit buttons to do different things.
+     *
+     * @return void
+     */
+
+    function showContent()
+    {
+        $user = common_current_user();
+
+        $this->elementStart('form', array('method' => 'post',
+                                          'id' => 'form_settings_openid_add',
+                                          'class' => 'form_settings',
+                                          'action' =>
+                                          common_local_url('openidsettings')));
+        $this->elementStart('fieldset', array('id' => 'settings_openid_add'));
+        $this->element('legend', null, _('Add OpenID'));
+        $this->hidden('token', common_session_token());
+        $this->element('p', 'form_guide',
+                       _('If you want to add an OpenID to your account, ' .
+                         'enter it in the box below and click "Add".'));
+        $this->elementStart('ul', 'form_data');
+        $this->elementStart('li');
+        $this->element('label', array('for' => 'openid_url'),
+                       _('OpenID URL'));
+        $this->element('input', array('name' => 'openid_url',
+                                      'type' => 'text',
+                                      'id' => 'openid_url'));
+        $this->elementEnd('li');
+        $this->elementEnd('ul');
+        $this->element('input', array('type' => 'submit',
+                                      'id' => 'settings_openid_add_action-submit',
+                                      'name' => 'add',
+                                      'class' => 'submit',
+                                      'value' => _('Add')));
+        $this->elementEnd('fieldset');
+        $this->elementEnd('form');
+
+        $oid = new User_openid();
+
+        $oid->user_id = $user->id;
+
+        $cnt = $oid->find();
+
+        if ($cnt > 0) {
+
+            $this->element('h2', null, _('Remove OpenID'));
+
+            if ($cnt == 1 && !$user->password) {
+
+                $this->element('p', 'form_guide',
+                               _('Removing your only OpenID '.
+                                 'would make it impossible to log in! ' .
+                                 'If you need to remove it, '.
+                                 'add another OpenID first.'));
+
+                if ($oid->fetch()) {
+                    $this->elementStart('p');
+                    $this->element('a', array('href' => $oid->canonical),
+                                   $oid->display);
+                    $this->elementEnd('p');
+                }
+
+            } else {
+
+                $this->element('p', 'form_guide',
+                               _('You can remove an OpenID from your account '.
+                                 'by clicking the button marked "Remove".'));
+                $idx = 0;
+
+                while ($oid->fetch()) {
+                    $this->elementStart('form',
+                                        array('method' => 'POST',
+                                              'id' => 'form_settings_openid_delete' . $idx,
+                                              'class' => 'form_settings',
+                                              'action' =>
+                                              common_local_url('openidsettings')));
+                    $this->elementStart('fieldset');
+                    $this->hidden('token', common_session_token());
+                    $this->element('a', array('href' => $oid->canonical),
+                                   $oid->display);
+                    $this->element('input', array('type' => 'hidden',
+                                                  'id' => 'openid_url'.$idx,
+                                                  'name' => 'openid_url',
+                                                  'value' => $oid->canonical));
+                    $this->element('input', array('type' => 'submit',
+                                                  'id' => 'remove'.$idx,
+                                                  'name' => 'remove',
+                                                  'class' => 'submit remove',
+                                                  'value' => _('Remove')));
+                    $this->elementEnd('fieldset');
+                    $this->elementEnd('form');
+                    $idx++;
+                }
+            }
+        }
+    }
+
+    /**
+     * Handle a POST request
+     *
+     * Muxes to different sub-functions based on which button was pushed
+     *
+     * @return void
+     */
+
+    function handlePost()
+    {
+        // CSRF protection
+        $token = $this->trimmed('token');
+        if (!$token || $token != common_session_token()) {
+            $this->showForm(_('There was a problem with your session token. '.
+                              'Try again, please.'));
+            return;
+        }
+
+        if ($this->arg('add')) {
+            $result = oid_authenticate($this->trimmed('openid_url'),
+                                       'finishaddopenid');
+            if (is_string($result)) { // error message
+                $this->showForm($result);
+            }
+        } else if ($this->arg('remove')) {
+            $this->removeOpenid();
+        } else {
+            $this->showForm(_('Something weird happened.'));
+        }
+    }
+
+    /**
+     * Handles a request to remove an OpenID from the user's account
+     *
+     * Validates input and, if everything is OK, deletes the OpenID.
+     * Reloads the form with a success or error notification.
+     *
+     * @return void
+     */
+
+    function removeOpenid()
+    {
+        $openid_url = $this->trimmed('openid_url');
+
+        $oid = User_openid::staticGet('canonical', $openid_url);
+
+        if (!$oid) {
+            $this->showForm(_('No such OpenID.'));
+            return;
+        }
+        $cur = common_current_user();
+        if (!$cur || $oid->user_id != $cur->id) {
+            $this->showForm(_('That OpenID does not belong to you.'));
+            return;
+        }
+        $oid->delete();
+        $this->showForm(_('OpenID removed.'), true);
+        return;
+    }
+}
diff --git a/plugins/OpenID/publicxrds.php b/plugins/OpenID/publicxrds.php
new file mode 100644 (file)
index 0000000..1b2b359
--- /dev/null
@@ -0,0 +1,122 @@
+<?php
+
+/**
+ * Public XRDS for OpenID
+ *
+ * PHP version 5
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @author   Robin Millette <millette@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+    exit(1);
+}
+
+require_once INSTALLDIR.'/plugins/OpenID/openid.php';
+
+/**
+ * Public XRDS for OpenID
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @author   Robin Millette <millette@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * @todo factor out similarities with XrdsAction
+ */
+class PublicxrdsAction extends Action
+{
+    /**
+     * Is read only?
+     *
+     * @return boolean true
+     */
+    function isReadOnly($args)
+    {
+        return true;
+    }
+
+    /**
+     * Class handler.
+     *
+     * @param array $args array of arguments
+     *
+     * @return nothing
+     */
+    function handle($args)
+    {
+        parent::handle($args);
+        header('Content-Type: application/xrds+xml');
+        $this->startXML();
+        $this->elementStart('XRDS', array('xmlns' => 'xri://$xrds'));
+        $this->elementStart('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)',
+                                          'xmlns:simple' => 'http://xrds-simple.net/core/1.0',
+                                          'version' => '2.0'));
+        $this->element('Type', null, 'xri://$xrds*simple');
+        foreach (array('finishopenidlogin', 'finishaddopenid') as $finish) {
+            $this->showService(Auth_OpenID_RP_RETURN_TO_URL_TYPE,
+                                common_local_url($finish));
+        }
+        $this->elementEnd('XRD');
+        $this->elementEnd('XRDS');
+        $this->endXML();
+    }
+
+    /**
+     * Show service.
+     *
+     * @param string $type    XRDS type
+     * @param string $uri     URI
+     * @param array  $params  type parameters, null by default
+     * @param array  $sigs    type signatures, null by default
+     * @param string $localId local ID, null by default
+     *
+     * @return void
+     */
+    function showService($type, $uri, $params=null, $sigs=null, $localId=null)
+    {
+        $this->elementStart('Service');
+        if ($uri) {
+            $this->element('URI', null, $uri);
+        }
+        $this->element('Type', null, $type);
+        if ($params) {
+            foreach ($params as $param) {
+                $this->element('Type', null, $param);
+            }
+        }
+        if ($sigs) {
+            foreach ($sigs as $sig) {
+                $this->element('Type', null, $sig);
+            }
+        }
+        if ($localId) {
+            $this->element('LocalID', null, $localId);
+        }
+        $this->elementEnd('Service');
+    }
+}
+
diff --git a/plugins/PtitUrl/PtitUrlPlugin.php b/plugins/PtitUrl/PtitUrlPlugin.php
new file mode 100644 (file)
index 0000000..f00d3e2
--- /dev/null
@@ -0,0 +1,62 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Plugin to push RSS/Atom updates to a PubSubHubBub hub
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Plugin
+ * @package   StatusNet
+ * @author    Craig Andrews <candrews@integralblue.com>
+ * @copyright 2009 Craig Andrews http://candrews.integralblue.com
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+class PtitUrlPlugin extends Plugin
+{
+    function __construct()
+    {
+        parent::__construct();
+    }
+
+    function onInitializePlugin(){
+        $this->registerUrlShortener(
+            'ptiturl.com',
+            array(),
+            array('PtitUrl',array('http://ptiturl.com/?creer=oui&action=Reduire&url='))
+        );
+    }
+}
+
+class PtitUrl extends ShortUrlApi
+{
+    protected function shorten_imp($url) {
+        $response = $this->http_get($url);
+        if (!$response) return $url;
+        $response = $this->tidy($response);
+        $y = @simplexml_load_string($response);
+        if (!isset($y->body)) return $url;
+        $xml = $y->body->center->table->tr->td->pre->a->attributes();
+        if (isset($xml['href'])) return $xml['href'];
+        return $url;
+    }
+}
diff --git a/plugins/PubSubHubBub/PubSubHubBubPlugin.php b/plugins/PubSubHubBub/PubSubHubBubPlugin.php
new file mode 100644 (file)
index 0000000..e1e82e3
--- /dev/null
@@ -0,0 +1,122 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Plugin to push RSS/Atom updates to a PubSubHubBub hub
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Plugin
+ * @package   StatusNet
+ * @author    Craig Andrews <candrews@integralblue.com>
+ * @copyright 2009 Craig Andrews http://candrews.integralblue.com
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+define('DEFAULT_HUB','http://pubsubhubbub.appspot.com');
+
+require_once(INSTALLDIR.'/plugins/PubSubHubBub/publisher.php');
+
+class PubSubHubBubPlugin extends Plugin
+{
+    private $hub;
+
+    function __construct()
+    {
+        parent::__construct();
+    }
+
+    function onInitializePlugin(){
+        $this->hub = common_config('PubSubHubBub', 'hub');
+        if(empty($this->hub)){
+            $this->hub = DEFAULT_HUB;
+        }
+    }
+
+    function onStartApiAtom($action){
+        $action->element('link',array('rel'=>'hub','href'=>$this->hub),null);
+    }
+
+    function onStartApiRss($action){
+        $action->element('atom:link',array('rel'=>'hub','href'=>$this->hub),null);
+    }
+
+    function onHandleQueuedNotice($notice){
+        $publisher = new Publisher($this->hub);
+
+        $feeds = array();
+
+        //public timeline feeds
+        $feeds[]=common_local_url('api',array('apiaction' => 'statuses','method' => 'public_timeline.rss'));
+        $feeds[]=common_local_url('api',array('apiaction' => 'statuses','method' => 'public_timeline.atom'));
+
+        //author's own feeds
+        $user = User::staticGet('id',$notice->profile_id);
+        $feeds[]=common_local_url('api',array('apiaction' => 'statuses','method' => 'user_timeline','argument' => $user->nickname.'.rss'));
+        $feeds[]=common_local_url('api',array('apiaction' => 'statuses','method' => 'user_timeline','argument' => $user->nickname.'.atom'));
+
+        //tag feeds
+        $tag = new Notice_tag();
+        $tag->notice_id = $notice->id;
+        if ($tag->find()) {
+            while ($tag->fetch()) {
+                $feeds[]=common_local_url('api',array('apiaction' => 'tags','method' => 'timeline', 'argument'=>$tag->tag.'.atom'));
+                $feeds[]=common_local_url('api',array('apiaction' => 'tags','method' => 'timeline', 'argument'=>$tag->tag.'.rss'));
+            }
+        }
+
+        //group feeds
+        $group_inbox = new Group_inbox();
+        $group_inbox->notice_id = $notice->id;
+        if ($group_inbox->find()) {
+            while ($group_inbox->fetch()) {
+                $group = User_group::staticGet('id',$group_inbox->group_id);
+                $feeds[]=common_local_url('api',array('apiaction' => 'groups','method' => 'timeline','argument' => $group->nickname.'.rss'));
+                $feeds[]=common_local_url('api',array('apiaction' => 'groups','method' => 'timeline','argument' => $group->nickname.'.atom'));
+            }
+        }
+
+        //feed of each user that subscribes to the notice's author
+        $notice_inbox = new Notice_inbox();
+        $notice_inbox->notice_id = $notice->id;
+        if ($notice_inbox->find()) {
+            while ($notice_inbox->fetch()) {
+                $user = User::staticGet('id',$notice_inbox->user_id);
+                $feeds[]=common_local_url('api',array('apiaction' => 'statuses','method' => 'user_timeline','argument' => $user->nickname.'.rss'));
+                $feeds[]=common_local_url('api',array('apiaction' => 'statuses','method' => 'user_timeline','argument' => $user->nickname.'.atom'));
+            }
+        }
+
+        /* TODO: when the reply page gets RSS and ATOM feeds, implement this
+        //feed of user replied to
+        if($notice->reply_to){
+                $user = User::staticGet('id',$notice->reply_to);
+                $feeds[]=common_local_url('api',array('apiaction' => 'statuses','method' => 'user_timeline','argument' => $user->nickname.'.rss'));
+                $feeds[]=common_local_url('api',array('apiaction' => 'statuses','method' => 'user_timeline','argument' => $user->nickname.'.atom'));
+        }*/
+
+        foreach(array_unique($feeds) as $feed){
+            if(! $publisher->publish_update($feed)){
+                common_log_line(LOG_WARNING,$feed.' was not published to hub at '.$this->hub.':'.$publisher->last_response());
+            }
+        }
+    }
+}
diff --git a/plugins/PubSubHubBub/publisher.php b/plugins/PubSubHubBub/publisher.php
new file mode 100644 (file)
index 0000000..f176a9b
--- /dev/null
@@ -0,0 +1,86 @@
+<?php
+
+// a PHP client library for pubsubhubbub
+// as defined at http://code.google.com/p/pubsubhubbub/
+// written by Josh Fraser | joshfraser.com | josh@eventvue.com
+// Released under Apache License 2.0
+
+class Publisher {
+    
+    protected $hub_url;
+    protected $last_response;
+    
+    // create a new Publisher
+    public function __construct($hub_url) {
+        
+        if (!isset($hub_url))
+            throw new Exception('Please specify a hub url');
+        
+        if (!preg_match("|^https?://|i",$hub_url)) 
+            throw new Exception('The specified hub url does not appear to be valid: '.$hub_url);
+            
+        $this->hub_url = $hub_url;
+    }
+
+    // accepts either a single url or an array of urls
+    public function publish_update($topic_urls, $http_function = false) {
+        if (!isset($topic_urls))
+            throw new Exception('Please specify a topic url');
+        
+        // check that we're working with an array
+        if (!is_array($topic_urls)) {
+            $topic_urls = array($topic_urls);
+        }
+        
+        // set the mode to publish
+        $post_string = "hub.mode=publish";
+        // loop through each topic url 
+        foreach ($topic_urls as $topic_url) {
+
+            // lightweight check that we're actually working w/ a valid url
+            if (!preg_match("|^https?://|i",$topic_url)) 
+                throw new Exception('The specified topic url does not appear to be valid: '.$topic_url);
+            
+            // append the topic url parameters
+            $post_string .= "&hub.url=".urlencode($topic_url);
+        }
+        
+        // make the http post request and return true/false
+        // easy to over-write to use your own http function
+        if ($http_function)
+            return $http_function($this->hub_url,$post_string);
+        else
+            return $this->http_post($this->hub_url,$post_string);
+    }
+
+    // returns any error message from the latest request
+    public function last_response() {
+        return $this->last_response;
+    }
+    
+    // default http function that uses curl to post to the hub endpoint
+    private function http_post($url, $post_string) {
+        
+        // add any additional curl options here
+        $options = array(CURLOPT_URL => $url,
+                         CURLOPT_POST => true,
+                         CURLOPT_POSTFIELDS => $post_string,
+                         CURLOPT_USERAGENT => "PubSubHubbub-Publisher-PHP/1.0");
+
+       $ch = curl_init();
+       curl_setopt_array($ch, $options);
+
+        $response = curl_exec($ch);
+        $this->last_response = $response;
+        $info = curl_getinfo($ch);
+
+        curl_close($ch);
+        
+        // all good
+        if ($info['http_code'] == 204) 
+            return true;
+        return false;  
+    }
+}
+
+?>
\ No newline at end of file
diff --git a/plugins/SimpleUrl/SimpleUrlPlugin.php b/plugins/SimpleUrl/SimpleUrlPlugin.php
new file mode 100644 (file)
index 0000000..82d7720
--- /dev/null
@@ -0,0 +1,79 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Plugin to push RSS/Atom updates to a PubSubHubBub hub
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Plugin
+ * @package   StatusNet
+ * @author    Craig Andrews <candrews@integralblue.com>
+ * @copyright 2009 Craig Andrews http://candrews.integralblue.com
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+class SimpleUrlPlugin extends Plugin
+{
+    function __construct()
+    {
+        parent::__construct();
+    }
+
+    function onInitializePlugin(){
+        $this->registerUrlShortener(
+            'is.gd',
+            array(),
+            array('SimpleUrl',array('http://is.gd/api.php?longurl='))
+        );
+        $this->registerUrlShortener(
+            'snipr.com',
+            array(),
+            array('SimpleUrl',array('http://snipr.com/site/snip?r=simple&link='))
+        );
+        $this->registerUrlShortener(
+            'metamark.net',
+            array(),
+            array('SimpleUrl',array('http://metamark.net/api/rest/simple?long_url='))
+        );
+        $this->registerUrlShortener(
+            'tinyurl.com',
+            array(),
+            array('SimpleUrl',array('http://tinyurl.com/api-create.php?url='))
+        );
+    }
+}
+
+class SimpleUrl extends ShortUrlApi
+{
+    protected function shorten_imp($url) {
+        $curlh = curl_init();
+        curl_setopt($curlh, CURLOPT_CONNECTTIMEOUT, 20); // # seconds to wait
+        curl_setopt($curlh, CURLOPT_USERAGENT, 'StatusNet');
+        curl_setopt($curlh, CURLOPT_RETURNTRANSFER, true);
+
+        curl_setopt($curlh, CURLOPT_URL, $this->service_url.urlencode($url));
+        $short_url = curl_exec($curlh);
+
+        curl_close($curlh);
+        return $short_url;
+    }
+}
diff --git a/plugins/TightUrl/TightUrlPlugin.php b/plugins/TightUrl/TightUrlPlugin.php
new file mode 100644 (file)
index 0000000..48efb35
--- /dev/null
@@ -0,0 +1,62 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Plugin to push RSS/Atom updates to a PubSubHubBub hub
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Plugin
+ * @package   StatusNet
+ * @author    Craig Andrews <candrews@integralblue.com>
+ * @copyright 2009 Craig Andrews http://candrews.integralblue.com
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+class TightUrlPlugin extends Plugin
+{
+    function __construct()
+    {
+        parent::__construct();
+    }
+
+    function onInitializePlugin(){
+        $this->registerUrlShortener(
+            '2tu.us',
+            array('freeService'=>true),
+            array('TightUrl',array('http://2tu.us/?save=y&url='))
+        );
+    }
+}
+
+class TightUrl extends ShortUrlApi
+{
+    protected function shorten_imp($url) {
+        $response = $this->http_get($url);
+        if (!$response) return $url;
+        $response = $this->tidy($response);
+        $y = @simplexml_load_string($response);
+        if (!isset($y->body)) return $url;
+        $xml = $y->body->p[0]->code[0]->a->attributes();
+        if (isset($xml['href'])) return $xml['href'];
+        return $url;
+    }
+}
index 71ed3bf7227ff2feb5ce4427c183c07be44a03f4..1266a9700b674ca58f76551b5417eee55cac5667 100644 (file)
@@ -101,7 +101,7 @@ function newSub($i)
 
     $to = User::staticGet('nickname', $tunic);
 
-    if (empty($from)) {
+    if (empty($to)) {
         throw new Exception("Can't find user '$tunic'.");
     }
 
index 8f48e8e6f04019a105771e6a2232345af4338d36..6dd01971296326e725f71ca5ae749011fd11d0d5 100755 (executable)
@@ -35,20 +35,36 @@ ENDOFHELP;
 
 require_once INSTALLDIR.'/scripts/commandline.inc';
 
+$daemons = array();
+
+$daemons[] = INSTALLDIR.'/scripts/pluginqueuehandler.php';
+$daemons[] = INSTALLDIR.'/scripts/ombqueuehandler.php';
+$daemons[] = INSTALLDIR.'/scripts/facebookqueuehandler.php';
+$daemons[] = INSTALLDIR.'/scripts/pingqueuehandler.php';
+
 if(common_config('xmpp','enabled')) {
-    echo "xmppdaemon.php jabberqueuehandler.php publicqueuehandler.php ";
-    echo "xmppconfirmhandler.php ";
+    $daemons[] = INSTALLDIR.'/scripts/xmppdaemon.php';
+    $daemons[] = INSTALLDIR.'/scripts/jabberqueuehandler.php';
+    $daemons[] = INSTALLDIR.'/scripts/publicqueuehandler.php';
+    $daemons[] = INSTALLDIR.'/scripts/xmppconfirmhandler.php';
 }
+
 if(common_config('twitterbridge','enabled')) {
-    echo "twitterstatusfetcher.php ";
+    $daemons[] = INSTALLDIR.'/scripts/twitterstatusfetcher.php';
 }
-echo "ombqueuehandler.php ";
+
 if (common_config('twitter', 'enabled')) {
-    echo "twitterqueuehandler.php ";
-    echo "synctwitterfriends.php ";
+    $daemons[] = INSTALLDIR.'/scripts/twitterqueuehandler.php';
+    $daemons[] = INSTALLDIR.'/scripts/synctwitterfriends.php';
 }
-echo "facebookqueuehandler.php ";
-echo "pingqueuehandler.php ";
+
 if (common_config('sms', 'enabled')) {
-    echo "smsqueuehandler.php ";
+    $daemons[] = INSTALLDIR.'/scripts/smsqueuehandler.php';
+}
+
+if (Event::handle('GetValidDaemons', array(&$daemons))) {
+    foreach ($daemons as $daemon) {
+        print $daemon . ' ';
+    }
+    print "\n";
 }
index 11911dcbdc6cbaba57d02438bc494a9178302715..586bef624ea154d2a53d60c7a71d7313cd18bf83 100755 (executable)
@@ -66,9 +66,10 @@ class MailerDaemon
         }
         $msg = $this->cleanup_msg($msg);
         $msg = common_shorten_links($msg);
-        if (mb_strlen($msg) > 140) {
-            $this->error($from,_('That\'s too long. '.
-                'Max notice size is 140 chars.'));
+        if (Notice::contentTooLong($msg)) {
+            $this->error($from, sprintf(_('That\'s too long. '.
+                                          'Max notice size is %d chars.'),
+                                        Notice::maxContent()));
         }
         $fileRecords = array();
         foreach($attachments as $attachment){
@@ -78,9 +79,9 @@ class MailerDaemon
                 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.'));
             }
@@ -96,9 +97,10 @@ class MailerDaemon
             $short_fileurl = common_shorten_url($fileurl);
             $msg .= ' ' . $short_fileurl;
 
-            if (mb_strlen($msg) > 140) {
+            if (Notice::contentTooLong($msg)) {
                 $this->deleteFile($filename);
-                $this->error($from,_('Max notice size is 140 chars, including attachment URL.'));
+                $this->error($from, sprintf(_('Max notice size is %d chars, including attachment URL.'),
+                                            Notice::maxContent()));
             }
 
             // Also, not sure this is necessary -- Zach
@@ -123,7 +125,7 @@ class MailerDaemon
         $stream  = stream_get_meta_data($attachment);
         if (copy($stream['uri'], $filepath) && chmod($filepath,0664)) {
             return $filename;
-        } else {   
+        } else {
             $this->error(null,_('File could not be moved to destination directory.' . $stream['uri'] . ' ' . $filepath));
         }
     }
@@ -152,7 +154,7 @@ class MailerDaemon
     }
 
     function maybeAddRedir($file_id, $url)
-    {   
+    {
         $file_redir = File_redirection::staticGet('url', $url);
 
         if (empty($file_redir)) {
@@ -258,10 +260,11 @@ class MailerDaemon
 
     function add_notice($user, $msg, $fileRecords)
     {
-        $notice = Notice::saveNew($user->id, $msg, 'mail');
-        if (is_string($notice)) {
-            $this->log(LOG_ERR, $notice);
-            return $notice;
+        try {
+            $notice = Notice::saveNew($user->id, $msg, 'mail');
+        } catch (Exception $e) {
+            $this->log(LOG_ERR, $e->getMessage());
+            return $e->getMessage();
         }
         foreach($fileRecords as $fileRecord){
             $this->attachFile($notice, $fileRecord);
@@ -273,7 +276,7 @@ class MailerDaemon
     }
 
     function attachFile($notice, $filerec)
-    {   
+    {
         File_to_post::processNew($filerec->id, $notice->id);
 
         $this->maybeAddRedir($filerec->id,
index 8e685f1c8ec33bbfb72495aa7924720117cc26a0..be33b9821801f63b2e2a12b2da9fdcc5a2b9fe1f 100755 (executable)
@@ -57,7 +57,7 @@ class OmbQueueHandler extends QueueHandler
             $this->log(LOG_DEBUG, 'Ignoring remote notice ' . $notice->id);
             return true;
         } else {
-            return omb_broadcast_remote_subscribers($notice);
+            return omb_broadcast_notice($notice);
         }
     }
 
diff --git a/scripts/pluginqueuehandler.php b/scripts/pluginqueuehandler.php
new file mode 100755 (executable)
index 0000000..ae807db
--- /dev/null
@@ -0,0 +1,58 @@
+#!/usr/bin/env php
+<?php
+/*
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
+
+$shortoptions = 'i::';
+$longoptions = array('id::');
+
+$helptext = <<<END_OF_OMB_HELP
+Daemon script for letting plugins handle stuff at queue time
+
+    -i --id           Identity (default none)
+
+END_OF_OMB_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+require_once INSTALLDIR . '/lib/queuehandler.php';
+
+class PluginQueueHandler extends QueueHandler
+{
+
+    function transport()
+    {
+        return 'plugin';
+    }
+
+    function handle_notice($notice)
+    {
+        Event::handle('HandleQueuedNotice', array(&$notice));
+        return true;
+    }
+}
+
+if (have_option('i', 'id')) {
+    $id = get_option_value('i', 'id');
+} else {
+    $id = null;
+}
+
+$handler = new PluginQueueHandler($id);
+$handler->runOnce();
index 298162673c5a80b822e31825b6d39fdcaee2bf81..5fb75414d33322292682c09b91b83c71205fb11d 100755 (executable)
@@ -40,7 +40,7 @@ DAEMONS=`php $DIR/getvaliddaemons.php $ARGSG`
 for f in $DAEMONS; do
 
          printf "Starting $f...";
-        php $DIR/$f $ARGSD
+        php $f $ARGSD
         printf "DONE.\n"
 
 done
index 9e621e725a3fae3697c22cce4e8e8373607e65df..b2efc07c38c644092b4a1549761efb01af99c330 100755 (executable)
@@ -316,17 +316,22 @@ class XMPPDaemon extends Daemon
     {
         $body = trim($pl['body']);
         $content_shortened = common_shorten_links($body);
-        if (mb_strlen($content_shortened) > 140) {
+        if (Notice::contentTooLong($content_shortened)) {
           $from = jabber_normalize_jid($pl['from']);
-          $this->from_site($from, "Message too long - maximum is 140 characters, you sent ".mb_strlen($content_shortened));
+          $this->from_site($from, sprintf(_("Message too long - maximum is %d characters, you sent %d"),
+                                          Notice::maxContent(),
+                                          mb_strlen($content_shortened)));
           return;
         }
-        $notice = Notice::saveNew($user->id, $content_shortened, 'xmpp');
-        if (is_string($notice)) {
-            $this->log(LOG_ERR, $notice);
-            $this->from_site($user->jabber, $notice);
+
+        try {
+            $notice = Notice::saveNew($user->id, $content_shortened, 'xmpp');
+        } catch (Exception $e) {
+            $this->log(LOG_ERR, $e->getMessage());
+            $this->from_site($user->jabber, $e->getMessage());
             return;
         }
+
         common_broadcast_notice($notice);
         $this->log(LOG_INFO,
                    'Added notice ' . $notice->id . ' from user ' . $user->nickname);
index aeac4a5e3f6c932ac0e26a24b2c5610ed3999de1..483d7135e1592b2e9656d037b12fa130b667f695 100644 (file)
@@ -7,6 +7,7 @@ if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
 
 define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
 define('STATUSNET', true);
+define('LACONICA', true);
 
 require_once INSTALLDIR . '/lib/common.php';
 
index 1c3f7cd96f073d64a8eede2527f61af14ef5aaef..45203bf6e3986f73732f35085d74dbabf58a1457 100644 (file)
@@ -7,6 +7,7 @@ if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
 
 define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
 define('STATUSNET', true);
+define('LACONICA', true);
 
 require_once INSTALLDIR . '/lib/common.php';
 
@@ -28,69 +29,71 @@ class URLDetectionTest extends PHPUnit_Framework_TestCase
                      array('not a link :: no way',
                            'not a link :: no way'),
                      array('link http://www.somesite.com/xyz/35637563@N00/52803365/ link',
-                           'link <a href="http://www.somesite.com/xyz/35637563@N00/52803365/" rel="external">http://www.somesite.com/xyz/35637563@N00/52803365/</a> link'),
+                           'link <a href="http://www.somesite.com/xyz/35637563@N00/52803365/" title="http://www.somesite.com/xyz/35637563@N00/52803365/" rel="external">http://www.somesite.com/xyz/35637563@N00/52803365/</a> link'),
                      array('http://127.0.0.1',
-                           '<a href="http://127.0.0.1/" rel="external">http://127.0.0.1</a>'),
+                           '<a href="http://127.0.0.1/" title="http://127.0.0.1/" rel="external">http://127.0.0.1</a>'),
                      array('127.0.0.1',
-                           '<a href="http://127.0.0.1/" rel="external">127.0.0.1</a>'),
+                           '<a href="http://127.0.0.1/" title="http://127.0.0.1/" rel="external">127.0.0.1</a>'),
                      array('127.0.0.1:99',
-                           '<a href="http://127.0.0.1:99/" rel="external">127.0.0.1:99</a>'),
+                           '<a href="http://127.0.0.1:99/" title="http://127.0.0.1:99/" rel="external">127.0.0.1:99</a>'),
                      array('127.0.0.1/Name:test.php',
-                           '<a href="http://127.0.0.1/Name:test.php" rel="external">127.0.0.1/Name:test.php</a>'),
+                           '<a href="http://127.0.0.1/Name:test.php" title="http://127.0.0.1/Name:test.php" rel="external">127.0.0.1/Name:test.php</a>'),
                      array('127.0.0.1/~test',
-                           '<a href="http://127.0.0.1/~test" rel="external">127.0.0.1/~test</a>'),
+                           '<a href="http://127.0.0.1/~test" title="http://127.0.0.1/~test" rel="external">127.0.0.1/~test</a>'),
                      array('127.0.0.1/+test',
-                           '<a href="http://127.0.0.1/+test" rel="external">127.0.0.1/+test</a>'),
+                           '<a href="http://127.0.0.1/+test" title="http://127.0.0.1/+test" rel="external">127.0.0.1/+test</a>'),
                      array('127.0.0.1/$test',
-                           '<a href="http://127.0.0.1/$test" rel="external">127.0.0.1/$test</a>'),
+                           '<a href="http://127.0.0.1/$test" title="http://127.0.0.1/$test" rel="external">127.0.0.1/$test</a>'),
                      array('127.0.0.1/\'test',
-                           '<a href="http://127.0.0.1/\'test" rel="external">127.0.0.1/\'test</a>'),
+                           '<a href="http://127.0.0.1/\'test" title="http://127.0.0.1/\'test" rel="external">127.0.0.1/\'test</a>'),
                      array('127.0.0.1/"test',
-                           '<a href="http://127.0.0.1/&quot;test" rel="external">127.0.0.1/&quot;test</a>'),
+                           '<a href="http://127.0.0.1/&quot;test" title="http://127.0.0.1/&quot;test" rel="external">127.0.0.1/&quot;test</a>'),
                      array('127.0.0.1/-test',
-                           '<a href="http://127.0.0.1/-test" rel="external">127.0.0.1/-test</a>'),
+                           '<a href="http://127.0.0.1/-test" title="http://127.0.0.1/-test" rel="external">127.0.0.1/-test</a>'),
                      array('127.0.0.1/_test',
-                           '<a href="http://127.0.0.1/_test" rel="external">127.0.0.1/_test</a>'),
+                           '<a href="http://127.0.0.1/_test" title="http://127.0.0.1/_test" rel="external">127.0.0.1/_test</a>'),
                      array('127.0.0.1/!test',
-                           '<a href="http://127.0.0.1/!test" rel="external">127.0.0.1/!test</a>'),
+                           '<a href="http://127.0.0.1/!test" title="http://127.0.0.1/!test" rel="external">127.0.0.1/!test</a>'),
                      array('127.0.0.1/*test',
-                           '<a href="http://127.0.0.1/*test" rel="external">127.0.0.1/*test</a>'),
+                           '<a href="http://127.0.0.1/*test" title="http://127.0.0.1/*test" rel="external">127.0.0.1/*test</a>'),
                      array('127.0.0.1/test%20stuff',
-                           '<a href="http://127.0.0.1/test%20stuff" rel="external">127.0.0.1/test%20stuff</a>'),
+                           '<a href="http://127.0.0.1/test%20stuff" title="http://127.0.0.1/test%20stuff" rel="external">127.0.0.1/test%20stuff</a>'),
                      array('http://[::1]:99/test.php',
-                           '<a href="http://[::1]:99/test.php" rel="external">http://[::1]:99/test.php</a>'),
+                           '<a href="http://[::1]:99/test.php" title="http://[::1]:99/test.php" rel="external">http://[::1]:99/test.php</a>'),
                      array('http://::1/test.php',
-                           '<a href="http://::1/test.php" rel="external">http://::1/test.php</a>'),
+                           '<a href="http://::1/test.php" title="http://::1/test.php" rel="external">http://::1/test.php</a>'),
                      array('http://::1',
-                           '<a href="http://::1/" rel="external">http://::1</a>'),
+                           '<a href="http://::1/" title="http://::1/" rel="external">http://::1</a>'),
                      array('2001:4978:1b5:0:21d:e0ff:fe66:59ab/test.php',
-                           '<a href="http://2001:4978:1b5:0:21d:e0ff:fe66:59ab/test.php" rel="external">2001:4978:1b5:0:21d:e0ff:fe66:59ab/test.php</a>'),
+                           '<a href="http://2001:4978:1b5:0:21d:e0ff:fe66:59ab/test.php" title="http://2001:4978:1b5:0:21d:e0ff:fe66:59ab/test.php" rel="external">2001:4978:1b5:0:21d:e0ff:fe66:59ab/test.php</a>'),
                      array('[2001:4978:1b5:0:21d:e0ff:fe66:59ab]:99/test.php',
-                           '<a href="http://[2001:4978:1b5:0:21d:e0ff:fe66:59ab]:99/test.php" rel="external">[2001:4978:1b5:0:21d:e0ff:fe66:59ab]:99/test.php</a>'),
+                           '<a href="http://[2001:4978:1b5:0:21d:e0ff:fe66:59ab]:99/test.php" title="http://[2001:4978:1b5:0:21d:e0ff:fe66:59ab]:99/test.php" rel="external">[2001:4978:1b5:0:21d:e0ff:fe66:59ab]:99/test.php</a>'),
                      array('2001:4978:1b5:0:21d:e0ff:fe66:59ab',
-                           '<a href="http://2001:4978:1b5:0:21d:e0ff:fe66:59ab/" rel="external">2001:4978:1b5:0:21d:e0ff:fe66:59ab</a>'),
+                           '<a href="http://2001:4978:1b5:0:21d:e0ff:fe66:59ab/" title="http://2001:4978:1b5:0:21d:e0ff:fe66:59ab/" rel="external">2001:4978:1b5:0:21d:e0ff:fe66:59ab</a>'),
                      array('http://127.0.0.1',
-                           '<a href="http://127.0.0.1/" rel="external">http://127.0.0.1</a>'),
+                           '<a href="http://127.0.0.1/" title="http://127.0.0.1/" rel="external">http://127.0.0.1</a>'),
                      array('example.com',
-                           '<a href="http://example.com/" rel="external">example.com</a>'),
+                           '<a href="http://example.com/" title="http://example.com/" rel="external">example.com</a>'),
                      array('example.com',
-                           '<a href="http://example.com/" rel="external">example.com</a>'),
+                           '<a href="http://example.com/" title="http://example.com/" rel="external">example.com</a>'),
                      array('http://example.com',
-                           '<a href="http://example.com/" rel="external">http://example.com</a>'),
+                           '<a href="http://example.com/" title="http://example.com/" rel="external">http://example.com</a>'),
                      array('http://example.com.',
-                           '<a href="http://example.com/" rel="external">http://example.com</a>.'),
+                           '<a href="http://example.com/" title="http://example.com/" rel="external">http://example.com</a>.'),
                      array('/var/lib/example.so',
                            '/var/lib/example.so'),
                      array('example',
                            'example'),
                      array('user@example.com',
-                           '<a href="mailto:user@example.com" rel="external">user@example.com</a>'),
+                           '<a href="mailto:user@example.com" title="mailto:user@example.com" rel="external">user@example.com</a>'),
                      array('user_name+other@example.com',
-                           '<a href="mailto:user_name+other@example.com" rel="external">user_name+other@example.com</a>'),
+                           '<a href="mailto:user_name+other@example.com" title="mailto:user_name+other@example.com" rel="external">user_name+other@example.com</a>'),
                      array('mailto:user@example.com',
-                           '<a href="mailto:user@example.com" rel="external">mailto:user@example.com</a>'),
+                           '<a href="mailto:user@example.com" title="mailto:user@example.com" rel="external">mailto:user@example.com</a>'),
                      array('mailto:user@example.com?subject=test',
-                           '<a href="mailto:user@example.com?subject=test" rel="external">mailto:user@example.com?subject=test</a>'),
+                           '<a href="mailto:user@example.com?subject=test" title="mailto:user@example.com?subject=test" rel="external">mailto:user@example.com?subject=test</a>'),
+                     array('xmpp:user@example.com',
+                           '<a href="xmpp:user@example.com" title="xmpp:user@example.com" rel="external">xmpp:user@example.com</a>'),
                      array('#example',
                            '#<span class="tag"><a href="' . common_local_url('tag', array('tag' => common_canonical_tag('example'))) . '" rel="tag">example</a></span>'),
                      array('#example.com',
@@ -98,165 +101,165 @@ class URLDetectionTest extends PHPUnit_Framework_TestCase
                      array('#.net',
                            '#<span class="tag"><a href="' . common_local_url('tag', array('tag' => common_canonical_tag('.net'))) . '" rel="tag">.net</a></span>'),
                      array('http://example',
-                           '<a href="http://example/" rel="external">http://example</a>'),
+                           '<a href="http://example/" title="http://example/" rel="external">http://example</a>'),
                      array('http://3xampl3',
-                           '<a href="http://3xampl3/" rel="external">http://3xampl3</a>'),
+                           '<a href="http://3xampl3/" title="http://3xampl3/" rel="external">http://3xampl3</a>'),
                      array('http://example/',
-                           '<a href="http://example/" rel="external">http://example/</a>'),
+                           '<a href="http://example/" title="http://example/" rel="external">http://example/</a>'),
                      array('http://example/path',
-                           '<a href="http://example/path" rel="external">http://example/path</a>'),
+                           '<a href="http://example/path" title="http://example/path" rel="external">http://example/path</a>'),
                      array('http://example.com',
-                           '<a href="http://example.com/" rel="external">http://example.com</a>'),
+                           '<a href="http://example.com/" title="http://example.com/" rel="external">http://example.com</a>'),
                      array('https://example.com',
-                           '<a href="https://example.com/" rel="external">https://example.com</a>'),
+                           '<a href="https://example.com/" title="https://example.com/" rel="external">https://example.com</a>'),
                      array('ftp://example.com',
-                           '<a href="ftp://example.com/" rel="external">ftp://example.com</a>'),
+                           '<a href="ftp://example.com/" title="ftp://example.com/" rel="external">ftp://example.com</a>'),
                      array('ftps://example.com',
-                           '<a href="ftps://example.com/" rel="external">ftps://example.com</a>'),
+                           '<a href="ftps://example.com/" title="ftps://example.com/" rel="external">ftps://example.com</a>'),
                      array('http://user@example.com',
-                           '<a href="http://user@example.com/" rel="external">http://user@example.com</a>'),
+                           '<a href="http://user@example.com/" title="http://user@example.com/" rel="external">http://user@example.com</a>'),
                      array('http://user:pass@example.com',
-                           '<a href="http://user:pass@example.com/" rel="external">http://user:pass@example.com</a>'),
+                           '<a href="http://user:pass@example.com/" title="http://user:pass@example.com/" rel="external">http://user:pass@example.com</a>'),
                      array('http://example.com:8080',
-                           '<a href="http://example.com:8080/" rel="external">http://example.com:8080</a>'),
+                           '<a href="http://example.com:8080/" title="http://example.com:8080/" rel="external">http://example.com:8080</a>'),
                      array('http://example.com:8080/test.php',
-                           '<a href="http://example.com:8080/test.php" rel="external">http://example.com:8080/test.php</a>'),
+                           '<a href="http://example.com:8080/test.php" title="http://example.com:8080/test.php" rel="external">http://example.com:8080/test.php</a>'),
                      array('example.com:8080/test.php',
-                           '<a href="http://example.com:8080/test.php" rel="external">example.com:8080/test.php</a>'),
+                           '<a href="http://example.com:8080/test.php" title="http://example.com:8080/test.php" rel="external">example.com:8080/test.php</a>'),
                      array('http://www.example.com',
-                           '<a href="http://www.example.com/" rel="external">http://www.example.com</a>'),
+                           '<a href="http://www.example.com/" title="http://www.example.com/" rel="external">http://www.example.com</a>'),
                      array('http://example.com/',
-                           '<a href="http://example.com/" rel="external">http://example.com/</a>'),
+                           '<a href="http://example.com/" title="http://example.com/" rel="external">http://example.com/</a>'),
                      array('http://example.com/path',
-                           '<a href="http://example.com/path" rel="external">http://example.com/path</a>'),
+                           '<a href="http://example.com/path" title="http://example.com/path" rel="external">http://example.com/path</a>'),
                      array('http://example.com/path.html',
-                           '<a href="http://example.com/path.html" rel="external">http://example.com/path.html</a>'),
+                           '<a href="http://example.com/path.html" title="http://example.com/path.html" rel="external">http://example.com/path.html</a>'),
                      array('http://example.com/path.html#fragment',
-                           '<a href="http://example.com/path.html#fragment" rel="external">http://example.com/path.html#fragment</a>'),
+                           '<a href="http://example.com/path.html#fragment" title="http://example.com/path.html#fragment" rel="external">http://example.com/path.html#fragment</a>'),
                      array('http://example.com/path.php?foo=bar&bar=foo',
-                           '<a href="http://example.com/path.php?foo=bar&amp;bar=foo" rel="external">http://example.com/path.php?foo=bar&amp;bar=foo</a>'),
+                           '<a href="http://example.com/path.php?foo=bar&amp;bar=foo" title="http://example.com/path.php?foo=bar&amp;bar=foo" rel="external">http://example.com/path.php?foo=bar&amp;bar=foo</a>'),
                      array('http://example.com.',
-                           '<a href="http://example.com/" rel="external">http://example.com</a>.'),
+                           '<a href="http://example.com/" title="http://example.com/" rel="external">http://example.com</a>.'),
                      array('http://müllärör.de',
-                           '<a href="http://m&#xFC;ll&#xE4;r&#xF6;r.de/" rel="external">http://müllärör.de</a>'),
+                           '<a href="http://m&#xFC;ll&#xE4;r&#xF6;r.de/" title="http://m&#xFC;ll&#xE4;r&#xF6;r.de/" rel="external">http://müllärör.de</a>'),
                      array('http://ﺱﺲﺷ.com',
-                           '<a href="http://&#xFEB1;&#xFEB2;&#xFEB7;.com/" rel="external">http://ﺱﺲﺷ.com</a>'),
+                           '<a href="http://&#xFEB1;&#xFEB2;&#xFEB7;.com/" title="http://&#xFEB1;&#xFEB2;&#xFEB7;.com/" rel="external">http://ﺱﺲﺷ.com</a>'),
                      array('http://сделаткартинки.com',
-                           '<a href="http://&#x441;&#x434;&#x435;&#x43B;&#x430;&#x442;&#x43A;&#x430;&#x440;&#x442;&#x438;&#x43D;&#x43A;&#x438;.com/" rel="external">http://сделаткартинки.com</a>'),
+                           '<a href="http://&#x441;&#x434;&#x435;&#x43B;&#x430;&#x442;&#x43A;&#x430;&#x440;&#x442;&#x438;&#x43D;&#x43A;&#x438;.com/" title="http://&#x441;&#x434;&#x435;&#x43B;&#x430;&#x442;&#x43A;&#x430;&#x440;&#x442;&#x438;&#x43D;&#x43A;&#x438;.com/" rel="external">http://сделаткартинки.com</a>'),
                      array('http://tūdaliņ.lv',
-                           '<a href="http://t&#x16B;dali&#x146;.lv/" rel="external">http://tūdaliņ.lv</a>'),
+                           '<a href="http://t&#x16B;dali&#x146;.lv/" title="http://t&#x16B;dali&#x146;.lv/" rel="external">http://tūdaliņ.lv</a>'),
                      array('http://brændendekærlighed.com',
-                           '<a href="http://br&#xE6;ndendek&#xE6;rlighed.com/" rel="external">http://brændendekærlighed.com</a>'),
+                           '<a href="http://br&#xE6;ndendek&#xE6;rlighed.com/" title="http://br&#xE6;ndendek&#xE6;rlighed.com/" rel="external">http://brændendekærlighed.com</a>'),
                      array('http://あーるいん.com',
-                           '<a href="http://&#x3042;&#x30FC;&#x308B;&#x3044;&#x3093;.com/" rel="external">http://あーるいん.com</a>'),
+                           '<a href="http://&#x3042;&#x30FC;&#x308B;&#x3044;&#x3093;.com/" title="http://&#x3042;&#x30FC;&#x308B;&#x3044;&#x3093;.com/" rel="external">http://あーるいん.com</a>'),
                      array('http://예비교사.com',
-                           '<a href="http://&#xC608;&#xBE44;&#xAD50;&#xC0AC;.com/" rel="external">http://예비교사.com</a>'),
+                           '<a href="http://&#xC608;&#xBE44;&#xAD50;&#xC0AC;.com/" title="http://&#xC608;&#xBE44;&#xAD50;&#xC0AC;.com/" rel="external">http://예비교사.com</a>'),
                      array('http://example.com.',
-                           '<a href="http://example.com/" rel="external">http://example.com</a>.'),
+                           '<a href="http://example.com/" title="http://example.com/" rel="external">http://example.com</a>.'),
                      array('http://example.com?',
-                           '<a href="http://example.com/" rel="external">http://example.com</a>?'),
+                           '<a href="http://example.com/" title="http://example.com/" rel="external">http://example.com</a>?'),
                      array('http://example.com!',
-                           '<a href="http://example.com/" rel="external">http://example.com</a>!'),
+                           '<a href="http://example.com/" title="http://example.com/" rel="external">http://example.com</a>!'),
                      array('http://example.com,',
-                           '<a href="http://example.com/" rel="external">http://example.com</a>,'),
+                           '<a href="http://example.com/" title="http://example.com/" rel="external">http://example.com</a>,'),
                      array('http://example.com;',
-                           '<a href="http://example.com/" rel="external">http://example.com</a>;'),
+                           '<a href="http://example.com/" title="http://example.com/" rel="external">http://example.com</a>;'),
                      array('http://example.com:',
-                           '<a href="http://example.com/" rel="external">http://example.com</a>:'),
+                           '<a href="http://example.com/" title="http://example.com/" rel="external">http://example.com</a>:'),
                      array('\'http://example.com\'',
-                           '\'<a href="http://example.com/" rel="external">http://example.com</a>\''),
+                           '\'<a href="http://example.com/" title="http://example.com/" rel="external">http://example.com</a>\''),
                      array('"http://example.com"',
-                           '&quot;<a href="http://example.com/" rel="external">http://example.com</a>&quot;'),
+                           '&quot;<a href="http://example.com/" title="http://example.com/" rel="external">http://example.com</a>&quot;'),
                      array('http://example.com',
-                           '<a href="http://example.com/" rel="external">http://example.com</a>'),
+                           '<a href="http://example.com/" title="http://example.com/" rel="external">http://example.com</a>'),
                      array('(http://example.com)',
-                           '(<a href="http://example.com/" rel="external">http://example.com</a>)'),
+                           '(<a href="http://example.com/" title="http://example.com/" rel="external">http://example.com</a>)'),
                      array('[http://example.com]',
-                           '[<a href="http://example.com/" rel="external">http://example.com</a>]'),
+                           '[<a href="http://example.com/" title="http://example.com/" rel="external">http://example.com</a>]'),
                      array('<http://example.com>',
-                           '&lt;<a href="http://example.com/" rel="external">http://example.com</a>&gt;'),
+                           '&lt;<a href="http://example.com/" title="http://example.com/" rel="external">http://example.com</a>&gt;'),
                      array('http://example.com/path/(foo)/bar',
-                           '<a href="http://example.com/path/(foo)/bar" rel="external">http://example.com/path/(foo)/bar</a>'),
+                           '<a href="http://example.com/path/(foo)/bar" title="http://example.com/path/(foo)/bar" rel="external">http://example.com/path/(foo)/bar</a>'),
                      array('http://example.com/path/[foo]/bar',
-                           '<a href="http://example.com/path/[foo]/bar" rel="external">http://example.com/path/[foo]/bar</a>'),
+                           '<a href="http://example.com/path/[foo]/bar" title="http://example.com/path/[foo]/bar" rel="external">http://example.com/path/[foo]/bar</a>'),
                      array('http://example.com/path/foo/(bar)',
-                           '<a href="http://example.com/path/foo/(bar)" rel="external">http://example.com/path/foo/(bar)</a>'),
+                           '<a href="http://example.com/path/foo/(bar)" title="http://example.com/path/foo/(bar)" rel="external">http://example.com/path/foo/(bar)</a>'),
                      //Not a valid url - urls cannot contain unencoded square brackets
                      array('http://example.com/path/foo/[bar]',
-                           '<a href="http://example.com/path/foo/[bar]" rel="external">http://example.com/path/foo/[bar]</a>'),
+                           '<a href="http://example.com/path/foo/[bar]" title="http://example.com/path/foo/[bar]" rel="external">http://example.com/path/foo/[bar]</a>'),
                      array('Hey, check out my cool site http://example.com okay?',
-                           'Hey, check out my cool site <a href="http://example.com/" rel="external">http://example.com</a> okay?'),
+                           'Hey, check out my cool site <a href="http://example.com/" title="http://example.com/" rel="external">http://example.com</a> okay?'),
                      array('What about parens (e.g. http://example.com/path/foo/(bar))?',
-                           'What about parens (e.g. <a href="http://example.com/path/foo/(bar)" rel="external">http://example.com/path/foo/(bar)</a>)?'),
+                           'What about parens (e.g. <a href="http://example.com/path/foo/(bar)" title="http://example.com/path/foo/(bar)" rel="external">http://example.com/path/foo/(bar)</a>)?'),
                      array('What about parens (e.g. http://example.com/path/foo/(bar)?',
-                           'What about parens (e.g. <a href="http://example.com/path/foo/(bar)" rel="external">http://example.com/path/foo/(bar)</a>?'),
+                           'What about parens (e.g. <a href="http://example.com/path/foo/(bar)" title="http://example.com/path/foo/(bar)" rel="external">http://example.com/path/foo/(bar)</a>?'),
                      array('What about parens (e.g. http://example.com/path/foo/(bar).)?',
-                           'What about parens (e.g. <a href="http://example.com/path/foo/(bar)" rel="external">http://example.com/path/foo/(bar)</a>.)?'),
+                           'What about parens (e.g. <a href="http://example.com/path/foo/(bar)" title="http://example.com/path/foo/(bar)" rel="external">http://example.com/path/foo/(bar)</a>.)?'),
                      //Not a valid url - urls cannot contain unencoded commas
                      array('What about parens (e.g. http://example.com/path/(foo,bar)?',
-                           'What about parens (e.g. <a href="http://example.com/path/(foo,bar)" rel="external">http://example.com/path/(foo,bar)</a>?'),
+                           'What about parens (e.g. <a href="http://example.com/path/(foo,bar)" title="http://example.com/path/(foo,bar)" rel="external">http://example.com/path/(foo,bar)</a>?'),
                      array('Unbalanced too (e.g. http://example.com/path/((((foo)/bar)?',
-                           'Unbalanced too (e.g. <a href="http://example.com/path/((((foo)/bar)" rel="external">http://example.com/path/((((foo)/bar)</a>?'),
+                           'Unbalanced too (e.g. <a href="http://example.com/path/((((foo)/bar)" title="http://example.com/path/((((foo)/bar)" rel="external">http://example.com/path/((((foo)/bar)</a>?'),
                      array('Unbalanced too (e.g. http://example.com/path/(foo))))/bar)?',
-                           'Unbalanced too (e.g. <a href="http://example.com/path/(foo))))/bar" rel="external">http://example.com/path/(foo))))/bar</a>)?'),
+                           'Unbalanced too (e.g. <a href="http://example.com/path/(foo))))/bar" title="http://example.com/path/(foo))))/bar" rel="external">http://example.com/path/(foo))))/bar</a>)?'),
                      array('Unbalanced too (e.g. http://example.com/path/foo/((((bar)?',
-                           'Unbalanced too (e.g. <a href="http://example.com/path/foo/((((bar)" rel="external">http://example.com/path/foo/((((bar)</a>?'),
+                           'Unbalanced too (e.g. <a href="http://example.com/path/foo/((((bar)" title="http://example.com/path/foo/((((bar)" rel="external">http://example.com/path/foo/((((bar)</a>?'),
                      array('Unbalanced too (e.g. http://example.com/path/foo/(bar))))?',
-                           'Unbalanced too (e.g. <a href="http://example.com/path/foo/(bar)" rel="external">http://example.com/path/foo/(bar)</a>)))?'),
+                           'Unbalanced too (e.g. <a href="http://example.com/path/foo/(bar)" title="http://example.com/path/foo/(bar)" rel="external">http://example.com/path/foo/(bar)</a>)))?'),
                      array('example.com',
-                           '<a href="http://example.com/" rel="external">example.com</a>'),
+                           '<a href="http://example.com/" title="http://example.com/" rel="external">example.com</a>'),
                      array('example.org',
-                           '<a href="http://example.org/" rel="external">example.org</a>'),
+                           '<a href="http://example.org/" title="http://example.org/" rel="external">example.org</a>'),
                      array('example.co.uk',
-                           '<a href="http://example.co.uk/" rel="external">example.co.uk</a>'),
+                           '<a href="http://example.co.uk/" title="http://example.co.uk/" rel="external">example.co.uk</a>'),
                      array('www.example.co.uk',
-                           '<a href="http://www.example.co.uk/" rel="external">www.example.co.uk</a>'),
+                           '<a href="http://www.example.co.uk/" title="http://www.example.co.uk/" rel="external">www.example.co.uk</a>'),
                      array('farm1.images.example.co.uk',
-                           '<a href="http://farm1.images.example.co.uk/" rel="external">farm1.images.example.co.uk</a>'),
+                           '<a href="http://farm1.images.example.co.uk/" title="http://farm1.images.example.co.uk/" rel="external">farm1.images.example.co.uk</a>'),
                      array('example.museum',
-                           '<a href="http://example.museum/" rel="external">example.museum</a>'),
+                           '<a href="http://example.museum/" title="http://example.museum/" rel="external">example.museum</a>'),
                      array('example.travel',
-                           '<a href="http://example.travel/" rel="external">example.travel</a>'),
+                           '<a href="http://example.travel/" title="http://example.travel/" rel="external">example.travel</a>'),
                      array('example.com.',
-                           '<a href="http://example.com/" rel="external">example.com</a>.'),
+                           '<a href="http://example.com/" title="http://example.com/" rel="external">example.com</a>.'),
                      array('example.com?',
-                           '<a href="http://example.com/" rel="external">example.com</a>?'),
+                           '<a href="http://example.com/" title="http://example.com/" rel="external">example.com</a>?'),
                      array('example.com!',
-                           '<a href="http://example.com/" rel="external">example.com</a>!'),
+                           '<a href="http://example.com/" title="http://example.com/" rel="external">example.com</a>!'),
                      array('example.com,',
-                           '<a href="http://example.com/" rel="external">example.com</a>,'),
+                           '<a href="http://example.com/" title="http://example.com/" rel="external">example.com</a>,'),
                      array('example.com;',
-                           '<a href="http://example.com/" rel="external">example.com</a>;'),
+                           '<a href="http://example.com/" title="http://example.com/" rel="external">example.com</a>;'),
                      array('example.com:',
-                           '<a href="http://example.com/" rel="external">example.com</a>:'),
+                           '<a href="http://example.com/" title="http://example.com/" rel="external">example.com</a>:'),
                      array('\'example.com\'',
-                           '\'<a href="http://example.com/" rel="external">example.com</a>\''),
+                           '\'<a href="http://example.com/" title="http://example.com/" rel="external">example.com</a>\''),
                      array('"example.com"',
-                           '&quot;<a href="http://example.com/" rel="external">example.com</a>&quot;'),
+                           '&quot;<a href="http://example.com/" title="http://example.com/" rel="external">example.com</a>&quot;'),
                      array('example.com',
-                           '<a href="http://example.com/" rel="external">example.com</a>'),
+                           '<a href="http://example.com/" title="http://example.com/" rel="external">example.com</a>'),
                      array('(example.com)',
-                           '(<a href="http://example.com/" rel="external">example.com</a>)'),
+                           '(<a href="http://example.com/" title="http://example.com/" rel="external">example.com</a>)'),
                      array('[example.com]',
-                           '[<a href="http://example.com/" rel="external">example.com</a>]'),
+                           '[<a href="http://example.com/" title="http://example.com/" rel="external">example.com</a>]'),
                      array('<example.com>',
-                           '&lt;<a href="http://example.com/" rel="external">example.com</a>&gt;'),
+                           '&lt;<a href="http://example.com/" title="http://example.com/" rel="external">example.com</a>&gt;'),
                      array('Hey, check out my cool site example.com okay?',
-                           'Hey, check out my cool site <a href="http://example.com/" rel="external">example.com</a> okay?'),
+                           'Hey, check out my cool site <a href="http://example.com/" title="http://example.com/" rel="external">example.com</a> okay?'),
                      array('Hey, check out my cool site example.com.I made it.',
-                           'Hey, check out my cool site <a href="http://example.com/" rel="external">example.com</a>.I made it.'),
+                           'Hey, check out my cool site <a href="http://example.com/" title="http://example.com/" rel="external">example.com</a>.I made it.'),
                      array('Hey, check out my cool site example.com.Funny thing...',
-                           'Hey, check out my cool site <a href="http://example.com/" rel="external">example.com</a>.Funny thing...'),
+                           'Hey, check out my cool site <a href="http://example.com/" title="http://example.com/" rel="external">example.com</a>.Funny thing...'),
                      array('Hey, check out my cool site example.com.You will love it.',
-                           'Hey, check out my cool site <a href="http://example.com/" rel="external">example.com</a>.You will love it.'),
+                           'Hey, check out my cool site <a href="http://example.com/" title="http://example.com/" rel="external">example.com</a>.You will love it.'),
                      array('What about parens (e.g. example.com/path/foo/(bar))?',
-                           'What about parens (e.g. <a href="http://example.com/path/foo/(bar)" rel="external">example.com/path/foo/(bar)</a>)?'),
+                           'What about parens (e.g. <a href="http://example.com/path/foo/(bar)" title="http://example.com/path/foo/(bar)" rel="external">example.com/path/foo/(bar)</a>)?'),
                      array('What about parens (e.g. example.com/path/foo/(bar)?',
-                           'What about parens (e.g. <a href="http://example.com/path/foo/(bar)" rel="external">example.com/path/foo/(bar)</a>?'),
+                           'What about parens (e.g. <a href="http://example.com/path/foo/(bar)" title="http://example.com/path/foo/(bar)" rel="external">example.com/path/foo/(bar)</a>?'),
                      array('What about parens (e.g. example.com/path/foo/(bar).)?',
-                           'What about parens (e.g. <a href="http://example.com/path/foo/(bar)" rel="external">example.com/path/foo/(bar)</a>.)?'),
+                           'What about parens (e.g. <a href="http://example.com/path/foo/(bar)" title="http://example.com/path/foo/(bar)" rel="external">example.com/path/foo/(bar)</a>.)?'),
                      array('What about parens (e.g. example.com/path/(foo,bar)?',
-                           'What about parens (e.g. <a href="http://example.com/path/(foo,bar)" rel="external">example.com/path/(foo,bar)</a>?'),
+                           'What about parens (e.g. <a href="http://example.com/path/(foo,bar)" title="http://example.com/path/(foo,bar)" rel="external">example.com/path/(foo,bar)</a>?'),
                      array('file.ext',
                            'file.ext'),
                      array('file.html',
diff --git a/tests/UserRightsTest.php b/tests/UserRightsTest.php
new file mode 100644 (file)
index 0000000..6544ee5
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+
+if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
+    print "This script must be run from the command line\n";
+    exit();
+}
+
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
+define('STATUSNET', true);
+
+require_once INSTALLDIR . '/lib/common.php';
+
+class UserRightsTest extends PHPUnit_Framework_TestCase
+{
+    protected $user = null;
+
+    function setUp()
+    {
+        $this->user = User::register(array('nickname' => 'userrightstestuser'));
+    }
+
+    function tearDown()
+    {
+        $profile = $this->user->getProfile();
+        $this->user->delete();
+        $profile->delete();
+    }
+
+    function testInvalidRole()
+    {
+        $this->assertFalse($this->user->hasRole('invalidrole'));
+    }
+
+    function standardRoles()
+    {
+        return array('admin', 'moderator');
+    }
+
+    /**
+     * @dataProvider standardRoles
+     *
+     */
+
+    function testUngrantedRole($role)
+    {
+        $this->assertFalse($this->user->hasRole($role));
+    }
+
+    /**
+     * @dataProvider standardRoles
+     *
+     */
+
+    function testGrantedRole($role)
+    {
+        $this->user->grantRole($role);
+        $this->assertFalse($this->user->hasRole($role));
+    }
+}
\ No newline at end of file
index 151b1fb71cdc8111cf7c1a03646b08d65015bc57..d030f2db4dc1f0e31adab283c54105b3bf892f64 100644 (file)
@@ -1,7 +1,7 @@
 /** Howto: create a statusnet theme
  *
  * @package   StatusNet
- * @author Sarven Capadisli <csarven@controlyourself.ca>
+ * @author Sarven Capadisli <csarven@status.net>
  * @copyright 2009 Control Yourself, Inc.
  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
  * @link      http://laconi.ca/