]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Merge branch '0.8.x' into 0.9.x
authorEvan Prodromou <evan@status.net>
Thu, 27 Aug 2009 18:16:45 +0000 (11:16 -0700)
committerEvan Prodromou <evan@status.net>
Thu, 27 Aug 2009 18:16:45 +0000 (11:16 -0700)
Conflicts:
EVENTS.txt
actions/finishremotesubscribe.php
actions/postnotice.php
actions/public.php
actions/remotesubscribe.php
actions/showstream.php
actions/updateprofile.php
actions/userauthorization.php
classes/laconica.ini
lib/common.php
lib/oauthstore.php
lib/omb.php

70 files changed:
1  2 
EVENTS.txt
README
actions/accesstoken.php
actions/all.php
actions/confirmaddress.php
actions/doc.php
actions/editgroup.php
actions/favorited.php
actions/finishremotesubscribe.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/postnotice.php
actions/profilesettings.php
actions/public.php
actions/publictagcloud.php
actions/register.php
actions/remotesubscribe.php
actions/replies.php
actions/requesttoken.php
actions/showfavorites.php
actions/showgroup.php
actions/shownotice.php
actions/showstream.php
actions/subscribers.php
actions/twitapidirect_messages.php
actions/twitapistatuses.php
actions/updateprofile.php
actions/userauthorization.php
actions/xrds.php
classes/Notice.php
classes/Profile.php
classes/User.php
classes/statusnet.ini
config.php.sample
db/statusnet.sql
db/statusnet_pg.sql
doc-src/help
index.php
install.php
js/util.js
lib/accountsettingsaction.php
lib/action.php
lib/command.php
lib/common.php
lib/facebookaction.php
lib/groupeditform.php
lib/logingroupnav.php
lib/messageform.php
lib/noticeform.php
lib/oauthstore.php
lib/omb.php
lib/router.php
lib/settingsaction.php
lib/unqueuemanager.php
lib/util.php
plugins/OpenID/finishaddopenid.php
plugins/OpenID/finishopenidlogin.php
plugins/OpenID/openid.php
plugins/OpenID/openidlogin.php
plugins/OpenID/openidsettings.php
plugins/OpenID/publicxrds.php
scripts/maildaemon.php
scripts/ombqueuehandler.php
scripts/xmppdaemon.php

diff --combined EVENTS.txt
index a79687bae180b71511f21a90a425faf3da79823c,68cb28603b644ea092a58a18fe91877c4c4d26af..05d172585549f413a4be6e3ed457ad38044f7d80
@@@ -20,16 -20,22 +20,22 @@@ StartShowStyles: Showing Style links; g
  EndShowStyles: End showing Style links; good place to add custom styles
  - $action: the current action
  
- StartShowLaconicaStyles: Showing Laconica Style links
+ StartShowStatusNetStyles: Showing StatusNet Style links
  - $action: the current action
  
- EndShowLaconicaStyles: End showing Laconica tyle links;  good place to add handheld or JavaScript dependent styles
+ EndShowStatusNetStyles: End showing StatusNet Style links;  good place to add handheld or JavaScript dependant styles
+ - $action: the current action
+ StartShowLaconicaStyles: backwards compatibility; deprecated
+ - $action: the current action
+ 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
@@@ -45,10 -51,16 +51,16 @@@ StartShowJQueryScripts: Showing JQuery 
  EndShowJQueryScripts: End showing JQuery script links
  - $action: the current action
  
- StartShowLaconicaScripts: Showing Laconica script links (use this to link to a CDN or something)
+ StartShowStatusNetScripts: Showing StatusNet script links (use this to link to a CDN or something)
+ - $action: the current action
+ EndShowStatusNetScripts: End showing StatusNet script links
+ - $action: the current action
+ StartShowLaconicaScripts: backwards compatibility; deprecated
  - $action: the current action
  
- EndShowLaconicaScripts: End showing Laconica script links
+ EndShowLaconicaScripts: backwards compatibility; deprecated
  - $action: the current action
  
  StartShowSections: Start the list of sections in the sidebar
@@@ -122,116 -134,3 +134,116 @@@ StartAddressData: Allows the site owne
  
  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
 +
 +StartHeadChildren: called before showing the children of <head> element (after <head> tag)
 +- $action: action object being shown
 +
 +EndHeadChildren: called after showing the children of <head> element (before </head>)
 +- $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
 +
 +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
diff --combined README
index ab25b9f3ffb7de09571d6b6e916ef3f136a0b18b,bfc84662b3fa1c838b4298496b4c62563da2aabc..78d1608a37ca69a880480364b8452ddf42c6c34e
--- 1/README
--- 2/README
+++ b/README
@@@ -2,41 -2,47 +2,47 @@@
  README
  ------
  
Laconica 0.8.0 ("Shiny Happy People")
15 July 2009
StatusNet 0.8.1 ("Second Guessing")
26 Aug 2009
  
- This is the README file for Laconica, the Open Source microblogging
- platform. It includes installation instructions, descriptions of
- options you can set, warnings, tips, and general info for
administrators. Information on using Laconica can be found in the
+ This is the README file for StatusNet (formerly Laconica), the Open
+ Source microblogging platform. It includes installation instructions,
+ descriptions of options you can set, warnings, tips, and general info
for administrators. Information on using StatusNet can be found in the
  "doc" subdirectory or in the "help" section on-line.
  
  About
  =====
  
- Laconica (pronounced "luh-KAWN-ih-kuh") is a Free and Open Source
- microblogging platform. It helps people in a community, company or
- group to exchange short (140 character) messages over the Web. Users
- can choose which people to "follow" and receive only their friends' or
- colleagues' status messages. It provides a similar service to sites
like Twitter, Jaiku and Plurk.
+ StatusNet (formerly Laconica) is a Free and Open Source microblogging
+ platform. It helps people in a community, company or group to exchange
+ short (140 character) messages over the Web. Users can choose which
+ people to "follow" and receive only their friends' or colleagues'
+ status messages. It provides a similar service to sites like Twitter,
Jaiku, Yammer, and Plurk.
  
  With a little work, status messages can be sent to mobile phones,
  instant messenger programs (GTalk/Jabber), and specially-designed
  desktop clients that support the Twitter API.
  
Laconica supports an open standard called OpenMicroBlogging
StatusNet supports an open standard called OpenMicroBlogging
  <http://openmicroblogging.org/> that lets users on different Web sites
  or in different companies subscribe to each others' notices. It
  enables a distributed social network spread all across the Web.
  
Laconica was originally developed for the Open Software Service,
StatusNet was originally developed for the Open Software Service,
  Identi.ca <http://identi.ca/>. It is shared with you in hope that you
  too make an Open Software Service available to your users. To learn
  more, please see the Open Software Service Definition 1.1:
  
        http://www.opendefinition.org/ossd
  
+ StatusNet, Inc. <http://status.net/> also offers this software as a
+ Web service, requiring no installation on your part. The software run
+ on status.net is identical to the software available for download, so
+ you can move back and forth between a hosted version or a version
+ installed on your own servers.
  License
  =======
  
@@@ -56,11 -62,11 +62,11 @@@ License along with this program, in th
  
      IMPORTANT NOTE: The GNU Affero General Public License (AGPL) has
      *different requirements* from the "regular" GPL. In particular, if
-     you make modifications to the Laconica source code on your server,
+     you make modifications to the StatusNet source code on your server,
      you *MUST MAKE AVAILABLE* the modified version of the source code
      to your users under the same license. This is a legal requirement
      of using the software, and if you do not wish to share your
-     modifications, *YOU MAY NOT INSTALL LACONICA*.
+     modifications, *YOU MAY NOT INSTALL STATUSNET*.
  
  Additional library software has been made available in the 'extlib'
  directory. All of it is Free Software and can be distributed under
@@@ -71,62 -77,49 +77,49 @@@ for additional terms
  New this version
  ================
  
- This is a major feature release since version 0.7.4, released May 31
- 2009. Notable changes this version:
- - Support for a hosted service (status network). Multiple sites can
-   share the same codebase but use different databases.
- - OEmbed. Links to pages that support OEmbed (http://www.oembed.com/)
-   become popup links, and the media are shown in a special lightbox.
- - File attachments. Users can attach files of the size and type approved
-   by an administrator, and a shortened link will be included in the
-   notice.
- - Related notices are organized into conversations, with each reply a
-   branch in a tree. Conversations have pages and are linked to from each
-   notice in the conversation.
- - User designs. Users can specify colours and backgrounds
-   for their profile pages and other "personal" pages.
- - Group designs. Group administrators can specify similar designs for
-   group profiles and related pages.
- - Site designs. Site authors can specify a design (background and
-   colors) for the site.
- - New themes. Five new themes are added to the base release; these show
-   off the flexibility of Laconica's theming system.
- - Statistics. Public sites will periodically send usage statistics,
-   configuration options, and dependency information to Laconica dev site.
-   This will help us understand how the software is used and plan future
-   versions of the software.
- - Additional hooks. The hooks and plugins system introduced in 0.7.x was
-   expanded with additional points of access.
- - Facebook Connect. A new plugin allows logging in with Facebook Connect
-   (http://developers.facebook.com/connect.php).
- - A session handler. A new optional session handler class to manage PHP
-   sessions reliably and quickly for large sites.
- - STOMP queuing. Queue management for offline daemons has been
-   abstracted with three concrete instances. A new interface that should
-   work with STOMP servers like ActiveMQ and RabbitMQ is available, which
-   should make things scale better.
- - Group block. Group admins can block users from joining or posting to
-   a group.
- - Group aliases. Groups can be referred to with aliases, additional
-   names. For example, "!yul" and "!montreal" can be the same group.
- - Bidirectional Twitter bridge. Users can read the tweets their Twitter
-   friends post on Twitter.
- - Adaptation of WordPress.com Terms of Service (http://en.wordpress.com/tos/)
-   as default TOS for Laconica sites.
- - Better command-line handling for scripts, including standard options
-   and ability to set hostname and path from the command line.
- - An experimental plugin to use Meteor (http://www.meteorserver.org/)
-   for "real-time" updates.
- - A new framework for "real-time" updates, making it easier to develop
-   plugins for different browser-based update modes.
- - RSS 2.0 and Atom feeds for groups.
- - RSS 2.0 and Atom feeds for tags.
- - Attachments can be sent by email.
- - Attachments are encoded as enclosures in RSS 2.0 and Atom.
- - Notices with attachments display in Facebook as media inline.
- - Many, many bug fixes.
+ This is a minor feature and bugfix release since version 0.8.0,
+ released Jul 15 2009. Notable changes this version:
+ - Laconica has been renamed StatusNet. With a few minor compatibility
+   exceptions, all references to "Laconica" in code, documentation
+   and comments were changed to "StatusNet".
+ - A new plugin to support "infinite scroll".
+ - A new plugin to support reCaptcha <http://recaptcha.net>.
+ - Better logging of server errors.
+ - Add an Openid-only mode for authentication.
+ - 'lite' parameter for some Twitter API methods.
+ - A new plugin to auto-complete nicknames for @-replies.
+ - Configuration options to disable OpenID, SMS, Twitter, post-by-email, and IM.
+ - Support for lighttpd <http://lighttpd.org/> using 404-based
+   rewrites.
+ - Support for using Twitter's OAuth authentication as a client.
+ - First version of the groups API.
+ - Can configure a site-wide design, including background image and
+   colors.
+ - Improved algorithm for replies and conversations, making
+   conversation trees more accurate and useful.
+ - Add a script to create a simulation database for testing/debugging.
+ - Sanitize HTML for OEmbed.
+ - Improved queue management for DB-based queuing.
+ - More complete URL detection.
+ - Hashtags now support full Unicode character set.
+ - Notice inboxes are now garbage-collected on a regular basis
+   at notice-write time.
+ - PiwikAnalyticsPlugin updated for latest Piwik interface.
+ - Attachment and notice pages can be embedded with OEmbed
+   <http://www.oembed.com>.
+ - Failed authentication is logged.
+ - PostgreSQL schema and support brought up-to-date with 0.8.x features.
+ - The installer works with PostgreSQL as well as MySQL.
+ - RSS 1.0 feeds use HTTP Basic authentication in private mode.
+ - Many, many bug fixes, particularly with performance.
+ - Better (=working) garbage collection for old sessions.
+ - Better (=working) search queries.
+ - Some cleanup of HTML output.
+ - Better error handling when updating Facebook.
+ - Considerably better performance when using replication for API
+   calls.
+ - Initial unit tests.
  
  Prerequisites
  =============
@@@ -137,7 -130,7 +130,7 @@@ run correctly
  - PHP 5.2.3+. It may be possible to run this software on earlier
    versions of PHP, but many of the functions used are only available
    in PHP 5.2 or above.
- - MySQL 5.x. The Laconica database is stored, by default, in a MySQL
+ - MySQL 5.x. The StatusNet database is stored, by default, in a MySQL
    server. It has been primarily tested on 5.x servers, although it may
    be possible to install on earlier (or later!) versions. The server
    *must* support the MyISAM storage engine -- the default for most
@@@ -206,7 -199,7 +199,7 @@@ and the URLs are listed here for your c
    as of this writing the version of this library that is available in
    the extlib directory is *significantly different* from the upstream
    version (patches have been submitted). Upgrading to the upstream
-   version may render your Laconica site unable to send or receive XMPP
+   version may render your StatusNet site unable to send or receive XMPP
    messages.
  - Facebook library. Used for the Facebook application.
  - PEAR Services_oEmbed. Used for some multimedia integration.
  - PEAR Net_URL2 is an oEmbed dependency.
  - Console_GetOpt for parsing command-line options.
  
- A design goal of Laconica is that the basic Web functionality should
+ A design goal of StatusNet is that the basic Web functionality should
  work on even the most restrictive commercial hosting services.
  However, additional functionality, such as receiving messages by
  Jabber/GTalk, require that you be able to run long-running processes
@@@ -225,15 -218,15 +218,15 @@@ that you be able to install a mail filt
  Installation
  ============
  
- Installing the basic Laconica Web component is relatively easy,
+ Installing the basic StatusNet Web component is relatively easy,
  especially if you've previously installed PHP/MySQL packages.
  
  1. Unpack the tarball you downloaded on your Web server. Usually a
     command like this will work:
  
-          tar zxf laconica-0.8.0.tar.gz
+          tar zxf statusnet-0.8.1.tar.gz
  
-    ...which will make a laconica-0.8.0 subdirectory in your current
+    ...which will make a statusnet-0.8.1 subdirectory in your current
     directory. (If you don't have shell access on your Web server, you
     may have to unpack the tarball on your local computer and FTP the
     files to the server.)
  2. Move the tarball to a directory of your choosing in your Web root
     directory. Usually something like this will work:
  
-          mv laconica-0.8.0 /var/www/mublog
+          mv statusnet-0.8.1 /var/www/mublog
  
-    This will make your Laconica instance available in the mublog path of
+    This will make your StatusNet instance available in the mublog path of
     your server, like "http://example.net/mublog". "microblog" or
-    "laconica" might also be good path names. If you know how to
+    "statusnet" might also be good path names. If you know how to
     configure virtual hosts on your web server, you can try setting up
     "http://micro.example.net/" or the like.
  
  5. Create a database to hold your microblog data. Something like this
     should work:
  
-         mysqladmin -u "username" --password="password" create laconica
+         mysqladmin -u "username" --password="password" create statusnet
  
-    Note that Laconica must have its own database; you can't share the
+    Note that StatusNet must have its own database; you can't share the
     database with another program. You can name it whatever you want,
     though.
  
     a tool like PHPAdmin to create a database. Check your hosting
     service's documentation for how to create a new MySQL database.)
  
- 6. Create a new database account that Laconica will use to access the
+ 6. Create a new database account that StatusNet will use to access the
     database. If you have shell access, this will probably work from the
     MySQL shell:
  
-           GRANT ALL on laconica.*
+           GRANT ALL on statusnet.*
          TO 'lacuser'@'localhost'
          IDENTIFIED BY 'lacpassword';
  
     username and password. You may want to test logging in to MySQL as
     this new user.
  
- 7. In a browser, navigate to the Laconica install script; something like:
+ 7. In a browser, navigate to the StatusNet install script; something like:
  
             http://yourserver.example.com/mublog/install.php
  
  Fancy URLs
  ----------
  
- By default, Laconica will use URLs that include the main PHP program's
+ By default, StatusNet will use URLs that include the main PHP program's
  name in them. For example, a user's home profile might be
  found at:
  
@@@ -336,7 -329,7 +329,7 @@@ fancy URLs, you must either have Apach
  mod_redirect enabled, -OR- know how to configure "url redirection" in
  your server.
  
- 1. Copy the htaccess.sample file to .htaccess in your Laconica
+ 1. Copy the htaccess.sample file to .htaccess in your StatusNet
     directory. Note: if you have control of your server's httpd.conf or
     similar configuration files, it can greatly improve performance to
     import the .htaccess file into your conf file instead. If you're
     just leaving the .htaccess file.
  
  2. Change the "RewriteBase" in the new .htaccess file to be the URL path
-    to your Laconica installation on your server. Typically this will
-    be the path to your Laconica directory relative to your Web root.
+    to your StatusNet installation on your server. Typically this will
+    be the path to your StatusNet directory relative to your Web root.
  
  3. Add or uncomment or change a line in your config.php file so it says:
  
@@@ -380,7 -373,7 +373,7 @@@ to start and stop the sphinx search dae
  SMS
  ---
  
Laconica supports a cheap-and-dirty system for sending update messages
StatusNet supports a cheap-and-dirty system for sending update messages
  to mobile phones and for receiving updates from the mobile. Instead of
  sending through the SMS network itself, which is costly and requires
  buy-in from the wireless carriers, it simply piggybacks on the email
@@@ -395,10 -388,10 +388,10 @@@ converted to a notice and stored in th
  For this to work, there *must* be a domain or sub-domain for which all
  (or most) incoming email can pass through the incoming mail filter.
  
- 1. Run the SQL script carrier.sql in your Laconica database. This will
+ 1. Run the SQL script carrier.sql in your StatusNet database. This will
     usually work:
  
-          mysql -u "lacuser" --password="lacpassword" laconica < db/carrier.sql
+          mysql -u "lacuser" --password="lacpassword" statusnet < db/carrier.sql
  
     This will populate your database with a list of wireless carriers
     that support email SMS gateways.
  
  2. Edit /etc/aliases on your mail server and add the following line:
  
-       *: /path/to/laconica/scripts/maildaemon.php
+       *: /path/to/statusnet/scripts/maildaemon.php
  
  3. Run whatever code you need to to update your aliases database. For
     many mail servers (Postfix, Exim, Sendmail), this should work:
  
  At this point, post-by-email and post-by-SMS-gateway should work. Note
  that if your mail server is on a different computer from your email
- server, you'll need to have a full installation of Laconica, a working
- config.php, and access to the Laconica database from the mail server.
+ server, you'll need to have a full installation of StatusNet, a working
+ config.php, and access to the StatusNet database from the mail server.
  
  XMPP
  ----
@@@ -449,7 -442,7 +442,7 @@@ well
     similar.  Alternately, your "update JID" can be registered on a
     publicly-available XMPP service, like jabber.org or GTalk.
  
-    Laconica will not register the JID with your chosen XMPP server;
+    StatusNet will not register the JID with your chosen XMPP server;
     you need to do this manually, with an XMPP client like Gajim,
     Telepathy, or Pidgin.im.
  
@@@ -465,7 -458,7 +458,7 @@@ can really slow down your site; it may 
  
  NOTE: stream_select(), a crucial function for network programming, is
  broken on PHP 5.2.x less than 5.2.6 on amd64-based servers. We don't
- work around this bug in Laconica; current recommendation is to move
+ work around this bug in StatusNet; current recommendation is to move
  off of amd64 to another server.
  
  Public feed
@@@ -488,7 -481,7 +481,7 @@@ consider setting up queues and daemons
  Queues and daemons
  ------------------
  
- Some activities that Laconica needs to do, like broadcast OMB, SMS,
+ Some activities that StatusNet needs to do, like broadcast OMB, SMS,
  and XMPP messages, can be 'queued' and done by off-line bots instead.
  For this to work, you must be able to run long-running offline
  processes, either on your main Web server or on another server you
@@@ -499,7 -492,7 +492,7 @@@ server is probably a good idea for high
  1. You'll need the "CLI" (command-line interface) version of PHP
     installed on whatever server you use.
  
- 2. If you're using a separate server for queues, install Laconica
+ 2. If you're using a separate server for queues, install StatusNet
     somewhere on the server. You don't need to worry about the
     .htaccess file, but make sure that your config.php file is close
     to, or identical to, your Web server's version.
  
  4. On the queues server, run the command scripts/startdaemons.sh. It
     needs as a parameter the install path; if you run it from the
-    Laconica dir, "." should suffice.
+    StatusNet dir, "." should suffice.
  
  This will run eight (for now) queue handlers:
  
@@@ -548,7 -541,7 +541,7 @@@ All the daemons write their process ID
  default. This can be useful for starting, stopping, and monitoring the
  daemons.
  
With version 0.8.0, it's now possible to use a STOMP server instead of
Since version 0.8.0, it's now possible to use a STOMP server instead of
  our kind of hacky home-grown DB-based queue solution. See the "queues"
  config section below for how to configure to use STOMP. As of this
  writing, the software has been tested with ActiveMQ (
@@@ -560,7 -553,7 +553,7 @@@ Twitter Bridg
  
  As of 0.8.1, OAuth is used to to access protected resources on Twitter
  instead of HTTP Basic Auth.  To use Twitter bridging you will need
- to register your instance of Laconica as an application on Twitter
+ to register your instance of StatusNet as an application on Twitter
  (http://twitter.com/apps), and update the following variables in your
  config.php with the consumer key and secret Twitter generates for you:
  
@@@ -583,19 -576,19 +576,19 @@@ need to enable the bidirectional Twitte
  
  and run the TwitterStatusFetcher daemon (scripts/twitterstatusfetcher.php).
  Additionally, you will want to set the integration source variable,
- which will keep notices posted to Twitter via Laconica from looping
+ which will keep notices posted to Twitter via StatusNet from looping
  back.  The integration source should be set to the name of your
  application, exactly as you specified it on the settings page for your
Laconica application on Twitter, e.g.:
StatusNet application on Twitter, e.g.:
  
        $config['integration']['source'] = 'YourApp';
  
  * Twitter Friends Syncing
  
  Users may set a flag in their settings ("Subscribe to my Twitter friends
- here" under the Twitter tab) to have Laconica attempt to locate and
+ here" under the Twitter tab) to have StatusNet attempt to locate and
  subscribe to "friends" (people they "follow") on Twitter who also have
- accounts on your Laconica system, and who have previously set up a link
+ accounts on your StatusNet system, and who have previously set up a link
  for automatically posting notices to Twitter.
  
  As of 0.8.0, this is no longer accomplished via a cron job. Instead you
@@@ -604,11 -597,11 +597,11 @@@ must run the SyncTwitterFriends daemon 
  Built-in Facebook Application
  -----------------------------
  
Laconica's Facebook application allows your users to automatically
StatusNet's Facebook application allows your users to automatically
  update their Facebook statuses with their latest notices, invite
  their friends to use the app (and thus your site), view their notice
  timelines, and post notices -- all from within Facebook. The application
- is built into Laconica and runs on your host.  For automatic Facebook
+ is built into StatusNet and runs on your host.  For automatic Facebook
  status updating to work you will need to enable queuing and run the
  facebookqueuehandler.php daemon (see the "Queues and daemons" section
  above).
@@@ -635,7 -628,7 +628,7 @@@ In Facebook's application editor, speci
  - Canvas URL: http://apps.facebook.com/yourapp/
  
  (Replace 'example.net' with your host's URL, 'mublog' with the path
- to your Laconica installation, and 'yourapp' with the name of the
+ to your StatusNet installation, and 'yourapp' with the name of the
  Facebook application you created.)
  
  Additionally, Choose "Web" for Application type in the Advanced tab.
@@@ -644,9 -637,9 +637,9 @@@ In the "Canvas setting" section, choos
  Everything else can be left with default values.
  
  *For more detailed instructions please see the installation guide on the
Laconica wiki:
StatusNet wiki:
  
-     http://laconi.ca/trac/wiki/FacebookApplication
+     http://status.net/trac/wiki/FacebookApplication
  
  Sitemaps
  --------
  Sitemap files <http://sitemaps.org/> are a very nice way of telling
  search engines and other interested bots what's available on your site
  and what's changed recently. You can generate sitemap files for your
Laconica instance.
StatusNet instance.
  
- 1. Choose your sitemap URL layout. Laconica creates a number of
+ 1. Choose your sitemap URL layout. StatusNet creates a number of
     sitemap XML files for different parts of your site. You may want to
-    put these in a sub-directory of your Laconica directory to avoid
+    put these in a sub-directory of your StatusNet directory to avoid
     clutter. The sitemap index file tells the search engines and other
     bots where to find all the sitemap files; it *must* be in the main
     installation directory or higher. Both types of file must be
@@@ -688,7 -681,7 +681,7 @@@ to these resources
  Themes
  ------
  
- There are two themes shipped with this version of Laconica: "identica",
+ There are two themes shipped with this version of StatusNet: "identica",
  which is what the Identi.ca site uses, and "default", which is a good
  basis for other sites.
  
@@@ -719,28 -712,28 +712,28 @@@ default-avatar-mini.png: Ditto ditto, b
  You may want to start by copying the files from the default theme to
  your own directory.
  
- NOTE: the HTML generated by Laconica changed *radically* between
+ NOTE: the HTML generated by StatusNet changed *radically* between
  version 0.6.x and 0.7.x. Older themes will need signification
  modification to use the new output format.
  
  Translation
  -----------
  
- Translations in Laconica use the gettext system <http://www.gnu.org/software/gettext/>.
+ Translations in StatusNet use the gettext system <http://www.gnu.org/software/gettext/>.
  Theoretically, you can add your own sub-directory to the locale/
  subdirectory to add a new language to your system. You'll need to
  compile the ".po" files into ".mo" files, however.
  
- Contributions of translation information to Laconica are very easy:
- you can use the Web interface at http://laconi.ca/pootle/ to add one
+ Contributions of translation information to StatusNet are very easy:
+ you can use the Web interface at http://status.net/pootle/ to add one
  or a few or lots of new translations -- or even new languages. You can
  also download more up-to-date .po files there, if you so desire.
  
  Backups
  -------
  
- There is no built-in system for doing backups in Laconica. You can make
- backups of a working Laconica system by backing up the database and
+ There is no built-in system for doing backups in StatusNet. You can make
+ backups of a working StatusNet system by backing up the database and
  the Web directory. To backup the database use mysqldump <http://ur1.ca/7xo>
  and to backup the Web directory, try tar.
  
@@@ -764,20 -757,20 +757,20 @@@ to users on a remote site. (Or not... i
  Upgrading
  =========
  
- IMPORTANT NOTE: Laconica 0.7.4 introduced a fix for some
+ IMPORTANT NOTE: StatusNet 0.7.4 introduced a fix for some
  incorrectly-stored international characters ("UTF-8"). For new
  installations, it will now store non-ASCII characters correctly.
  However, older installations will have the incorrect storage, and will
  consequently show up "wrong" in browsers. See below for how to deal
  with this situation.
  
- If you've been using Laconica 0.7, 0.6, 0.5 or lower, or if you've
+ If you've been using StatusNet 0.7, 0.6, 0.5 or lower, or if you've
  been tracking the "git" version of the software, you will probably
  want to upgrade and keep your existing data. There is no automated
- upgrade procedure in Laconica 0.8.0. Try these step-by-step
+ upgrade procedure in StatusNet 0.8.1. Try these step-by-step
  instructions; read to the end first before trying them.
  
- 0. Download Laconica and set up all the prerequisites as if you were
+ 0. Download StatusNet and set up all the prerequisites as if you were
     doing a new install.
  1. Make backups of both your database and your Web directory. UNDER NO
     CIRCUMSTANCES should you try to do an upgrade without a known-good
     maildaemon.php file, and running something like "newaliases".
  5. Once all writing processes to your site are turned off, make a
     final backup of the Web directory and database.
- 6. Move your Laconica directory to a backup spot, like "mublog.bak".
- 7. Unpack your Laconica 0.8.0 tarball and move it to "mublog" or
+ 6. Move your StatusNet directory to a backup spot, like "mublog.bak".
+ 7. Unpack your StatusNet 0.8.1 tarball and move it to "mublog" or
     wherever your code used to be.
  8. Copy the config.php file and avatar directory from your old
     directory to your new directory.
  9. Copy htaccess.sample to .htaccess in the new directory. Change the
     RewriteBase to use the correct path.
- 10. Rebuild the database. NOTE: this step is destructive and cannot be
+ 10. Rebuild the database. (You can safely skip this step and go to #12
+     if you're upgrading from another 0.8.x version).
+     NOTE: this step is destructive and cannot be
      reversed. YOU CAN EASILY DESTROY YOUR SITE WITH THIS STEP. Don't
      do it without a known-good backup!
  
  
      mysql -u<rootuser> -p<rootpassword> <database> db/074to080.sql
  
-     Otherwise, go to your Laconica directory and AFTER YOU MAKE A
+     Otherwise, go to your StatusNet directory and AFTER YOU MAKE A
      BACKUP run the rebuilddb.sh script like this:
  
-     ./scripts/rebuilddb.sh rootuser rootpassword database db/laconica.sql
+     ./scripts/rebuilddb.sh rootuser rootpassword database db/statusnet.sql
  
      Here, rootuser and rootpassword are the username and password for a
      user who can drop and create databases as well as tables; typically
-     that's _not_ the user Laconica runs as. Note that rebuilddb.sh drops
+     that's _not_ the user StatusNet runs as. Note that rebuilddb.sh drops
      your database and rebuilds it; if there is an error you have no
      database. Make sure you have a backup.
      For PostgreSQL databases there is an equivalent, rebuilddb_psql.sh,
@@@ -834,8 -830,8 +830,8 @@@ the fixup_* scripts in the scripts dire
  precooked data in the DB. All upgraders should check out the inboxes
  options below.
  
- NOTE: the database definition file, stoica.ini, has been renamed to
laconica.ini (since this is the recommended database name). If you
+ NOTE: the database definition file, laconica.ini, has been renamed to
statusnet.ini (since this is the recommended database name). If you
  have a line in your config.php pointing to the old name, you'll need
  to update it.
  
@@@ -871,13 -867,16 +867,16 @@@ problem
  3. When fixup_inboxes is finished, you can set the enabled flag to
     'true'.
  
+ NOTE: As of version 0.8.1 notice inboxes are automatically trimmed back
+       to ~1000 notices every once in a while.
  NOTE: we will drop support for non-inboxed sites in the 0.9.x version
- of Laconica. It's time to switch now!
+ of StatusNet. It's time to switch now!
  
  UTF-8 Database
  --------------
  
Laconica 0.7.4 introduced a fix for some incorrectly-stored
StatusNet 0.7.4 introduced a fix for some incorrectly-stored
  international characters ("UTF-8"). This fix is not
  backwards-compatible; installations from before 0.7.4 will show
  non-ASCII characters of old notices incorrectly. This section explains
@@@ -900,19 -899,19 +899,19 @@@ what to do
  Configuration options
  =====================
  
- The main configuration file for Laconica (excepting configurations for
- dependency software) is config.php in your Laconica directory. If you
+ The main configuration file for StatusNet (excepting configurations for
+ dependency software) is config.php in your StatusNet directory. If you
  edit any other file in the directory, like lib/common.php (where most
  of the defaults are defined), you will lose your configuration options
  in any upgrade, and you will wish that you had been more careful.
  
  Starting with version 0.7.1, you can put config files in the
- /etc/laconica/ directory on your server, if it exists. Config files
+ /etc/statusnet/ directory on your server, if it exists. Config files
  will be included in this order:
  
- * /etc/laconica/laconica.php - server-wide config
- * /etc/laconica/<servername>.php - for a virtual host
- * /etc/laconica/<servername>_<pathname>.php - for a path
+ * /etc/statusnet/statusnet.php - server-wide config
+ * /etc/statusnet/<servername>.php - for a virtual host
+ * /etc/statusnet/<servername>_<pathname>.php - for a path
  * INSTALLDIR/config.php - for a particular implementation
  
  Almost all configuration options are made through a two-dimensional
@@@ -935,7 -934,7 +934,7 @@@ path: The path part of your site's URLs
        (installed in root).
  fancy: whether or not your site uses fancy URLs (see Fancy URLs
         section above). Default is false.
- logfile: full path to a file for Laconica to save logging
+ logfile: full path to a file for StatusNet to save logging
         information to. You may want to use this if you don't have
         access to syslog.
  logdebug: whether to log additional debug info like backtraces on
@@@ -968,6 -967,8 +967,6 @@@ closed: If set to 'true', will disallo
        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
@@@ -995,9 -996,6 +994,9 @@@ shorturllength: Length of URL at which 
  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
  --
@@@ -1006,14 -1004,14 +1005,14 @@@ This section is a reference to the conf
  DB_DataObject (see <http://ur1.ca/7xp>). The ones that you may want to
  set are listed below for clarity.
  
- database: a DSN (Data Source Name) for your Laconica database. This is
+ database: a DSN (Data Source Name) for your StatusNet database. This is
          in the format 'protocol://username:password@hostname/databasename',
          where 'protocol' is 'mysql' or 'mysqli' (or possibly 'postgresql', if you
          really know what you're doing), 'username' is the username,
          'password' is the password, and etc.
- ini_yourdbname: if your database is not named 'laconica', you'll need
+ ini_yourdbname: if your database is not named 'statusnet', you'll need
                to set this to point to the location of the
-               laconica.ini file. Note that the real name of your database
+               statusnet.ini file. Note that the real name of your database
                should go in there, not literally 'yourdbname'.
  db_driver: You can try changing this to 'MDB2' to use the other driver
           type for DB_DataObject, but note that it breaks the OpenID
@@@ -1041,11 -1039,11 +1040,11 @@@ utf8: whether to talk to the database i
  syslog
  ------
  
- By default, Laconica sites log error messages to the syslog facility.
+ By default, StatusNet sites log error messages to the syslog facility.
  (You can override this using the 'logfile' parameter described above).
  
- appname: The name that Laconica uses to log messages. By default it's
-        "laconica", but if you have more than one installation on the
+ appname: The name that StatusNet uses to log messages. By default it's
+        "statusnet", but if you have more than one installation on the
         server, you may want to change the name for each instance so
         you can track log messages more easily.
  priority: level to log at. Currently ignored.
@@@ -1104,9 -1102,9 +1103,9 @@@ This is for configuring nicknames in th
  
  blacklist: an array of strings for usernames that may not be
           registered. A default array exists for strings that are
-          used by Laconica (e.g. 'doc', 'main', 'avatar', 'theme')
+          used by StatusNet (e.g. 'doc', 'main', 'avatar', 'theme')
           but you may want to add others if you have other software
-          installed in a subdirectory of Laconica or if you just
+          installed in a subdirectory of StatusNet or if you just
           don't want certain words used as usernames.
  featured: an array of nicknames of 'featured' users of the site.
          Can be useful to draw attention to well-known users, or
@@@ -1179,7 -1177,7 +1178,7 @@@ host: some XMPP domains are served by m
        hostname. (For example, @gmail.com GTalk users connect to
        talk.google.com). Set this to the correct hostname if that's the
        case with your server.
- encryption: Whether to encrypt the connection between Laconica and the
+ encryption: Whether to encrypt the connection between StatusNet and the
            XMPP server. Defaults to true, but you can get
            considerably better performance turning it off if you're
            connecting to a server on the same machine or on a
@@@ -1198,6 -1196,14 +1197,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
  ---
  
@@@ -1245,7 -1251,7 +1244,7 @@@ base: memcached uses key-value pairs t
        base of the key is usually a simplified version of the site name
        (like "Identi.ca" => "identica"), but you can overwrite this if
        you need to. You can safely ignore it if you only have one
-       Laconica site using your memcached server.
+       StatusNet site using your memcached server.
  port: Port to connect to; defaults to 11211.
  
  sphinx
@@@ -1289,7 -1295,7 +1288,7 @@@ integratio
  A catch-all for integration with other systems.
  
  source: The name to use for the source of posts to Twitter. Defaults
-       to 'laconica', but if you request your own source name from
+       to 'statusnet', but if you request your own source name from
        Twitter <http://twitter.com/help/request_source>, you can use
        that here instead. Status updates on Twitter will then have
        links to your site.
@@@ -1324,8 -1330,6 +1323,8 @@@ banned: an array of usernames and/or pr
          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
  -------
@@@ -1348,11 -1352,11 +1347,11 @@@ snapsho
  --------
  
  The software will, by default, send statistical snapshots about the
- local installation to a stats server on the laconi.ca Web site. This
+ local installation to a stats server on the status.net Web site. This
  data is used by the developers to prioritize development decisions. No
  identifying data about users or organizations is collected. The data
  is available to the public for review. Participating in this survey
- helps Laconica developers take your needs into account when updating
+ helps StatusNet developers take your needs into account when updating
  the software.
  
  run: string indicating when to run the statistics. Values can be 'web'
@@@ -1363,7 -1367,7 +1362,7 @@@ frequency: if run value is 'web', how o
             Measured in Web hits; depends on how active your site is.
             Default is 10000 -- that is, one report every 10000 Web hits,
             on average.
- reporturl: URL to post statistics to. Defaults to Laconica developers'
+ reporturl: URL to post statistics to. Defaults to StatusNet developers'
             report system, but if they go evil or disappear you may
             need to update this to another value. Note: if you
             don't want to report stats, it's much better to
@@@ -1422,9 -1426,6 +1421,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
  --------
@@@ -1503,28 -1504,10 +1502,28 @@@ 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
  =======
  
- Beginning with the 0.7.x branch, Laconica has supported a simple but
+ Beginning with the 0.7.x branch, StatusNet has supported a simple but
  powerful plugin architecture. Important events in the code are named,
  like 'StartNoticeSave', and other software can register interest
  in those events. When the events happen, the other software is called
@@@ -1560,7 -1543,7 +1559,7 @@@ can enable a plugin with the following 
  
  This will look for and load files named 'ExamplePlugin.php' or
  'Example/ExamplePlugin.php' either in the plugins/ directory (for
- plugins that ship with Laconica) or in the local/ directory (for
+ plugins that ship with StatusNet) or in the local/ directory (for
  plugins you write yourself or that you get from somewhere else) or
  local/plugins/.
  
@@@ -1569,24 -1552,24 +1568,24 @@@ Plugins are documented in their own dir
  Troubleshooting
  ===============
  
- The primary output for Laconica is syslog, unless you configured a
+ The primary output for StatusNet is syslog, unless you configured a
  separate logfile. This is probably the first place to look if you're
- getting weird behaviour from Laconica.
+ getting weird behaviour from StatusNet.
  
- If you're tracking the unstable version of Laconica in the git
+ If you're tracking the unstable version of StatusNet in the git
  repository (see below), and you get a compilation error ("unexpected
  T_STRING") in the browser, check to see that you don't have any
  conflicts in your code.
  
- If you upgraded to Laconica 0.7.4 without reading the "Notice inboxes"
- section above, and all your users' 'Personal' tabs are empty, read the
- "Notice inboxes" section above.
+ If you upgraded to StatusNet 0.8.1 without reading the "Notice
+ inboxes" section above, and all your users' 'Personal' tabs are empty,
read the "Notice inboxes" section above.
  
  Myths
  =====
  
- These are some myths you may see on the Web about Laconica.
- Documentation from the core team about Laconica has been pretty
+ These are some myths you may see on the Web about StatusNet.
+ Documentation from the core team about StatusNet has been pretty
  sparse, so some backtracking and guesswork resulted in some incorrect
  assumptions.
  
  
  - "Edit dataobject.ini with the following settings..." dataobject.ini
    is a development file for the DB_DataObject framework and is not
-   used by the running software. It was removed from the Laconica
+   used by the running software. It was removed from the StatusNet
    distribution because its presence was confusing. Do not bother
    configuring dataobject.ini, and do not put your database username
    and password into the file on a production Web server; unscrupulous
@@@ -1608,45 -1591,55 +1607,55 @@@ Unstable versio
  ================
  
  If you're adventurous or impatient, you may want to install the
- development version of Laconica. To get it, use the git version
+ development version of StatusNet. To get it, use the git version
  control tool <http://git-scm.com/> like so:
  
-       git clone http://laconi.ca/software/laconica.git
+       git clone git@gitorious.org:statusnet/mainline.git
+ This is the version of the software that runs on Identi.ca and the
+ status.net hosted service. Using it is a mixed bag. On the positive
+ side, it usually includes the latest security and bug fix patches. On
+ the downside, it may also include changes that require admin
+ intervention (like running a script or even raw SQL!) that may not be
+ documented yet. It may be a good idea to test this version before
+ installing it on your production machines.
  
  To keep it up-to-date, use 'git pull'. Watch for conflicts!
  
  Further information
  ===================
  
- There are several ways to get more information about Laconica.
+ There are several ways to get more information about StatusNet.
  
- * There is a mailing list for Laconica developers and admins at
-   http://mail.laconi.ca/mailman/listinfo/laconica-dev
- * The #laconica IRC channel on freenode.net <http://www.freenode.net/>.
- * The Laconica wiki, http://laconi.ca/trac/
+ * There is a mailing list for StatusNet developers and admins at
+   http://mail.status.net/mailman/listinfo/statusnet-dev
+ * The #statusnet IRC channel on freenode.net <http://www.freenode.net/>.
+ * The StatusNet wiki, http://status.net/wiki/
+ * The StatusNet blog, http://status.net/blog/
+ * The StatusNet status update, <http://status.status.net/status> (!)
  
  Feedback
  ========
  
  * Microblogging messages to http://identi.ca/evan are very welcome.
- * Laconica's Trac server has a bug tracker for any defects you may find,
-   or ideas for making things better. http://laconi.ca/trac/
- * e-mail to evan@identi.ca will usually be read and responded to very
+ * StatusNet's Trac server has a bug tracker for any defects you may find,
+   or ideas for making things better. http://status.net/trac/
+ * e-mail to evan@status.net will usually be read and responded to very
    quickly, unless the question is really hard.
  
  Credits
  =======
  
  The following is an incomplete list of developers who've worked on
Laconi.ca. Apologies for any oversight; please let evan@identi.ca know
StatusNet. Apologies for any oversight; please let evan@status.net know
  if anyone's been overlooked in error.
  
- * Evan Prodromou, founder and lead developer, Control Yourself, Inc.
- * Zach Copley, Control Yourself, Inc.
- * Earle Martin, Control Yourself, Inc.
- * Marie-Claude Doyon, designer, Control Yourself, Inc.
- * Sarven Capadisli, Control Yourself, Inc.
- * Robin Millette, Control Yourself, Inc.
+ * Evan Prodromou, founder and lead developer, StatusNet, Inc.
+ * Zach Copley, StatusNet, Inc.
+ * Earle Martin, StatusNet, Inc.
+ * Marie-Claude Doyon, designer, StatusNet, Inc.
+ * Sarven Capadisli, StatusNet, Inc.
+ * Robin Millette, StatusNet, Inc.
  * Ciaran Gultnieks
  * Michael Landers
  * Ori Avtalion
  * Craig Andrews
  
  Thanks also to the developers of our upstream library code and to the
- thousands of people who have tried out Identi.ca, installed Laconi.ca,
+ thousands of people who have tried out Identi.ca, installed StatusNet,
  told their friends, and built the Open Microblogging network to what
  it is today.
diff --combined actions/accesstoken.php
index dcd04a1b404f79ad0c8635e080ddd30deea374f3,c99aaeded32ab74b544d1a532ec9630c0a784b3d..76bd40473a954729f43fd2f53bbc852292b35446
@@@ -1,18 -1,18 +1,18 @@@
  <?php
  /**
 - * Access token class.
 + * Access token class
   *
   * PHP version 5
   *
   * @category Action
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
-  * @author   Robin Millette <millette@controlyourself.ca>
+  * @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://laconi.ca/
+  * @link     http://status.net/
   *
-  * Laconica - a distributed open-source microblogging tool
-  * Copyright (C) 2008, 2009, Control Yourself, Inc.
+  * 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
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  
- if (!defined('LACONICA')) {
+ 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  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
-  * @author   Robin Millette <millette@controlyourself.ca>
+  * @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://laconi.ca/
+  * @link     http://status.net/
   */
  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());
          }
      }
  }
 +?>
diff --combined actions/all.php
index 82394922b4bf9c6fbafcaddbf4d3d19937e6a9eb,bfde3a7e4ad051a392841abb910a449ab717dc2a..29a19afb6ba9d27e9b6ab406178312f4443d7752
@@@ -1,7 -1,7 +1,7 @@@
  <?php
  /*
-  * Laconica - a distributed open-source microblogging tool
-  * Copyright (C) 2008, 2009, Control Yourself, Inc.
+  * 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
@@@ -17,7 -17,7 +17,7 @@@
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  
- if (!defined('LACONICA')) { exit(1); }
+ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
  
  require_once INSTALLDIR.'/lib/personalgroupnav.php';
  require_once INSTALLDIR.'/lib/noticelist.php';
@@@ -108,7 -108,9 +108,7 @@@ class AllAction extends ProfileActio
              }
          }
          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 725c1f1e3bb37a3f7da8e03d6f0beef07ba9a295,2016942862197af7def453190a3429ee17436051..6fd74f3ff7889bf40e55d9df9343edecb0d10e6c
@@@ -1,6 -1,6 +1,6 @@@
  <?php
  /**
-  * Laconica, the distributed open-source microblogging tool
+  * StatusNet, the distributed open-source microblogging tool
   *
   * Confirm an address
   *
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   *
   * @category  Confirm
-  * @package   Laconica
-  * @author    Evan Prodromou <evan@controlyourself.ca>
-  * @copyright 2008-2009 Control Yourself, Inc.
+  * @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://laconi.ca/
+  * @link      http://status.net/
   */
  
- if (!defined('LACONICA')) {
+ if (!defined('STATUSNET') && !defined('LACONICA')) {
      exit(1);
  }
  
   * accepts those codes.
   *
   * @category Confirm
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
+  * @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://laconi.ca/
+  * @link     http://status.net/
   */
  
  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');
diff --combined actions/doc.php
index 6f3a87e489fbbe99c4236918e43ade4371f823ad,68295234c5ca1c7a2f6d74fe2f922ac730de9cc5..836f039d32128d9696d5bbc07b598155fd803218
@@@ -6,14 -6,14 +6,14 @@@
   * PHP version 5
   *
   * @category Action
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
-  * @author   Robin Millette <millette@controlyourself.ca>
+  * @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://laconi.ca/
+  * @link     http://status.net/
   *
-  * Laconica - a distributed open-source microblogging tool
-  * Copyright (C) 2008, 2009, Control Yourself, Inc.
+  * 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
@@@ -29,7 -29,7 +29,7 @@@
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  
- if (!defined('LACONICA')) {
+ if (!defined('STATUSNET') && !defined('LACONICA')) {
      exit(1);
  }
  
   * Documentation class.
   *
   * @category Action
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
-  * @author   Robin Millette <millette@controlyourself.ca>
+  * @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://laconi.ca/
+  * @link     http://status.net/
   */
  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();
      }
  
       */
      function showContent()
      {
 -        $c      = file_get_contents($this->filename);
 -        $output = common_markup_to_html($c);
 -        $this->raw($output);
 +        $this->raw($this->output);
      }
  
      /**
diff --combined actions/editgroup.php
index aeeea2b63ce73e3f0aebb675e9f53c0e374079d1,cac910e9bc56d96ca17580ed3a2494ee343ad19a..c92ca6ec65fdfd5c6854016d24c79e83e8de00ae
@@@ -1,6 -1,6 +1,6 @@@
  <?php
  /**
-  * Laconica, the distributed open-source microblogging tool
+  * StatusNet, the distributed open-source microblogging tool
   *
   * Edit an existing group
   *
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   *
   * @category  Group
-  * @package   Laconica
-  * @author    Evan Prodromou <evan@controlyourself.ca>
-  * @author    Sarven Capadisli <csarven@controlyourself.ca>
-  * @author   Zach Copley <zach@controlyourself.ca>
-  * @copyright 2008-2009 Control Yourself, Inc.
+  * @package   StatusNet
+  * @author    Evan Prodromou <evan@status.net>
+  * @author    Sarven Capadisli <csarven@status.net>
+  * @author   Zach Copley <zach@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://laconi.ca/
+  * @link      http://status.net/
   */
  
- if (!defined('LACONICA')) {
+ if (!defined('STATUSNET') && !defined('LACONICA')) {
      exit(1);
  }
  
   * This is the form for adding a new group
   *
   * @category Group
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
-  * @author   Zach Copley <zach@controlyourself.ca>
+  * @package  StatusNet
+  * @author   Evan Prodromou <evan@status.net>
+  * @author   Zach Copley <zach@status.net>
   * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
-  * @link     http://laconi.ca/
+  * @link     http://status.net/
   */
  
  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).'));
diff --combined actions/favorited.php
index 156c7a70094a68513f22ec298d568146a0b0324e,5ba508cdf5e592bfb95ecd16444c0c180fd92cbe..150b67b0b09451f752d226486031de07852e32b7
@@@ -1,6 -1,6 +1,6 @@@
  <?php
  /**
-  * Laconica, the distributed open-source microblogging tool
+  * StatusNet, the distributed open-source microblogging tool
   *
   * List of popular notices
   *
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   *
   * @category  Public
-  * @package   Laconica
-  * @author    Zach Copley <zach@controlyourself.ca>
-  * @author    Evan Prodromou <evan@controlyourself.ca>
-  * @copyright 2008-2009 Control Yourself, Inc.
+  * @package   StatusNet
+  * @author    Zach Copley <zach@status.net>
+  * @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://laconi.ca/
+  * @link      http://status.net/
   */
  
- if (!defined('LACONICA')) {
+ if (!defined('STATUSNET') && !defined('LACONICA')) {
      exit(1);
  }
  
@@@ -42,11 -42,11 +42,11 @@@ require_once INSTALLDIR.'/lib/noticelis
   * is measured by
   *
   * @category Personal
-  * @package  Laconica
-  * @author   Zach Copley <zach@controlyourself.ca>
-  * @author   Evan Prodromou <evan@controlyourself.ca>
+  * @package  StatusNet
+  * @author   Zach Copley <zach@status.net>
+  * @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://laconi.ca/
+  * @link     http://status.net/
   */
  
  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 da563cb29014d020e546441177b61fae10890986,871bc3d2d131c3d8fbd7b1c33deb598ca461ebfc..44abbfceb793c31d9a74119cce11987643246948
@@@ -1,18 -1,7 +1,18 @@@
  <?php
 -/*
 +/**
 + * Handler for remote subscription finish callback
 + *
 + * PHP version 5
 + *
 + * @category Action
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
-  * @author   Robin Millette <millette@controlyourself.ca>
++ * @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://laconi.ca/
++ * @link     http://status.net/
 + *
-  * Laconica - a distributed open-source microblogging tool
-  * Copyright (C) 2008, 2009, Control Yourself, Inc.
+  * 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
   *
   * 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('LACONICA')) {
-     exit(1);
- }
+ 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@controlyourself.ca>
 + * @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');
 -
 -        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__);
 +        common_debug('stored request: '. print_r($service, true), __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'));
 -                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'));
 +        /* 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;
 -            }
 -        } else {
 -            $remote->created = DB_DataObject_Cast::dateTime(); # current time
 -            if (!$remote->insert()) {
 -                $this->serverError(_('Error inserting remote profile'));
 +            } else {
 +                $this->clientError(_('Could not convert request token to ' .
 +                                     'access token.'));
                  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']);
+     }
  }
diff --combined actions/groupsearch.php
index c50466ce62a4f65f77ec730b58b2784756f66613,bbd4c3a74aa89a471be744cf1331701aba11856c..ee4167552a467c8019feaf8fcc2359bc1cab0703
@@@ -5,14 -5,14 +5,14 @@@
   * PHP version 5
   *
   * @category Action
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
-  * @author   Robin Millette <millette@controlyourself.ca>
+  * @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://laconi.ca/
+  * @link     http://status.net/
   *
-  * Laconica - a distributed open-source microblogging tool
-  * Copyright (C) 2008, 2009, Control Yourself, Inc.
+  * 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
@@@ -28,7 -28,7 +28,7 @@@
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  
- if (!defined('LACONICA')) {
+ if (!defined('STATUSNET') && !defined('LACONICA')) {
      exit(1);
  }
  
   * Group search action class.
   *
   * @category Action
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
-  * @author   Robin Millette <millette@controlyourself.ca>
+  * @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://laconi.ca/
+  * @link     http://status.net/
   */
  class GroupsearchAction extends SearchAction
  {
@@@ -82,7 -82,8 +82,7 @@@
                  $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));
diff --combined actions/invite.php
index 26c951ed2233d89da409a5c4e7f90d27ad213614,ab43a2491d581edfb17120d50cbb32f02eb2b65c..9f9368701cbcb1ec279c1ba981671c60cf8b32b3
@@@ -1,7 -1,7 +1,7 @@@
  <?php
  /*
-  * Laconica - a distributed open-source microblogging tool
-  * Copyright (C) 2008, 2009, Control Yourself, Inc.
+  * 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
@@@ -17,7 -17,7 +17,7 @@@
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  
- if (!defined('LACONICA')) { exit(1); }
+ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
  
  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);
      }
diff --combined actions/login.php
index f5a658bf503b2bf3f49a46de4b6bed6a6363a053,37f3c54ffb95b770195cc94033a482ddaa65e46d..4b00b2f08622aa6a98430b822d700ac1edb2e307
@@@ -1,6 -1,6 +1,6 @@@
  <?php
  /**
-  * Laconica, the distributed open-source microblogging tool
+  * StatusNet, the distributed open-source microblogging tool
   *
   * Login form
   *
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   *
   * @category  Login
-  * @package   Laconica
-  * @author    Evan Prodromou <evan@controlyourself.ca>
-  * @copyright 2008-2009 Control Yourself, Inc.
+  * @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://laconi.ca/
+  * @link      http://status.net/
   */
  
- if (!defined('LACONICA')) {
+ if (!defined('STATUSNET') && !defined('LACONICA')) {
      exit(1);
  }
  
   * Login form
   *
   * @category Personal
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
+  * @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://laconi.ca/
+  * @link     http://status.net/
   */
  
  class LoginAction extends Action
@@@ -65,6 -65,8 +65,6 @@@
       *
       * 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
@@@ -73,7 -75,9 +73,7 @@@
      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();
              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? ' .
diff --combined actions/logout.php
index cb20a26b54ecd20e561b769b9c273ac21ec9e77f,298b2a484b58088a76e7bf334a2dc55c746b7dd2..1e0adae57592a536bdaf7b6bbf3eb7ae60959f14
@@@ -5,14 -5,14 +5,14 @@@
   * PHP version 5
   *
   * @category Action
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
-  * @author   Robin Millette <millette@controlyourself.ca>
+  * @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://laconi.ca/
+  * @link     http://status.net/
   *
-  * Laconica - a distributed open-source microblogging tool
-  * Copyright (C) 2008, 2009, Control Yourself, Inc.
+  * 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
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  
- if (!defined('LACONICA')) {
+ if (!defined('STATUSNET') && !defined('LACONICA')) {
      exit(1);
  }
  
 -require_once INSTALLDIR.'/lib/openid.php';
 -
  /**
   * Logout action class.
   *
   * @category Action
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
-  * @author   Robin Millette <millette@controlyourself.ca>
+  * @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://laconi.ca/
+  * @link     http://status.net/
   */
  class LogoutAction extends Action
  {
diff --combined actions/newgroup.php
index 71647d83481adcd3a4a529224f10fd0026cac97d,01cb636aaf80a43555221836eb1485bd7b7e0fa6..a2cf72528b2e182645d588b23c3747bcfc709237
@@@ -1,6 -1,6 +1,6 @@@
  <?php
  /**
-  * Laconica, the distributed open-source microblogging tool
+  * StatusNet, the distributed open-source microblogging tool
   *
   * Add a new group
   *
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   *
   * @category  Group
-  * @package   Laconica
-  * @author    Evan Prodromou <evan@controlyourself.ca>
-  * @author    Sarven Capadisli <csarven@controlyourself.ca>
-  * @copyright 2008-2009 Control Yourself, Inc.
+  * @package   StatusNet
+  * @author    Evan Prodromou <evan@status.net>
+  * @author    Sarven Capadisli <csarven@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://laconi.ca/
+  * @link      http://status.net/
   */
  
- if (!defined('LACONICA')) {
+ if (!defined('STATUSNET') && !defined('LACONICA')) {
      exit(1);
  }
  
   * This is the form for adding a new group
   *
   * @category Group
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
+  * @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://laconi.ca/
+  * @link     http://status.net/
   */
  
  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).'));
diff --combined actions/newmessage.php
index cd26e1640b5b5f64219fd4af82146ad29e06bfbf,828a339cfe1db56578b9b22db06f88e71c9cbba0..a0b17fc18a5ec315eac3c25b13c5c7f1c271dcb7
@@@ -1,6 -1,6 +1,6 @@@
  <?php
  /**
-  * Laconica, the distributed open-source microblogging tool
+  * StatusNet, the distributed open-source microblogging tool
   *
   * Handler for posting new messages
   *
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   *
   * @category  Personal
-  * @package   Laconica
-  * @author    Evan Prodromou <evan@controlyourself.ca>
-  * @author    Zach Copley <zach@controlyourself.ca>
-  * @author    Sarven Capadisli <csarven@controlyourself.ca>
-  * @copyright 2008-2009 Control Yourself, Inc.
+  * @package   StatusNet
+  * @author    Evan Prodromou <evan@status.net>
+  * @author    Zach Copley <zach@status.net>
+  * @author    Sarven Capadisli <csarven@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://laconi.ca/
+  * @link      http://status.net/
   */
  
- if (!defined('LACONICA')) {
+ if (!defined('STATUSNET') && !defined('LACONICA')) {
      exit(1);
  }
  
   * Action for posting new direct messages
   *
   * @category Personal
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
-  * @author   Zach Copley <zach@controlyourself.ca>
-  * @author   Sarven Capadisli <csarven@controlyourself.ca>
+  * @package  StatusNet
+  * @author   Evan Prodromou <evan@status.net>
+  * @author   Zach Copley <zach@status.net>
+  * @author   Sarven Capadisli <csarven@status.net>
   * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
-  * @link     http://laconi.ca/
+  * @link     http://status.net/
   */
  
  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;
              }
          }
diff --combined actions/newnotice.php
index 049d7c322982aadfa3cff3f8d51eeb16d78b1e5c,00a822860e5065a442eb40431b62fa87fec72c0f..4c6372c2bb96eac658aa94be085ee05c29ce4d49
@@@ -1,6 -1,6 +1,6 @@@
  <?php
  /**
-  * Laconica, the distributed open-source microblogging tool
+  * StatusNet, the distributed open-source microblogging tool
   *
   * Handler for posting new notices
   *
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   *
   * @category  Personal
-  * @package   Laconica
-  * @author    Evan Prodromou <evan@controlyourself.ca>
-  * @author    Zach Copley <zach@controlyourself.ca>
-  * @author    Sarven Capadisli <csarven@controlyourself.ca>
-  * @copyright 2008-2009 Control Yourself, Inc.
+  * @package   StatusNet
+  * @author    Evan Prodromou <evan@status.net>
+  * @author    Zach Copley <zach@status.net>
+  * @author    Sarven Capadisli <csarven@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://laconi.ca/
+  * @link      http://status.net/
   */
  
- if (!defined('LACONICA')) {
+ if (!defined('STATUSNET') && !defined('LACONICA')) {
      exit(1);
  }
  
@@@ -39,12 -39,12 +39,12 @@@ require_once INSTALLDIR.'/lib/noticelis
   * Action for posting new notices
   *
   * @category Personal
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
-  * @author   Zach Copley <zach@controlyourself.ca>
-  * @author   Sarven Capadisli <csarven@controlyourself.ca>
+  * @package  StatusNet
+  * @author   Evan Prodromou <evan@status.net>
+  * @author   Zach Copley <zach@status.net>
+  * @author   Sarven Capadisli <csarven@status.net>
   * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
-  * @link     http://laconi.ca/
+  * @link     http://status.net/
   */
  
  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()));
              }
          }
  
              $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
diff --combined actions/noticesearch.php
index 49b473d9e9cd542c03be950ff5fc93a60afb111b,1188e7e10fb3f881876e5e9eb37bcf0a9c230800..734e43464800df221f2862671919b28a73c0c71a
@@@ -5,15 -5,15 +5,15 @@@
   * PHP version 5
   *
   * @category Action
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
-  * @author   Robin Millette <millette@controlyourself.ca>
-  * @author   Sarven Capadisli <csarven@controlyourself.ca>
+  * @package  StatusNet
+  * @author   Evan Prodromou <evan@status.net>
+  * @author   Robin Millette <millette@status.net>
+  * @author   Sarven Capadisli <csarven@status.net>
   * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
-  * @link     http://laconi.ca/
+  * @link     http://status.net/
   *
-  * Laconica - a distributed open-source microblogging tool
-  * Copyright (C) 2008, 2009, Control Yourself, Inc.
+  * 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
@@@ -29,7 -29,7 +29,7 @@@
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  
- if (!defined('LACONICA')) {
+ if (!defined('STATUSNET') && !defined('LACONICA')) {
      exit(1);
  }
  
@@@ -39,11 -39,11 +39,11 @@@ require_once INSTALLDIR.'/lib/searchact
   * Notice search action class.
   *
   * @category Action
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
-  * @author   Robin Millette <millette@controlyourself.ca>
+  * @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://laconi.ca/
+  * @link     http://status.net/
   * @todo     common parent for people and content search?
   */
  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 --combined actions/postnotice.php
index 14152a83d64d36387445e2fdf3cf348ca33a179a,e775ca17e83e6359ebefefe3711d86a13c485a8f..c2e1c44cae0b4a84099663aee85d954befa4edaf
@@@ -1,18 -1,7 +1,18 @@@
  <?php
 -/*
 +/**
 + * Handle postnotice action
 + *
 + * PHP version 5
 + *
 + * @category Action
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
-  * @author   Robin Millette <millette@controlyourself.ca>
++ * @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://laconi.ca/
++ * @link     http://status.net/
 + *
-  * Laconica - a distributed open-source microblogging tool
-  * Copyright (C) 2008, 2009, Control Yourself, Inc.
+  * 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
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  
- if (!defined('LACONICA')) {
-     exit(1);
- }
+ 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';
  
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
-  * @author   Robin Millette <millette@controlyourself.ca>
 +/**
 + * Handler for postnotice action
 + *
 + * @category Action
-  * @link     http://laconi.ca/
++ * @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;
      }
  }
- ?>
++?>
index f429a2e516479fe4bf90a9c8e74f6965660d0c88,f9c16351d36eb15e4b27566229662a67c261dc6d..83051c6fd3e8a2cb778c2b32f42a2dc106fff962
@@@ -1,6 -1,6 +1,6 @@@
  <?php
  /**
-  * Laconica, the distributed open-source microblogging tool
+  * StatusNet, the distributed open-source microblogging tool
   *
   * Change profile settings
   *
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   *
   * @category  Settings
-  * @package   Laconica
-  * @author    Evan Prodromou <evan@controlyourself.ca>
-  * @author    Zach Copley <zach@controlyourself.ca>
-  * @copyright 2008-2009 Control Yourself, Inc.
+  * @package   StatusNet
+  * @author    Evan Prodromou <evan@status.net>
+  * @author    Zach Copley <zach@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://laconi.ca/
+  * @link      http://status.net/
   */
  
- if (!defined('LACONICA')) {
+ if (!defined('STATUSNET') && !defined('LACONICA')) {
      exit(1);
  }
  
@@@ -38,11 -38,11 +38,11 @@@ require_once INSTALLDIR.'/lib/accountse
   * Change profile settings
   *
   * @category Settings
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
-  * @author   Zach Copley <zach@controlyourself.ca>
+  * @package  StatusNet
+  * @author   Evan Prodromou <evan@status.net>
+  * @author   Zach Copley <zach@status.net>
   * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
-  * @link     http://laconi.ca/
+  * @link     http://status.net/
   */
  
  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'),
              } 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).'));
diff --combined actions/public.php
index 15bedb7119b6f24cde6b29f8a9e2bb3d85bd69d3,d426648f3d3623ad214adc00a3d748ef930749e9..86b0d6f56b2350200cc0402c7e320e90c4103913
@@@ -1,6 -1,6 +1,6 @@@
  <?php
  /**
-  * Laconica, the distributed open-source microblogging tool
+  * StatusNet, the distributed open-source microblogging tool
   *
   * Action for displaying the public stream
   *
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   *
   * @category  Public
-  * @package   Laconica
-  * @author    Evan Prodromou <evan@controlyourself.ca>
-  * @copyright 2008-2009 Control Yourself, Inc.
+  * @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://laconi.ca/
+  * @link      http://status.net/
   */
  
- if (!defined('LACONICA')) {
+ if (!defined('STATUSNET') && !defined('LACONICA')) {
      exit(1);
  }
  
@@@ -43,10 -43,10 +43,10 @@@ define('MAX_PUBLIC_PAGE', 100)
   * Action for displaying the public stream
   *
   * @category Public
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
+  * @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://laconi.ca/
+  * @link     http://status.net/
   *
   * @see      PublicrssAction
   * @see      PublicxrdsAction
@@@ -114,6 -114,8 +114,6 @@@ class PublicAction extends Actio
      {
          parent::handle($args);
  
 -        header('X-XRDS-Location: '. common_local_url('publicxrds'));
 -
          $this->showPage();
      }
  
                                _('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
       *
          }
          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!');
              }
        }
  
      function showAnonymousMessage()
      {
          if (! (common_config('site','closed') || common_config('site','inviteonly'))) {
-           $m = _('This is %%site.name%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
-                   'based on the Free Software [Laconica](http://laconi.ca/) tool. ' .
-                   '[Join now](%%action.register%%) to share notices about yourself with friends, family, and colleagues! ([Read more](%%doc.help%%))');
 -            $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 [Laconica](http://laconi.ca/) tool.');
+                    'based on the Free Software [StatusNet](http://status.net/) tool.');
          }
          $this->elementStart('div', array('id' => 'anon_notice'));
          $this->raw(common_markup_to_html($m));
index e9f33d58b6d95df7925a07355e20ec31217f1c38,60bb53e27c934d9682b6ac616144d20d6c890294..e7f6ee36c736552bc33817bf5018940ea0e1d254
@@@ -1,6 -1,6 +1,6 @@@
  <?php
  /**
-  * Laconica, the distributed open-source microblogging tool
+  * StatusNet, the distributed open-source microblogging tool
   *
   * Public tag cloud for notices
   *
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   *
   * @category  Public
-  * @package   Laconica
+  * @package   StatusNet
   * @author    Mike Cochrane <mikec@mikenz.geek.nz>
-  * @author    Evan Prodromou <evan@controlyourself.ca>
+  * @author    Evan Prodromou <evan@status.net>
   * @copyright 2008 Mike Cochrane
-  * @copyright 2008-2009 Control Yourself, Inc.
+  * @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://laconi.ca/
+  * @link      http://status.net/
   */
  
- if (!defined('LACONICA')) { exit(1); }
+ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
  
  define('TAGS_PER_PAGE', 100);
  
   * Public tag cloud for notices
   *
   * @category Personal
-  * @package  Laconica
+  * @package  StatusNet
   * @author    Mike Cochrane <mikec@mikenz.geek.nz>
-  * @author    Evan Prodromou <evan@controlyourself.ca>
+  * @author    Evan Prodromou <evan@status.net>
   * @copyright 2008 Mike Cochrane
-  * @copyright 2008-2009 Control Yourself, Inc.
-  * @link     http://laconi.ca/
+  * @copyright 2008-2009 StatusNet, Inc.
+  * @link     http://status.net/
   */
  
  class PublictagcloudAction extends Action
@@@ -72,7 -72,8 +72,7 @@@
              $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 --combined actions/register.php
index aa295c0978d9ed2a93eea8ede341560f99c05fce,c431aeee3e356fd01002fd70e93f56ef57ecb01b..b40ac266d2eceb6d5f869e6f70bdf59fd3b4645f
@@@ -1,6 -1,6 +1,6 @@@
  <?php
  /**
-  * Laconica, the distributed open-source microblogging tool
+  * StatusNet, the distributed open-source microblogging tool
   *
   * Register a new user account
   *
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   *
   * @category  Login
-  * @package   Laconica
-  * @author    Evan Prodromou <evan@controlyourself.ca>
-  * @copyright 2008-2009 Control Yourself, Inc.
+  * @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://laconi.ca/
+  * @link      http://status.net/
   */
  
- if (!defined('LACONICA')) {
+ if (!defined('STATUSNET') && !defined('LACONICA')) {
      exit(1);
  }
  
   * An action for registering a new user account
   *
   * @category Login
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
+  * @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://laconi.ca/
+  * @link     http://status.net/
   */
  
  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
  
          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') {
              } 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).'));
          } 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);
                             '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 353717bebfd76faf6761c7b98b7d2d2ab5f5f248,374392d4a31df096599dc11cc2e59f002566aeaa..aee2a5d8e79f1d7a54a7c1f945eecf4752299344
@@@ -1,18 -1,7 +1,18 @@@
  <?php
 -/*
 +/**
 + * Handler for remote subscription
 + *
 + * PHP version 5
 + *
 + * @category Action
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
-  * @author   Robin Millette <millette@controlyourself.ca>
++ * @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://laconi.ca/
++ * @link     http://status.net/
 + *
-  * Laconica - a distributed open-source microblogging tool
-  * Copyright (C) 2008, 2009, Control Yourself, Inc.
+  * 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
   *
   * 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('LACONICA')) {
-     exit(1);
- }
+ 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  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
-  * @author   Robin Millette <millette@controlyourself.ca>
++ * @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://laconi.ca/
++ * @link     http://status.net/
 + */
  
  class RemotesubscribeAction extends Action
  {
@@@ -62,7 -36,7 +60,7 @@@
              return false;
          }
  
 -        $this->nickname = $this->trimmed('nickname');
 +        $this->nickname    = $this->trimmed('nickname');
          $this->profile_url = $this->trimmed('profile_url');
  
          return true;
@@@ -73,7 -47,7 +71,7 @@@
          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. '.
          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);
  
      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',
  
      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__);
              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);
      }
  }
 +?>
diff --combined actions/replies.php
index 47e01e27976b3efd91f7b4a99d4818b155551529,cca43023097c2f11ff5806cf629488cd88831bd9..6003ad30bde3e60b5a7fb5b9f953d20d1d634583
@@@ -1,6 -1,6 +1,6 @@@
  <?php
  /**
-  * Laconica, the distributed open-source microblogging tool
+  * StatusNet, the distributed open-source microblogging tool
   *
   * List of replies
   *
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   *
   * @category  Personal
-  * @package   Laconica
-  * @author    Evan Prodromou <evan@controlyourself.ca>
-  * @copyright 2008-2009 Control Yourself, Inc.
+  * @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://laconi.ca/
+  * @link      http://status.net/
   */
  
- if (!defined('LACONICA')) {
+ if (!defined('STATUSNET') && !defined('LACONICA')) {
      exit(1);
  }
  
@@@ -39,10 -39,10 +39,10 @@@ require_once INSTALLDIR.'/lib/feedlist.
   * List of replies
   *
   * @category Personal
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
+  * @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://laconi.ca/
+  * @link     http://status.net/
   */
  
  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');
diff --combined actions/requesttoken.php
index 8328962f2d5a94ec5aab2417df728995263eb740,48fe1db08f1ea13cef4eec9b6ce59b8f68cc9e13..e095161a7d01bf988604faba3ab9356f053bf191
@@@ -6,14 -6,14 +6,14 @@@
   * PHP version 5
   *
   * @category Action
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
-  * @author   Robin Millette <millette@controlyourself.ca>
+  * @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://laconi.ca/
+  * @link     http://status.net/
   *
-  * Laconica - a distributed open-source microblogging tool
-  * Copyright (C) 2008, 2009, Control Yourself, Inc.
+  * 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
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  
- if (!defined('LACONICA')) {
+ if (!defined('STATUSNET') && !defined('LACONICA')) {
      exit(1);
  }
  
  require_once INSTALLDIR.'/lib/omb.php';
 +require_once INSTALLDIR.'/extlib/libomb/service_provider.php';
  
  /**
   * Request token action class.
   *
   * @category Action
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
-  * @author   Robin Millette <millette@controlyourself.ca>
+  * @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://laconi.ca/
+  * @link     http://status.net/
   */
  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
      {
          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;
 -        } 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 5be997306964a159af2bcc3e88e9b71dd6bdeaf0,0f7a66330242be0a588c05f0b19a16b6e306e4b8..b96d2af37fc568a672132bba2a32280b488ecfc8
@@@ -1,6 -1,6 +1,6 @@@
  <?php
  /**
-  * Laconica, the distributed open-source microblogging tool
+  * StatusNet, the distributed open-source microblogging tool
   *
   * List of replies
   *
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   *
   * @category  Personal
-  * @package   Laconica
-  * @author    Evan Prodromou <evan@controlyourself.ca>
-  * @copyright 2008-2009 Control Yourself, Inc.
+  * @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://laconi.ca/
+  * @link      http://status.net/
   */
  
- if (!defined('LACONICA')) {
+ if (!defined('STATUSNET') && !defined('LACONICA')) {
      exit(1);
  }
  
@@@ -39,10 -39,10 +39,10 @@@ require_once INSTALLDIR.'/lib/feedlist.
   * List of replies
   *
   * @category Personal
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
+  * @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://laconi.ca/
+  * @link     http://status.net/
   */
  
  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');
diff --combined actions/showgroup.php
index c3471c195a2e19010aac020897f3550f942a9691,8157ee3c852f0884288e058c8e83bae90bc88806..ff994976215263447b6151d5f65b131873c1039f
@@@ -1,6 -1,6 +1,6 @@@
  <?php
  /**
-  * Laconica, the distributed open-source microblogging tool
+  * StatusNet, the distributed open-source microblogging tool
   *
   * Group main page
   *
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   *
   * @category  Group
-  * @package   Laconica
-  * @author    Evan Prodromou <evan@controlyourself.ca>
-  * @author    Sarven Capadisli <csarven@controlyourself.ca>
-  * @copyright 2008-2009 Control Yourself, Inc.
+  * @package   StatusNet
+  * @author    Evan Prodromou <evan@status.net>
+  * @author    Sarven Capadisli <csarven@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://laconi.ca/
+  * @link      http://status.net/
   */
  
- if (!defined('LACONICA')) {
+ if (!defined('STATUSNET') && !defined('LACONICA')) {
      exit(1);
  }
  
@@@ -41,10 -41,10 +41,10 @@@ define('MEMBERS_PER_SECTION', 27)
   * Group main page
   *
   * @category Group
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
+  * @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://laconi.ca/
+  * @link     http://status.net/
   */
  
  class ShowgroupAction extends GroupDesignAction
      {
          if (!(common_config('site','closed') || common_config('site','inviteonly'))) {
              $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 [Laconica](http://laconi.ca/) tool. Its members share ' .
+                 '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 [Laconica](http://laconi.ca/) tool. Its members share ' .
+                 'based on the Free Software [StatusNet](http://status.net/) tool. Its members share ' .
                  'short messages about their life and interests. '),
                       $this->group->nickname);
          }
diff --combined actions/shownotice.php
index 82031d90dc5d7c3f83cf6345938ad6a39d5c259b,3bc52b2dbc7bae3fef45a4bf7cedc24df156bc38..41408c23ccb4a396d92d814366824a5871475ae5
@@@ -1,6 -1,6 +1,6 @@@
  <?php
  /**
-  * Laconica, the distributed open-source microblogging tool
+  * StatusNet, the distributed open-source microblogging tool
   *
   * Show a single notice
   *
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   *
   * @category  Personal
-  * @package   Laconica
-  * @author    Evan Prodromou <evan@controlyourself.ca>
-  * @copyright 2008-2009 Control Yourself, Inc.
+  * @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://laconi.ca/
+  * @link      http://status.net/
   */
  
- if (!defined('LACONICA')) {
+ if (!defined('STATUSNET') && !defined('LACONICA')) {
      exit(1);
  }
  
@@@ -39,10 -39,10 +39,10 @@@ require_once INSTALLDIR.'/lib/feedlist.
   * Show a single notice
   *
   * @category Personal
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
+  * @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://laconi.ca/
+  * @link     http://status.net/
   */
  
  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;
          }
  
diff --combined actions/showstream.php
index cd5d4bb7013bfb84f87d8eca580ddc0ba8c74bb6,4d3067eed37275e35144fc2ecd292e956a0c4954..2e9679faed5444a6f4af302b0c0af0dcc2d7979d
@@@ -1,6 -1,6 +1,6 @@@
  <?php
  /**
-  * Laconica, the distributed open-source microblogging tool
+  * StatusNet, the distributed open-source microblogging tool
   *
   * User profile page
   *
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   *
   * @category  Personal
-  * @package   Laconica
-  * @author    Evan Prodromou <evan@controlyourself.ca>
-  * @author    Sarven Capadisli <csarven@controlyourself.ca>
-  * @copyright 2008-2009 Control Yourself, Inc.
+  * @package   StatusNet
+  * @author    Evan Prodromou <evan@status.net>
+  * @author    Sarven Capadisli <csarven@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://laconi.ca/
+  * @link      http://status.net/
   */
  
- if (!defined('LACONICA')) {
+ if (!defined('STATUSNET') && !defined('LACONICA')) {
      exit(1);
  }
  
@@@ -48,10 -48,10 +48,10 @@@ require_once INSTALLDIR.'/lib/feedlist.
   * to subscriptions and stuff, etc.
   *
   * @category Personal
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
+  * @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://laconi.ca/
+  * @link     http://status.net/
   */
  
  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');
      {
          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 [Laconica](http://laconi.ca/) tool. ' .
+                  '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 [Laconica](http://laconi.ca/) tool. '),
+                  'based on the Free Software [StatusNet](http://status.net/) tool. '),
                   $this->user->nickname, $this->user->nickname);
        }
          $this->elementStart('div', array('id' => 'anon_notice'));
diff --combined actions/subscribers.php
index 66ac00fb19aa26d64da5441d07fafec8bc08f36a,f7d08d9d0b03d35493368982a18214c831af709c..df9ec996159abf64dfb4c04b26bdfb9b7c13e359
@@@ -1,6 -1,6 +1,6 @@@
  <?php
  /**
-  * Laconica, the distributed open-source microblogging tool
+  * StatusNet, the distributed open-source microblogging tool
   *
   * List a user's subscribers
   *
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   *
   * @category  Social
-  * @package   Laconica
-  * @author    Evan Prodromou <evan@controlyourself.ca>
-  * @author    Sarven Capadisli <csarven@controlyourself.ca>
-  * @copyright 2008-2009 Control Yourself, Inc.
+  * @package   StatusNet
+  * @author    Evan Prodromou <evan@status.net>
+  * @author    Sarven Capadisli <csarven@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://laconi.ca/
+  * @link      http://status.net/
   */
  
- if (!defined('LACONICA')) {
+ if (!defined('STATUSNET') && !defined('LACONICA')) {
      exit(1);
  }
  
   * List a user's subscribers
   *
   * @category Social
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
+  * @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://laconi.ca/
+  * @link     http://status.net/
   */
  
  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 aac7d63b13fc7ce656b28ae7c94a4c2275b3dabc,dbe55804b16337aada7f06f373c481e19eb96d44..08b8f4e9c18a78121cd133cbbb594f5a90a5067f
@@@ -1,7 -1,7 +1,7 @@@
  <?php
  /*
-  * Laconica - a distributed open-source microblogging tool
-  * Copyright (C) 2008, 2009, Control Yourself, Inc.
+  * 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
@@@ -17,7 -17,7 +17,7 @@@
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  
- if (!defined('LACONICA')) {
+ if (!defined('STATUSNET') && !defined('LACONICA')) {
      exit(1);
  }
  
@@@ -141,10 -141,9 +141,10 @@@ class Twitapidirect_messagesAction exte
                  $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 1f3c53beffd72c652d6ba6a6c102f79436a80b7a,edee239a03e67dc2f7c44919ac5d6adf98e14d84..ad6654dff54321f7d0a5c0089791cd60e75134ee
@@@ -1,7 -1,7 +1,7 @@@
  <?php
  /*
-  * Laconica - a distributed open-source microblogging tool
-  * Copyright (C) 2008, 2009, Control Yourself, Inc.
+  * 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
@@@ -17,7 -17,7 +17,7 @@@
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  
- if (!defined('LACONICA')) {
+ if (!defined('STATUSNET') && !defined('LACONICA')) {
      exit(1);
  }
  
@@@ -242,15 -242,14 +242,15 @@@ class TwitapistatusesAction extends Twi
  
              $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;
              }
          }
          } 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 b020413b3563fd3e2ebc6b50872ba6a4aec74f50,9a4cf8e4659b6f51c09fb23a48993457efd52014..7f7dd75fef14481c8992d6e3ae7db711533eab9c
@@@ -1,18 -1,7 +1,18 @@@
  <?php
 -/*
 +/**
 + * Handle an updateprofile action
 + *
 + * PHP version 5
 + *
 + * @category Action
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
-  * @author   Robin Millette <millette@controlyourself.ca>
++ * @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://laconi.ca/
++ * @link     http://status.net/
 + *
-  * Laconica - a distributed open-source microblogging tool
-  * Copyright (C) 2008, 2009, Control Yourself, Inc.
+  * 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
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  
- if (!defined('LACONICA')) {
-     exit(1);
- }
+ 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@controlyourself.ca>
 + * @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(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 cc8bfdaea499963f2747ad15483aa350d9c7fa5f,a9ac1f256f79eb3bef2e445fe2fbab668c33089e..dc59e6c94112ccec04d926c11647ce3c9b7a3e5b
@@@ -1,18 -1,7 +1,18 @@@
  <?php
 -/*
 +/**
 + * Let the user authorize a remote subscription request
 + *
 + * PHP version 5
 + *
 + * @category Action
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
-  * @author   Robin Millette <millette@controlyourself.ca>
++ * @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://laconi.ca/
++ * @link     http://status.net/
 + *
-  * Laconica - a distributed open-source microblogging tool
-  * Copyright (C) 2008, 2009, Control Yourself, Inc.
+  * 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
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  
- if (!defined('LACONICA')) {
-     exit(1);
- }
+ 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
          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();
      }
  
      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');
                                            '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');
  
      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)
          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()
      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.");
 -        }
      }
--}
++}
diff --combined actions/xrds.php
index b3aa8df8e57499bda8e5f18978fcff8ea4ed0e58,def10e4cf7bd90d8af6b6263eec7af093dfb1fbe..8ba89fec0ff7c56a262ebaa29374ba9a8c0b8359
@@@ -1,19 -1,19 +1,19 @@@
  <?php
  
  /**
 - * XRDS for OpenID
 + * XRDS for OpenMicroBlogging
   *
   * PHP version 5
   *
   * @category Action
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
-  * @author   Robin Millette <millette@controlyourself.ca>
+  * @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://laconi.ca/
+  * @link     http://status.net/
   *
-  * Laconica - a distributed open-source microblogging tool
-  * Copyright (C) 2008, 2009, Control Yourself, Inc.
+  * 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
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  
- if (!defined('LACONICA')) {
+ if (!defined('STATUSNET') && !defined('LACONICA')) {
      exit(1);
  }
  
  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  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
-  * @author   Robin Millette <millette@controlyourself.ca>
+  * @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://laconi.ca/
+  * @link     http://status.net/
   */
  class XrdsAction extends Action
  {
@@@ -54,7 -52,7 +54,7 @@@
       *
       * @return boolean true
       */
 -    function isReadOnly($args)
 +    function isReadOnly()
      {
          return true;
      }
       */
      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 --combined classes/Notice.php
index 48d4a094029f942346551e77265b191d181935b1,e597128641e141c9cec793e0adbc7b2af4b195e7..ed41acbaea08d2852d2c0158e56de53674a82fe2
@@@ -1,7 -1,7 +1,7 @@@
  <?php
  /*
-  * Laconica - a distributed open-source microblogging tool
-  * Copyright (C) 2008, 2009, Control Yourself, Inc.
+  * 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
@@@ -17,7 -17,7 +17,7 @@@
   * along with this program.     If not, see <http://www.gnu.org/licenses/>.
   */
  
- if (!defined('LACONICA')) { exit(1); }
+ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
  
  /**
   * Table Definition for notice
@@@ -40,7 -40,7 +40,7 @@@ class Notice extends Memcached_DataObje
      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,7 -51,9 +51,7 @@@
      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
          $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',
  
          $final = common_shorten_links($content);
  
 -        if (mb_strlen($final) > 140) {
 +        if (Notice::contentTooLong($final)) {
              common_log(LOG_INFO, 'Rejecting notice that is too long.');
              return _('Problem saving notice. Too long.');
          }
              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));
 +    }
  }
diff --combined classes/Profile.php
index 8f92b386e963b1167c8326085e6e81c01c2646d9,6ad0e7a3a3b3f3884fbe221466cb034938550551..7f0d1275833daa51feee64172b8a35e8ec2ff346
@@@ -1,7 -1,7 +1,7 @@@
  <?php
  /*
-  * Laconica - a distributed open-source microblogging tool
-  * Copyright (C) 2008, 2009, Control Yourself, Inc.
+  * 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
@@@ -17,7 -17,7 +17,7 @@@
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  
- if (!defined('LACONICA')) { exit(1); }
+ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
  
  /**
   * Table Definition for profile
@@@ -35,13 -35,14 +35,13 @@@ class Profile extends Memcached_DataObj
      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
              $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));
 +    }
  }
diff --combined classes/User.php
index 104dfbacb7888b82d26a4760e9488d7f1ea16c15,14d3cf54fac608caf62f386f090b3d24eb77fdf0..aa366a5f343c92c5187e8251ad96b564fba0a3e4
@@@ -1,7 -1,7 +1,7 @@@
  <?php
  /*
-  * Laconica - a distributed open-source microblogging tool
-  * Copyright (C) 2008, 2009, Control Yourself, Inc.
+  * 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
@@@ -17,7 -17,7 +17,7 @@@
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  
- if (!defined('LACONICA')) {
+ if (!defined('STATUSNET') && !defined('LACONICA')) {
      exit(1);
  }
  
@@@ -670,6 -670,17 +670,6 @@@ class User extends Memcached_DataObjec
          return $profile;
      }
  
 -    function hasOpenID()
 -    {
 -        $oid = new User_openid();
 -
 -        $oid->user_id = $this->id;
 -
 -        $cnt = $oid->find();
 -
 -        return ($cnt > 0);
 -    }
 -
      function getDesign()
      {
          return Design::staticGet('id', $this->design_id);
diff --combined classes/statusnet.ini
index 8123265e46c879fd6ff043a3c498d40aca82aef9,766bed75deff2a8af0134b0c6773dac420895844..7edeeebe4fa3ec7b0e49ef05615f61b63feee1e7
mode 100644,100644..100755
- [status_network]
+ [avatar]
+ profile_id = 129
+ original = 17
+ width = 129
+ height = 129
+ mediatype = 130
+ filename = 2
+ url = 2
+ created = 142
+ modified = 384
+ [avatar__keys]
+ profile_id = K
+ 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
+ address = 130
+ address_extra = 130
+ address_type = 130
+ claimed = 14
+ sent = 14
+ modified = 384
+ [confirm_address__keys]
+ code = K
+ [consumer]
+ consumer_key = 130
+ seed = 130
+ created = 142
+ 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
+ contentcolor = 1
+ sidebarcolor = 1
+ textcolor = 1
+ linkcolor = 1
+ backgroundimage = 2
+ disposition = 17
+ [design__keys]
+ id = N
+ [fave]
+ notice_id = 129
+ user_id = 129
+ modified = 384
+ [fave__keys]
+ notice_id = K
+ user_id = K
+ [file]
+ id = 129
+ url = 2
+ mimetype = 2
+ size = 1
+ title = 2
+ date = 1
+ protected = 1
+ filename = 2
+ modified = 384
+ [file__keys]
+ id = N
+ [file_oembed]
+ file_id = 129
+ version = 2
+ type = 2
+ provider = 2
+ provider_url = 2
+ width = 1
+ height = 1
+ html = 34
+ title = 2
+ author_name = 2
+ author_url = 2
+ url = 2
+ modified = 384
+ [file_oembed__keys]
+ file_id = K
+ [file_redirection]
+ url = 130
+ file_id = 1
+ redirections = 1
+ httpcode = 1
+ modified = 384
+ [file_redirection__keys]
+ url = K
+ [file_thumbnail]
+ file_id = 129
+ url = 2
+ width = 1
+ height = 1
+ modified = 384
+ [file_thumbnail__keys]
+ file_id = K
+ url = U
+ [file_to_post]
+ file_id = 129
+ post_id = 129
+ modified = 384
+ [file_to_post__keys]
+ file_id = K
+ post_id = K
+ [foreign_link]
+ user_id = 129
+ foreign_id = 129
+ service = 129
+ credentials = 2
+ noticesync = 145
+ friendsync = 145
+ profilesync = 145
+ last_noticesync = 14
+ last_friendsync = 14
+ created = 142
+ modified = 384
+ [foreign_link__keys]
+ user_id = K
+ foreign_id = K
+ service = K
+ [foreign_service]
+ id = 129
+ name = 130
+ description = 2
+ created = 142
+ modified = 384
+ [foreign_service__keys]
+ id = K
+ name = U
+ [foreign_subscription]
+ service = 129
+ subscriber = 129
+ subscribed = 129
+ created = 142
+ [foreign_subscription__keys]
+ service = K
+ subscriber = K
+ subscribed = K
+ [foreign_user]
+ id = 129
+ service = 129
+ uri = 130
+ nickname = 2
+ created = 142
+ modified = 384
+ [foreign_user__keys]
+ id = K
+ service = K
+ uri = U
+ [group_alias]
+ alias = 130
+ group_id = 129
+ modified = 384
+ [group_alias__keys]
+ alias = K
+ [group_block]
+ group_id = 129
+ blocked = 129
+ blocker = 129
+ modified = 384
+ [group_block__keys]
+ group_id = K
+ blocked = K
+ [group_inbox]
+ group_id = 129
+ notice_id = 129
+ created = 142
+ [group_inbox__keys]
+ group_id = K
+ notice_id = K
+ [group_member]
+ group_id = 129
+ profile_id = 129
+ is_admin = 17
+ created = 142
+ modified = 384
+ [group_member__keys]
+ group_id = K
+ profile_id = K
+ [invitation]
+ code = 130
+ user_id = 129
+ address = 130
+ address_type = 130
+ created = 142
+ [invitation__keys]
+ code = K
+ [message]
+ id = 129
+ uri = 2
+ from_profile = 129
+ to_profile = 129
 -content = 2
++content = 34
+ rendered = 34
+ url = 2
+ created = 142
+ modified = 384
+ source = 2
+ [message__keys]
+ id = N
+ [nonce]
+ consumer_key = 130
+ tok = 2
+ nonce = 130
+ ts = 142
+ created = 142
+ modified = 384
+ [nonce__keys]
+ consumer_key = K
+ nonce = K
+ ts = K
+ [notice]
+ id = 129
+ profile_id = 129
+ uri = 2
 -content = 2
++content = 34
+ rendered = 34
+ url = 2
+ created = 142
+ modified = 384
+ reply_to = 1
+ is_local = 17
+ source = 2
+ conversation = 1
+ [notice__keys]
+ id = N
+ [notice_inbox]
+ user_id = 129
+ notice_id = 129
+ created = 142
+ source = 17
+ [notice_inbox__keys]
+ user_id = K
+ notice_id = K
+ [notice_source]
+ code = 130
+ name = 130
+ url = 130
+ created = 142
+ modified = 384
+ [notice_source__keys]
+ code = K
+ [notice_tag]
+ tag = 130
+ notice_id = 129
+ created = 142
+ [notice_tag__keys]
+ tag = K
+ notice_id = K
+ [profile]
+ id = 129
  nickname = 130
- hostname = 2
- pathname = 2
- dbhost = 2
- dbuser = 2
- dbpass = 2
- dbname = 2
- sitename = 2
- theme = 2
- logo = 2
- created = 142
- modified = 384
- [status_network__keys]
- nickname = K
- hostname = U
- pathname = U
+ fullname = 2
+ profileurl = 2
+ homepage = 2
 -bio = 2
++bio = 34
+ location = 2
+ created = 142
+ modified = 384
+ [profile__keys]
+ id = N
+ [profile_block]
+ blocker = 129
+ blocked = 129
+ modified = 384
+ [profile_block__keys]
+ blocker = K
+ blocked = K
+ [profile_tag]
+ tagger = 129
+ tagged = 129
+ tag = 130
+ modified = 384
+ [profile_tag__keys]
+ tagger = K
+ tagged = K
+ tag = K
+ [queue_item]
+ notice_id = 129
+ transport = 130
+ created = 142
+ claimed = 14
+ [queue_item__keys]
+ notice_id = K
+ transport = K
+ [related_group]
+ group_id = 129
+ related_group_id = 129
+ created = 142
+ [related_group__keys]
+ group_id = K
+ related_group_id = K
+ [remember_me]
+ code = 130
+ user_id = 129
+ modified = 384
+ [remember_me__keys]
+ code = K
+ [remote_profile]
+ id = 129
+ uri = 2
+ postnoticeurl = 2
+ updateprofileurl = 2
+ created = 142
+ modified = 384
+ [remote_profile__keys]
+ id = K
+ uri = U
+ [reply]
+ notice_id = 129
+ profile_id = 129
+ modified = 384
+ replied_id = 1
+ [reply__keys]
+ notice_id = K
+ profile_id = K
+ [session]
+ id = 130
+ session_data = 34
+ created = 142
+ modified = 384
+ [session__keys]
+ id = K
+ [sms_carrier]
+ id = 129
+ name = 2
+ email_pattern = 130
+ created = 142
+ modified = 384
+ [sms_carrier__keys]
+ id = K
+ name = U
+ [subscription]
+ subscriber = 129
+ subscribed = 129
+ jabber = 17
+ sms = 17
+ token = 2
+ secret = 2
+ created = 142
+ modified = 384
+ [subscription__keys]
+ subscriber = K
+ subscribed = K
+ [token]
+ consumer_key = 130
+ tok = 130
+ secret = 130
+ type = 145
+ state = 17
+ created = 142
+ modified = 384
+ [token__keys]
+ consumer_key = K
+ tok = K
+ [user]
+ id = 129
+ nickname = 2
+ password = 2
+ email = 2
+ incomingemail = 2
+ emailnotifysub = 17
+ emailnotifyfav = 17
+ emailnotifynudge = 17
+ emailnotifymsg = 17
+ emailnotifyattn = 17
+ emailmicroid = 17
+ language = 2
+ timezone = 2
+ emailpost = 17
+ jabber = 2
+ jabbernotify = 17
+ jabberreplies = 17
+ jabbermicroid = 17
+ updatefrompresence = 17
+ sms = 2
+ carrier = 1
+ smsnotify = 17
+ smsreplies = 17
+ smsemail = 2
+ uri = 2
+ autosubscribe = 17
+ urlshorteningservice = 2
+ inboxed = 17
+ design_id = 1
+ viewdesigns = 17
+ created = 142
+ modified = 384
+ [user__keys]
+ id = K
+ nickname = U
+ email = U
+ incomingemail = U
+ jabber = U
+ sms = U
+ uri = U
+ [user_group]
+ id = 129
+ nickname = 2
+ fullname = 2
+ homepage = 2
 -description = 2
++description = 34
+ location = 2
+ original_logo = 2
+ homepage_logo = 2
+ stream_logo = 2
+ mini_logo = 2
+ design_id = 1
+ created = 142
+ modified = 384
+ [user_group__keys]
+ id = N
+ [user_openid]
+ canonical = 130
+ display = 130
+ user_id = 129
+ created = 142
+ modified = 384
+ [user_openid__keys]
+ canonical = K
+ display = U
diff --combined config.php.sample
index 7877df71639469ea84997fceba80c1336e122b64,bd3776a47f4a0620c4a3a3c02e588f58af3d911a..997c9d6b0bccb971157c6cc50f5689fc17419f87
@@@ -1,7 -1,7 +1,7 @@@
  <?php
  /* -*- mode: php -*- */
  
- if (!defined('LACONICA')) { exit(1); }
+ if (!defined('STATUSNET')) { exit(1); }
  
  // If you have downloaded libraries in random little places, you
  // can add the paths here
@@@ -13,9 -13,9 +13,9 @@@
  // options
  // These are for configuring your URLs
  
- $config['site']['name'] = 'Just another Laconica microblog';
+ $config['site']['name'] = 'Just another StatusNet microblog';
  $config['site']['server'] = 'localhost';
- $config['site']['path'] = 'laconica';
+ $config['site']['path'] = 'statusnet';
  // $config['site']['fancy'] = false;
  // $config['site']['theme'] = 'default';
  // Sets the site's default design values
  // $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;
  
  // If you want logging sent to a file instead of syslog
- // $config['site']['logfile'] = '/tmp/laconica.log';
+ // $config['site']['logfile'] = '/tmp/statusnet.log';
  
- // Change the syslog facility that Laconica logs to (default is LOG_USER)
+ // Change the syslog facility that StatusNet logs to (default is LOG_USER)
  // $config['syslog']['facility'] = LOG_LOCAL7;
  
  // Enables extra log information, for example full details of PEAR DB errors
@@@ -56,8 -58,8 +56,8 @@@
  // This is a PEAR DB DSN, see http://pear.php.net/manual/en/package.database.db.intro-dsn.php
  // Set it to match your actual database
  
- $config['db']['database'] = 'mysql://laconica:microblog@localhost/laconica';
- // $config['db']['ini_your_db_name'] = $config['db']['schema_location'].'/laconica.ini';
+ $config['db']['database'] = 'mysql://statusnet:microblog@localhost/statusnet';
+ // $config['db']['ini_your_db_name'] = $config['db']['schema_location'].'/statusnet.ini';
  // *** WARNING *** WARNING *** WARNING *** WARNING ***
  // Setting debug to a non-zero value will expose your DATABASE PASSWORD to Web users.
  // !!!!!! DO NOT SET THIS ON PRODUCTION SERVERS !!!!!! DB_DataObject's bug, btw, not
@@@ -97,6 -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;
  
  // using stomp requires an external message queue server
  // $config['queue']['subsystem'] = 'stomp';
  // $config['queue']['stomp_server'] = 'tcp://localhost:61613';
- // use different queue_basename for each laconica instance managed by the server
- // $config['queue']['queue_basename'] = 'laconica';
+ // use different queue_basename for each statusnet instance managed by the server
+ // $config['queue']['queue_basename'] = 'statusnet';
  
  // The following customise the behaviour of the various daemons:
  // $config['daemon']['piddir'] = '/var/run';
  // $config['daemon']['user'] = false;
  // $config['daemon']['group'] = false;
  
- // For installations with high traffic, laconica can use MemCached to cache
+ // For installations with high traffic, statusnet can use MemCached to cache
  // frequently requested information. Only enable the following if you have
  // MemCached up and running:
  // $config['memcached']['enabled'] = false;
  // Disable Twitter integration
  // $config['twitter']['enabled'] = false;
  
- // Twitter integration source attribute. Note: default is Laconica
- // $config['integration']['source'] = 'Laconica';
+ // Twitter integration source attribute. Note: default is StatusNet
+ // $config['integration']['source'] = 'StatusNet';
  
  // Enable bidirectional Twitter bridge
  //
diff --combined db/statusnet.sql
index 0000000000000000000000000000000000000000,2c04f680a85d587a032cd06fada5c63c8488eaa5..1662ef7a8be6bc074c1c76b4a25937cd3955ad3d
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,537 +1,559 @@@
 -    bio varchar(140) comment 'descriptive biography',
+ /* local and remote users have profiles */
+ create table profile (
+     id integer auto_increment primary key comment 'unique identifier',
+     nickname varchar(64) not null comment 'nickname or username',
+     fullname varchar(255) comment 'display name',
+     profileurl varchar(255) comment 'URL, cached so we dont regenerate',
+     homepage varchar(255) comment 'identifying URL',
 -    content varchar(140) comment 'update content',
++    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',
+     index profile_nickname_idx (nickname),
+     FULLTEXT(nickname, fullname, location, bio, homepage)
+ ) ENGINE=MyISAM CHARACTER SET utf8 COLLATE utf8_general_ci;
+ create table avatar (
+     profile_id integer not null comment 'foreign key to profile table' references profile (id),
+     original boolean default false comment 'uploaded by user or generated?',
+     width integer not null comment 'image width',
+     height integer not null comment 'image height',
+     mediatype varchar(32) not null comment 'file type',
+     filename varchar(255) null comment 'local filename, if local',
+     url varchar(255) unique key comment 'avatar location',
+     created datetime not null comment 'date this record was created',
+     modified timestamp comment 'date this record was modified',
+     constraint primary key (profile_id, width, height),
+     index avatar_profile_id_idx (profile_id)
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+ create table sms_carrier (
+     id integer primary key comment 'primary key for SMS carrier',
+     name varchar(64) unique key comment 'name of the carrier',
+     email_pattern varchar(255) not null comment 'sprintf pattern for making an email address from a phone number',
+     created datetime not null comment 'date this record was created',
+     modified timestamp comment 'date this record was modified'
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+ /* local users */
+ create table user (
+     id integer primary key comment 'foreign key to profile table' references profile (id),
+     nickname varchar(64) unique key comment 'nickname or username, duped in profile',
+     password varchar(255) comment 'salted password, can be null for OpenID users',
+     email varchar(255) unique key comment 'email address for password recovery etc.',
+     incomingemail varchar(255) unique key comment 'email address for post-by-email',
+     emailnotifysub tinyint default 1 comment 'Notify by email of subscriptions',
+     emailnotifyfav tinyint default 1 comment 'Notify by email of favorites',
+     emailnotifynudge tinyint default 1 comment 'Notify by email of nudges',
+     emailnotifymsg tinyint default 1 comment 'Notify by email of direct messages',
+     emailnotifyattn tinyint default 1 comment 'Notify by email of @-replies',
+     emailmicroid tinyint default 1 comment 'whether to publish email microid',
+     language varchar(50) comment 'preferred language',
+     timezone varchar(50) comment 'timezone',
+     emailpost tinyint default 1 comment 'Post by email',
+     jabber varchar(255) unique key comment 'jabber ID for notices',
+     jabbernotify tinyint default 0 comment 'whether to send notices to jabber',
+     jabberreplies tinyint default 0 comment 'whether to send notices to jabber on replies',
+     jabbermicroid tinyint default 1 comment 'whether to publish xmpp microid',
+     updatefrompresence tinyint default 0 comment 'whether to record updates from Jabber presence notices',
+     sms varchar(64) unique key comment 'sms phone number',
+     carrier integer comment 'foreign key to sms_carrier' references sms_carrier (id),
+     smsnotify tinyint default 0 comment 'whether to send notices to SMS',
+     smsreplies tinyint default 0 comment 'whether to send notices to SMS on replies',
+     smsemail varchar(255) comment 'built from sms and carrier',
+     uri varchar(255) unique key comment 'universally unique identifier, usually a tag URI',
+     autosubscribe tinyint default 0 comment 'automatically subscribe to users who subscribe to us',
+     urlshorteningservice varchar(50) default 'ur1.ca' comment 'service to use for auto-shortening URLs',
+     inboxed tinyint default 0 comment 'has an inbox been created for this user?',
+     design_id integer comment 'id of a design' references design(id),
+     viewdesigns tinyint default 1 comment 'whether to view user-provided designs',
+     created datetime not null comment 'date this record was created',
+     modified timestamp comment 'date this record was modified',
+     index user_smsemail_idx (smsemail)
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
+ /* remote people */
+ create table remote_profile (
+     id integer primary key comment 'foreign key to profile table' references profile (id),
+     uri varchar(255) unique key comment 'universally unique identifier, usually a tag URI',
+     postnoticeurl varchar(255) comment 'URL we use for posting notices',
+     updateprofileurl varchar(255) comment 'URL we use for updates to this profile',
+     created datetime not null comment 'date this record was created',
+     modified timestamp comment 'date this record was modified'
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+ create table subscription (
+     subscriber integer not null comment 'profile listening',
+     subscribed integer not null comment 'profile being listened to',
+     jabber tinyint default 1 comment 'deliver jabber messages',
+     sms tinyint default 1 comment 'deliver sms messages',
+     token varchar(255) comment 'authorization token',
+     secret varchar(255) comment 'token secret',
+     created datetime not null comment 'date this record was created',
+     modified timestamp comment 'date this record was modified',
+     constraint primary key (subscriber, subscribed),
+     index subscription_subscriber_idx (subscriber),
+     index subscription_subscribed_idx (subscribed),
+     index subscription_token_idx (token)
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+ 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 'message 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',
+     modified timestamp comment 'date this record was modified',
+     reply_to integer comment 'notice replied to (usually a guess)' references notice (id),
+     is_local tinyint default 0 comment 'notice was generated by a user',
+     source varchar(32) comment 'source of comment, like "web", "im", or "clientname"',
+     conversation integer comment 'id of root notice in this conversation' references notice (id),
+     index notice_profile_id_idx (profile_id),
+     index notice_conversation_idx (conversation),
+     index notice_created_idx (created),
+     index notice_replyto_idx (reply_to),
+     FULLTEXT(content)
+ ) ENGINE=MyISAM CHARACTER SET utf8 COLLATE utf8_general_ci;
+ create table notice_source (
+      code varchar(32) primary key not null comment 'source code',
+      name varchar(255) not null comment 'name of the source',
+      url varchar(255) not null comment 'url to link to',
+      created datetime not null comment 'date this record was created',
+      modified timestamp comment 'date this record was modified'
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+ create table reply (
+     notice_id integer not null comment 'notice that is the reply' references notice (id),
+     profile_id integer not null comment 'profile replied to' references profile (id),
+     modified timestamp not null comment 'date this record was modified',
+     replied_id integer comment 'notice replied to (not used, see notice.reply_to)',
+     constraint primary key (notice_id, profile_id),
+     index reply_notice_id_idx (notice_id),
+     index reply_profile_id_idx (profile_id),
+     index reply_replied_id_idx (replied_id)
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+ create table fave (
+     notice_id integer not null comment 'notice that is the favorite' references notice (id),
+     user_id integer not null comment 'user who likes this notice' references user (id),
+     modified timestamp not null comment 'date this record was modified',
+     constraint primary key (notice_id, user_id),
+     index fave_notice_id_idx (notice_id),
+     index fave_user_id_idx (user_id),
+     index fave_modified_idx (modified)
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+ /* tables for OAuth */
+ create table consumer (
+     consumer_key varchar(255) primary key comment 'unique identifier, root URL',
+     seed char(32) not null comment 'seed for new tokens by this consumer',
+     created datetime not null comment 'date this record was created',
+     modified timestamp comment 'date this record was modified'
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+ create table token (
+     consumer_key varchar(255) not null comment 'unique identifier, root URL' references consumer (consumer_key),
+     tok char(32) not null comment 'identifying value',
+     secret char(32) not null comment 'secret value',
+     type tinyint not null default 0 comment 'request or access',
+     state tinyint default 0 comment 'for requests, 0 = initial, 1 = authorized, 2 = used',
+     created datetime not null comment 'date this record was created',
+     modified timestamp comment 'date this record was modified',
+     constraint primary key (consumer_key, tok)
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+ create table nonce (
+     consumer_key varchar(255) not null comment 'unique identifier, root URL',
+     tok char(32) null comment 'buggy old value, ignored',
+     nonce char(32) not null comment 'nonce',
+     ts datetime not null comment 'timestamp sent',
+     created datetime not null comment 'date this record was created',
+     modified timestamp comment 'date this record was modified',
+     constraint primary key (consumer_key, ts, nonce)
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+ /* One-to-many relationship of user to openid_url */
+ create table user_openid (
+     canonical varchar(255) primary key comment 'Canonical true URL',
+     display varchar(255) not null unique key comment 'URL for viewing, may be different from canonical',
+     user_id integer not null comment 'user owning this URL' references user (id),
+     created datetime not null comment 'date this record was created',
+     modified timestamp comment 'date this record was modified',
+     index user_openid_user_id_idx (user_id)
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+ /* These are used by JanRain OpenID library */
+ create table oid_associations (
+     server_url BLOB,
+     handle VARCHAR(255) character set latin1,
+     secret BLOB,
+     issued INTEGER,
+     lifetime INTEGER,
+     assoc_type VARCHAR(64),
+     PRIMARY KEY (server_url(255), handle)
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+ create table oid_nonces (
+     server_url VARCHAR(2047),
+     timestamp INTEGER,
+     salt CHAR(40),
+     UNIQUE (server_url(255), timestamp, salt)
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+ create table confirm_address (
+     code varchar(32) not null primary key comment 'good random code',
+     user_id integer not null comment 'user who requested confirmation' references user (id),
+     address varchar(255) not null comment 'address (email, Jabber, SMS, etc.)',
+     address_extra varchar(255) not null comment 'carrier ID, for SMS',
+     address_type varchar(8) not null comment 'address type ("email", "jabber", "sms")',
+     claimed datetime comment 'date this was claimed for queueing',
+     sent datetime comment 'date this was sent for queueing',
+     modified timestamp comment 'date this record was modified'
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+ create table remember_me (
+     code varchar(32) not null primary key comment 'good random code',
+     user_id integer not null comment 'user who is logged in' references user (id),
+     modified timestamp comment 'date this record was modified'
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+ create table queue_item (
+     notice_id integer not null comment 'notice queued' references notice (id),
+     transport varchar(8) not null comment 'queue for what? "email", "jabber", "sms", "irc", ...',
+     created datetime not null comment 'date this record was created',
+     claimed datetime comment 'date this item was claimed',
+     constraint primary key (notice_id, transport),
+     index queue_item_created_idx (created)
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+ /* Hash tags */
+ create table notice_tag (
+     tag varchar( 64 ) not null comment 'hash tag associated with this notice',
+     notice_id integer not null comment 'notice tagged' references notice (id),
+     created datetime not null comment 'date this record was created',
+     constraint primary key (tag, notice_id),
+     index notice_tag_created_idx (created),
+     index notice_tag_notice_id_idx (notice_id)
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+ /* Synching with foreign services */
+ create table foreign_service (
+      id int not null primary key comment 'numeric key for service',
+      name varchar(32) not null unique key comment 'name of the service',
+      description varchar(255) comment 'description',
+      created datetime not null comment 'date this record was created',
+      modified timestamp comment 'date this record was modified'
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+ create table foreign_user (
+      id bigint not null comment 'unique numeric key on foreign service',
+      service int not null comment 'foreign key to service' references foreign_service(id),
+      uri varchar(255) not null unique key comment 'identifying URI',
+      nickname varchar(255) comment 'nickname on foreign service',
+      created datetime not null comment 'date this record was created',
+      modified timestamp comment 'date this record was modified',
+      constraint primary key (id, service)
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+ create table foreign_link (
+      user_id int comment 'link to user on this system, if exists' references user (id),
+      foreign_id bigint unsigned comment 'link to user on foreign service, if exists' references foreign_user(id),
+      service int not null comment 'foreign key to service' references foreign_service(id),
+      credentials varchar(255) comment 'authc credentials, typically a password',
+      noticesync tinyint not null default 1 comment 'notice synchronization, bit 1 = sync outgoing, bit 2 = sync incoming, bit 3 = filter local replies',
+      friendsync tinyint not null default 2 comment 'friend synchronization, bit 1 = sync outgoing, bit 2 = sync incoming',
+      profilesync tinyint not null default 1 comment 'profile synchronization, bit 1 = sync outgoing, bit 2 = sync incoming',
+      last_noticesync datetime default null comment 'last time notices were imported',
+      last_friendsync datetime default null comment 'last time friends were imported',
+      created datetime not null comment 'date this record was created',
+      modified timestamp comment 'date this record was modified',
+      constraint primary key (user_id, foreign_id, service),
+      index foreign_user_user_id_idx (user_id)
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+ create table foreign_subscription (
+      service int not null comment 'service where relationship happens' references foreign_service(id),
+      subscriber int not null comment 'subscriber on foreign service' references foreign_user (id),
+      subscribed int not null comment 'subscribed user' references foreign_user (id),
+      created datetime not null comment 'date this record was created',
+      constraint primary key (service, subscriber, subscribed),
+      index foreign_subscription_subscriber_idx (subscriber),
+      index foreign_subscription_subscribed_idx (subscribed)
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+ create table invitation (
+      code varchar(32) not null primary key comment 'random code for an invitation',
+      user_id int not null comment 'who sent the invitation' references user (id),
+      address varchar(255) not null comment 'invitation sent to',
+      address_type varchar(8) not null comment 'address type ("email", "jabber", "sms")',
+      created datetime not null comment 'date this record was created',
+      index invitation_address_idx (address, address_type),
+      index invitation_user_id_idx (user_id)
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+ create table message (
+     id integer auto_increment primary key comment 'unique identifier',
+     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),
 -    description varchar(140) comment 'descriptive biography',
++    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',
+     modified timestamp comment 'date this record was modified',
+     source varchar(32) comment 'source of comment, like "web", "im", or "clientname"',
+     index message_from_idx (from_profile),
+     index message_to_idx (to_profile),
+     index message_created_idx (created)
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
+ create table notice_inbox (
+     user_id integer not null comment 'user receiving the message' references user (id),
+     notice_id integer not null comment 'notice received' references notice (id),
+     created datetime not null comment 'date the notice was created',
+     source tinyint default 1 comment 'reason it is in the inbox, 1=subscription',
+     constraint primary key (user_id, notice_id),
+     index notice_inbox_notice_id_idx (notice_id)
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+ create table profile_tag (
+    tagger integer not null comment 'user making the tag' references user (id),
+    tagged integer not null comment 'profile tagged' references profile (id),
+    tag varchar(64) not null comment 'hash tag associated with this notice',
+    modified timestamp comment 'date the tag was added',
+    constraint primary key (tagger, tagged, tag),
+    index profile_tag_modified_idx (modified),
+    index profile_tag_tagger_tag_idx (tagger, tag),
+    index profile_tag_tagged_idx (tagged)
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+ create table profile_block (
+    blocker integer not null comment 'user making the block' references user (id),
+    blocked integer not null comment 'profile that is blocked' references profile (id),
+    modified timestamp comment 'date of blocking',
+    constraint primary key (blocker, blocked)
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+ create table user_group (
+     id integer auto_increment primary key comment 'unique identifier',
+     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',
 -) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
++    description text comment 'group description',
+     location varchar(255) comment 'related physical location, if any',
+     original_logo varchar(255) comment 'original size logo',
+     homepage_logo varchar(255) comment 'homepage (profile) size logo',
+     stream_logo varchar(255) comment 'stream-sized logo',
+     mini_logo varchar(255) comment 'mini logo',
+     design_id integer comment 'id of a design' references design(id),
+     created datetime not null comment 'date this record was created',
+     modified timestamp comment 'date this record was modified',
+     index user_group_nickname_idx (nickname)
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
+ create table group_member (
+     group_id integer not null comment 'foreign key to user_group' references user_group (id),
+     profile_id integer not null comment 'foreign key to profile table' references profile (id),
+     is_admin boolean default false comment 'is this user an admin?',
+     created datetime not null comment 'date this record was created',
+     modified timestamp comment 'date this record was modified',
+     constraint primary key (group_id, profile_id),
+     index group_member_profile_id_idx (profile_id),
+     index group_member_created_idx (created)
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+ create table related_group (
+     group_id integer not null comment 'foreign key to user_group' references user_group (id),
+     related_group_id integer not null comment 'foreign key to user_group' references user_group (id),
+     created datetime not null comment 'date this record was created',
+     constraint primary key (group_id, related_group_id)
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+ create table group_inbox (
+     group_id integer not null comment 'group receiving the message' references user_group (id),
+     notice_id integer not null comment 'notice received' references notice (id),
+     created datetime not null comment 'date the notice was created',
+     constraint primary key (group_id, notice_id),
+     index group_inbox_created_idx (created)
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+ create table file (
+     id integer primary key auto_increment,
+     url varchar(255) comment 'destination URL after following redirections',
+     mimetype varchar(50) comment 'mime type of resource',
+     size integer comment 'size of resource when available',
+     title varchar(255) comment 'title of resource when available',
+     date integer(11) comment 'date of resource according to http query',
+     protected integer(1) comment 'true when URL is private (needs login)',
+     filename varchar(255) comment 'if a local file, name of the file',
+     modified timestamp comment 'date this record was modified',
+     unique(url)
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
+ 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',
+     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',
+     height integer comment 'height of oEmbed resource when available',
+     html text comment 'html representation of this oEmbed resource when applicable',
+     title varchar(255) comment 'title of oEmbed resource when available',
+     author_name varchar(50) comment 'author name for this oEmbed resource',
+     author_url varchar(255) comment 'author URL for this oEmbed resource',
+     url varchar(255) comment 'URL for this oEmbed resource when applicable (photo, link)',
+     modified timestamp comment 'date this record was modified'
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
+ create table file_redirection (
+     url varchar(255) primary key comment 'short URL (or any other kind of redirect) for file (id)',
+     file_id integer comment 'short URL for what URL/file' references file (id),
+     redirections integer comment 'redirect count',
+     httpcode integer comment 'HTTP status code (20x, 30x, etc.)',
+     modified timestamp comment 'date this record was modified'
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+ create table file_thumbnail (
+     file_id integer primary key comment 'thumbnail for what URL/file' references file (id),
+     url varchar(255) comment 'URL of thumbnail',
+     width integer comment 'width of thumbnail',
+     height integer comment 'height of thumbnail',
+     modified timestamp comment 'date this record was modified',
+     unique(url)
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+ create table file_to_post (
+     file_id integer comment 'id of URL/file' references file (id),
+     post_id integer comment 'id of the notice it belongs to' references notice (id),
+     modified timestamp comment 'date this record was modified',
+     constraint primary key (file_id, post_id)
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+ create table design (
+     id integer primary key auto_increment comment 'design ID',
+     backgroundcolor integer comment 'main background color',
+     contentcolor integer comment 'content area background color',
+     sidebarcolor integer comment 'sidebar background color',
+     textcolor integer comment 'text color',
+     linkcolor integer comment 'link color',
+     backgroundimage varchar(255) comment 'background image, if any',
+     disposition tinyint default 1 comment 'bit 1 = hide background image, bit 2 = display background image, bit 4 = tile background image'
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+ create table group_block (
+    group_id integer not null comment 'group profile is blocked from' references user_group (id),
+    blocked integer not null comment 'profile that is blocked' references profile (id),
+    blocker integer not null comment 'user making the block' references user (id),
+    modified timestamp comment 'date of blocking',
+    constraint primary key (group_id, blocked)
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+ create table group_alias (
+    alias varchar(64) primary key comment 'additional nickname for the group',
+    group_id integer not null comment 'group profile is blocked from' references user_group (id),
+    modified timestamp comment 'date alias was created',
+    index group_alias_group_id_idx (group_id)
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+ create table session (
+     id varchar(32) primary key comment 'session ID',
+     session_data text comment 'session data',
+     created datetime not null comment 'date this record was created',
+     modified timestamp comment 'date this record was modified',
+     index session_modified_idx (modified)
++) 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;
diff --combined db/statusnet_pg.sql
index 0000000000000000000000000000000000000000,ad34720a23357e405c99a7ff433303385d4df041..b5626d3f4a063ae5eb73c0219bca8444f0e594d0
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,539 +1,550 @@@
+ /* local and remote users have profiles */
+ create sequence profile_seq;
+ create table profile (
+     id bigint default nextval('profile_seq') primary key /* comment 'unique identifier' */,
+     nickname varchar(64) not null /* comment 'nickname or username' */,
+     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' */,
+     location varchar(255) /* comment 'physical location' */,
+     created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */,
+     modified timestamp /* comment 'date this record was modified' */,
+     textsearch tsvector
+ );
+ create index profile_nickname_idx on profile using btree(nickname);
+ create table avatar (
+     profile_id integer not null /* comment 'foreign key to profile table' */ references profile (id) ,
+     original integer default 0 /* comment 'uploaded by user or generated?' */,
+     width integer not null /* comment 'image width' */,
+     height integer not null /* comment 'image height' */,
+     mediatype varchar(32) not null /* comment 'file type' */,
+     filename varchar(255) null /* comment 'local filename, if local' */,
+     url varchar(255) unique /* comment 'avatar location' */,
+     created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */,
+     modified timestamp /* comment 'date this record was modified' */,
+     primary key(profile_id, width, height)
+ );
+ create index avatar_profile_id_idx on avatar using btree(profile_id);
+ create sequence sms_carrier_seq;
+ create table sms_carrier (
+     id bigint default nextval('sms_carrier_seq') primary key /* comment 'primary key for SMS carrier' */,
+     name varchar(64) unique /* comment 'name of the carrier' */,
+     email_pattern varchar(255) not null /* comment 'sprintf pattern for making an email address from a phone number' */,
+     created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */,
+     modified timestamp /* comment 'date this record was modified ' */
+ );
+ create sequence design_seq;
+ create table design (
+     id bigint default nextval('design_seq') /* comment 'design ID'*/,
+     backgroundcolor integer /* comment 'main background color'*/ ,
+     contentcolor integer /*comment 'content area background color'*/ ,
+     sidebarcolor integer /*comment 'sidebar background color'*/ ,
+     textcolor integer /*comment 'text color'*/ ,
+     linkcolor integer /*comment 'link color'*/,
+     backgroundimage varchar(255) /*comment 'background image, if any'*/,
+     disposition int default 1 /*comment 'bit 1 = hide background image, bit 2 = display background image, bit 4 = tile background image'*/,
+     primary key (id)
+ );
+ /* local users */
+ create table "user" (
+     id integer primary key /* comment 'foreign key to profile table' */ references profile (id) ,
+     nickname varchar(64) unique /* comment 'nickname or username, duped in profile' */,
+     password varchar(255) /* comment 'salted password, can be null for OpenID users' */,
+     email varchar(255) unique /* comment 'email address for password recovery etc.' */,
+     incomingemail varchar(255) unique /* comment 'email address for post-by-email' */,
+     emailnotifysub integer default 1 /* comment 'Notify by email of subscriptions' */,
+     emailnotifyfav integer default 1 /* comment 'Notify by email of favorites' */,
+     emailnotifynudge integer default 1 /* comment 'Notify by email of nudges' */,
+     emailnotifymsg integer default 1 /* comment 'Notify by email of direct messages' */,
+     emailnotifyattn integer default 1 /* command 'Notify by email of @-replies' */, 
+     emailmicroid integer default 1 /* comment 'whether to publish email microid' */,
+     language varchar(50) /* comment 'preferred language' */,
+     timezone varchar(50) /* comment 'timezone' */,
+     emailpost integer default 1 /* comment 'Post by email' */,
+     jabber varchar(255) unique /* comment 'jabber ID for notices' */,
+     jabbernotify integer default 0 /* comment 'whether to send notices to jabber' */,
+     jabberreplies integer default 0 /* comment 'whether to send notices to jabber on replies' */,
+     jabbermicroid integer default 1 /* comment 'whether to publish xmpp microid' */,
+     updatefrompresence integer default 0 /* comment 'whether to record updates from Jabber presence notices' */,
+     sms varchar(64) unique /* comment 'sms phone number' */,
+     carrier integer /* comment 'foreign key to sms_carrier' */ references sms_carrier (id) ,
+     smsnotify integer default 0 /* comment 'whether to send notices to SMS' */,
+     smsreplies integer default 0 /* comment 'whether to send notices to SMS on replies' */,
+     smsemail varchar(255) /* comment 'built from sms and carrier' */,
+     uri varchar(255) unique /* comment 'universally unique identifier, usually a tag URI' */,
+     autosubscribe integer default 0 /* comment 'automatically subscribe to users who subscribe to us' */,
+     urlshorteningservice varchar(50) default 'ur1.ca' /* comment 'service to use for auto-shortening URLs' */,
+     inboxed integer default 0 /* comment 'has an inbox been created for this user?' */, 
+     design_id integer /* comment 'id of a design' */references design(id),
+     viewdesigns integer default 1 /* comment 'whether to view user-provided designs'*/,
+     created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */,
+     modified timestamp /* comment 'date this record was modified' */
+ );
+ create index user_smsemail_idx on "user" using btree(smsemail);
+ /* remote people */
+ create table remote_profile (
+     id integer primary key /* comment 'foreign key to profile table' */ references profile (id) ,
+     uri varchar(255) unique /* comment 'universally unique identifier, usually a tag URI' */,
+     postnoticeurl varchar(255) /* comment 'URL we use for posting notices' */,
+     updateprofileurl varchar(255) /* comment 'URL we use for updates to this profile' */,
+     created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */,
+     modified timestamp /* comment 'date this record was modified' */
+ );
+ create table subscription (
+     subscriber integer not null /* comment 'profile listening' */,
+     subscribed integer not null /* comment 'profile being listened to' */,
+     jabber integer default 1 /* comment 'deliver jabber messages' */,
+     sms integer default 1 /* comment 'deliver sms messages' */,
+     token varchar(255) /* comment 'authorization token' */,
+     secret varchar(255) /* comment 'token secret' */,
+     created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */,
+     modified timestamp /* comment 'date this record was modified' */,
+     primary key (subscriber, subscribed)
+ );
+ create index subscription_subscriber_idx on subscription using btree(subscriber);
+ create index subscription_subscribed_idx on subscription using btree(subscribed);
+ create sequence notice_seq;
+ create table notice (
+     id bigint default nextval('notice_seq') primary key /* comment 'unique identifier' */,
+     profile_id integer not null /* comment 'who made the update' */ references profile (id) ,
+     uri varchar(255) unique /* comment 'universally unique identifier, usually a tag URI' */,
+     content varchar(140) /* comment 'update content' */,
+     rendered text /* comment 'HTML version of the content' */,
+     url varchar(255) /* comment 'URL of any attachment (image, video, bookmark, whatever)' */,
+     created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */,
+     modified timestamp /* comment 'date this record was modified' */,
+     reply_to integer /* comment 'notice replied to (usually a guess)' */ references notice (id) ,
+     is_local integer default 0 /* comment 'notice was generated by a user' */,
+     source varchar(32) /* comment 'source of comment, like "web", "im", or "clientname"' */,
+     conversation integer /*id of root notice in this conversation' */ references notice (id)
+ /*    FULLTEXT(content) */
+ );
+ create index notice_profile_id_idx on notice using btree(profile_id);
+ create index notice_created_idx on notice using btree(created);
+ create table notice_source (
+      code varchar(32) primary key not null /* comment 'source code' */,
+      name varchar(255) not null /* comment 'name of the source' */,
+      url varchar(255) not null /* comment 'url to link to' */,
+      created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */,
+      modified timestamp /* comment 'date this record was modified' */
+ );
+ create table reply (
+     notice_id integer not null /* comment 'notice that is the reply' */ references notice (id) ,
+     profile_id integer not null /* comment 'profile replied to' */ references profile (id) ,
+     modified timestamp /* comment 'date this record was modified' */,
+     replied_id integer /* comment 'notice replied to (not used, see notice.reply_to)' */,
+     primary key (notice_id, profile_id)
+ );
+ create index reply_notice_id_idx on reply using btree(notice_id);
+ create index reply_profile_id_idx on reply using btree(profile_id);
+ create index reply_replied_id_idx on reply using btree(replied_id);
+ create table fave (
+     notice_id integer not null /* comment 'notice that is the favorite' */ references notice (id),
+     user_id integer not null /* comment 'user who likes this notice' */ references "user" (id) ,
+     modified timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was modified' */,
+     primary key (notice_id, user_id)
+ );
+ create index fave_notice_id_idx on fave using btree(notice_id);
+ create index fave_user_id_idx on fave using btree(user_id);
+ create index fave_modified_idx on fave using btree(modified);
+ /* tables for OAuth */
+ create table consumer (
+     consumer_key varchar(255) primary key /* comment 'unique identifier, root URL' */,
+     seed char(32) not null /* comment 'seed for new tokens by this consumer' */,
+     created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */,
+     modified timestamp /* comment 'date this record was modified' */
+ );
+ create table token (
+     consumer_key varchar(255) not null /* comment 'unique identifier, root URL' */ references consumer (consumer_key),
+     tok char(32) not null /* comment 'identifying value' */,
+     secret char(32) not null /* comment 'secret value' */,
+     type integer not null default 0 /* comment 'request or access' */,
+     state integer default 0 /* comment 'for requests 0 = initial, 1 = authorized, 2 = used' */,
+     created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */,
+     modified timestamp /* comment 'date this record was modified' */,
+     primary key (consumer_key, tok)
+ );
+ create table nonce (
+     consumer_key varchar(255) not null /* comment 'unique identifier, root URL' */,
+     tok char(32) /* comment 'buggy old value, ignored' */,
+     nonce char(32) null /* comment 'buggy old value, ignored */,
+     ts integer not null /* comment 'timestamp sent' values are epoch, and only used internally */,
+     created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */,
+     modified timestamp /* comment 'date this record was modified' */,
+     primary key (consumer_key, ts, nonce)
+ );
+ /* One-to-many relationship of user to openid_url */
+ create table user_openid (
+     canonical varchar(255) primary key /* comment 'Canonical true URL' */,
+     display varchar(255) not null unique /* comment 'URL for viewing, may be different from canonical' */,
+     user_id integer not null /* comment 'user owning this URL' */ references "user" (id) ,
+     created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */,
+     modified timestamp /* comment 'date this record was modified' */
+ );
+ create index user_openid_user_id_idx on user_openid using btree(user_id);
+ /* These are used by JanRain OpenID library */
+ create table oid_associations (
+     server_url varchar(2047),
+     handle varchar(255),
+     secret bytea,
+     issued integer,
+     lifetime integer,
+     assoc_type varchar(64),
+     primary key (server_url, handle)
+ );
+ create table oid_nonces (
+     server_url varchar(2047),
+     "timestamp" integer,
+     salt character(40),
+     unique (server_url, "timestamp", salt)
+ );
+ create table confirm_address (
+     code varchar(32) not null primary key /* comment 'good random code' */,
+     user_id integer not null /* comment 'user who requested confirmation' */ references "user" (id),
+     address varchar(255) not null /* comment 'address (email, Jabber, SMS, etc.)' */,
+     address_extra varchar(255) not null default '' /* comment 'carrier ID, for SMS' */,
+     address_type varchar(8) not null /* comment 'address type ("email", "jabber", "sms")' */,
+     claimed timestamp /* comment 'date this was claimed for queueing' */,
+     sent timestamp /* comment 'date this was sent for queueing' */,
+     modified timestamp /* comment 'date this record was modified' */
+ );
+ create table remember_me (
+     code varchar(32) not null primary key /* comment 'good random code' */,
+     user_id integer not null /* comment 'user who is logged in' */ references "user" (id),
+     modified timestamp /* comment 'date this record was modified' */
+ );
+ create table queue_item (
+     notice_id integer not null /* comment 'notice queued' */ references notice (id) ,
+     transport varchar(8) not null /* comment 'queue for what? "email", "jabber", "sms", "irc", ...' */,
+     created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */,
+     claimed timestamp /* comment 'date this item was claimed' */,
+     primary key (notice_id, transport)
+ );
+ create index queue_item_created_idx on queue_item using btree(created);
+ /* Hash tags */
+ create table notice_tag (
+     tag varchar( 64 ) not null /* comment 'hash tag associated with this notice' */,
+     notice_id integer not null /* comment 'notice tagged' */ references notice (id) ,
+     created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */,
+     primary key (tag, notice_id)
+ );
+ create index notice_tag_created_idx on notice_tag using btree(created);
+ /* Synching with foreign services */
+ create table foreign_service (
+      id int not null primary key /* comment 'numeric key for service' */,
+      name varchar(32) not null unique /* comment 'name of the service' */,
+      description varchar(255) /* comment 'description' */,
+      created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */,
+      modified timestamp /* comment 'date this record was modified' */
+ );
+ create table foreign_user (
+      id int not null unique /* comment 'unique numeric key on foreign service' */,
+      service int not null /* comment 'foreign key to service' */ references foreign_service(id) ,
+      uri varchar(255) not null unique /* comment 'identifying URI' */,
+      nickname varchar(255) /* comment 'nickname on foreign service' */,
+      created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */,
+      modified timestamp /* comment 'date this record was modified' */,
+      
+      primary key (id, service)
+ );
+ create table foreign_link (
+      user_id int /* comment 'link to user on this system, if exists' */ references "user" (id),
+      foreign_id int /* comment 'link' */ references foreign_user (id),
+      service int not null /* comment 'foreign key to service' */ references foreign_service (id),
+      credentials varchar(255) /* comment 'authc credentials, typically a password' */,
+      noticesync int not null default 1 /* comment 'notice synchronisation, bit 1 = sync outgoing, bit 2 = sync incoming, bit 3 = filter local replies' */,
+      friendsync int not null default 2 /* comment 'friend synchronisation, bit 1 = sync outgoing, bit 2 = sync incoming */, 
+      profilesync int not null default 1 /* comment 'profile synchronization, bit 1 = sync outgoing, bit 2 = sync incoming' */,
+      last_noticesync timestamp default null /* comment 'last time notices were imported' */,
+      last_friendsync timestamp default null /* comment 'last time friends were imported' */,
+      created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */,
+      modified timestamp /* comment 'date this record was modified' */,
+      primary key (user_id,foreign_id,service)
+ );
+ create index foreign_user_user_id_idx on foreign_link using btree(user_id);
+ create table foreign_subscription (
+      service int not null /* comment 'service where relationship happens' */ references foreign_service(id) ,
+      subscriber int not null /* comment 'subscriber on foreign service' */ ,
+      subscribed int not null /* comment 'subscribed user' */ ,
+      created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */,
+      
+      primary key (service, subscriber, subscribed)
+ );
+ create index foreign_subscription_subscriber_idx on foreign_subscription using btree(subscriber);
+ create index foreign_subscription_subscribed_idx on foreign_subscription using btree(subscribed);
+ create table invitation (
+      code varchar(32) not null primary key /* comment 'random code for an invitation' */,
+      user_id int not null /* comment 'who sent the invitation' */ references "user" (id),
+      address varchar(255) not null /* comment 'invitation sent to' */,
+      address_type varchar(8) not null /* comment 'address type ("email", "jabber", "sms") '*/,
+      created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */
+ );
+ create index invitation_address_idx on invitation using btree(address,address_type);
+ create index invitation_user_id_idx on invitation using btree(user_id);
+ create sequence message_seq;
+ create table message (
+     id bigint default nextval('message_seq') primary key /* comment 'unique identifier' */,
+     uri varchar(255) unique /* 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' */,
+     rendered text /* comment 'HTML version of the content' */,
+     url varchar(255) /* comment 'URL of any attachment (image, video, bookmark, whatever)' */,
+     created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */,
+     modified timestamp /* comment 'date this record was modified' */,
+     source varchar(32) /* comment 'source of comment, like "web", "im", or "clientname"' */
+     
+ );
+ create index message_from_idx on message using btree(from_profile);
+ create index message_to_idx on message using btree(to_profile);
+ create index message_created_idx on message using btree(created);
+ create table notice_inbox (
+     user_id integer not null /* comment 'user receiving the message' */ references "user" (id),
+     notice_id integer not null /* comment 'notice received' */ references notice (id),
+     created timestamp not null default CURRENT_TIMESTAMP /* comment 'date the notice was created' */,
+     source integer default 1 /* comment 'reason it is in the inbox: 1=subscription' */,
+     primary key (user_id, notice_id)
+ );
+ create index notice_inbox_notice_id_idx on notice_inbox using btree(notice_id);
+ create table profile_tag (
+    tagger integer not null /* comment 'user making the tag' */ references "user" (id),
+    tagged integer not null /* comment 'profile tagged' */ references profile (id),
+    tag varchar(64) not null /* comment 'hash tag associated with this notice' */,
+    modified timestamp /* comment 'date the tag was added' */,
+    primary key (tagger, tagged, tag)
+ );
+ create index profile_tag_modified_idx on profile_tag using btree(modified);
+ create index profile_tag_tagger_tag_idx on profile_tag using btree(tagger,tag);
+ create table profile_block (
+    blocker integer not null /* comment 'user making the block' */ references "user" (id),
+    blocked integer not null /* comment 'profile that is blocked' */ references profile (id),
+    modified timestamp /* comment 'date of blocking' */,
+    primary key (blocker, blocked)
+ );
+ create sequence user_group_seq;
+ create table user_group (
+     id bigint default nextval('user_group_seq') primary key /* comment 'unique identifier' */,
+     nickname varchar(64) unique /* 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' */,
+     location varchar(255) /* comment 'related physical location, if any' */,
+     original_logo varchar(255) /* comment 'original size logo' */,
+     homepage_logo varchar(255) /* comment 'homepage (profile) size logo' */,
+     stream_logo varchar(255) /* comment 'stream-sized logo' */,
+     mini_logo varchar(255) /* comment 'mini logo' */,
+     design_id integer /*comment 'id of a design' */ references design(id),
+     created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */,
+     modified timestamp /* comment 'date this record was modified' */
+ );
+ create index user_group_nickname_idx on user_group using btree(nickname);
+ create table group_member (
+     group_id integer not null /* comment 'foreign key to user_group' */ references user_group (id),
+     profile_id integer not null /* comment 'foreign key to profile table' */ references profile (id),
+     is_admin integer default 0 /* comment 'is this user an admin?' */,
+     created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */,
+     modified timestamp /* comment 'date this record was modified' */,
+     primary key (group_id, profile_id)
+ );
+ create table related_group (
+     group_id integer not null /* comment 'foreign key to user_group' */ references user_group (id) ,
+     related_group_id integer not null /* comment 'foreign key to user_group' */ references user_group (id),
+     created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */,
+     primary key (group_id, related_group_id)
+ );
+ create table group_inbox (
+     group_id integer not null /* comment 'group receiving the message' references user_group (id) */,
+     notice_id integer not null /* comment 'notice received' references notice (id) */,
+     created timestamp not null default CURRENT_TIMESTAMP /* comment 'date the notice was created' */,
+     primary key (group_id, notice_id)
+ );
+ create index group_inbox_created_idx on group_inbox using btree(created);
+ /*attachments and URLs stuff */
+ create sequence file_seq;
+ create table file (
+     id bigint default nextval('file_seq') primary key /* comment 'unique identifier' */,
+     url varchar(255) unique, 
+     mimetype varchar(50), 
+     size integer, 
+     title varchar(255), 
+     date integer, 
+     protected integer,
+     filename text /* comment 'if a local file, name of the file' */,
+     modified timestamp default CURRENT_TIMESTAMP /* comment 'date this record was modified'*/
+ );
+ create sequence file_oembed_seq;
+ create table file_oembed (
+     file_id bigint default nextval('file_oembed_seq') primary key /* comment 'unique identifier' */,
+     version varchar(20),
+     type varchar(20),
+     provider varchar(50),
+     provider_url varchar(255),
+     width integer,
+     height integer,
+     html text,
+     title varchar(255),
+     author_name varchar(50), 
+     author_url varchar(255), 
+     url varchar(255) 
+ );
+ create sequence file_redirection_seq;
+ create table file_redirection (
+     url varchar(255) primary key, 
+     file_id bigint, 
+     redirections integer, 
+     httpcode integer
+ );
+ create sequence file_thumbnail_seq;
+ create table file_thumbnail (
+     file_id bigint primary key, 
+     url varchar(255) unique, 
+     width integer, 
+     height integer 
+ );
+ create sequence file_to_post_seq;
+ create table file_to_post (
+     file_id bigint, 
+     post_id bigint, 
+     primary key (file_id, post_id)
+ );
+ create table group_block (
+    group_id integer not null /* comment 'group profile is blocked from' */ references user_group (id),
+    blocked integer not null /* comment 'profile that is blocked' */references profile (id),
+    blocker integer not null /* comment 'user making the block'*/ references "user" (id),
+    modified timestamp /* comment 'date of blocking'*/ ,
+    primary key (group_id, blocked)
+ );
+ create table group_alias (
+    alias varchar(64) /* comment 'additional nickname for the group'*/ ,
+    group_id integer not null /* comment 'group profile is blocked from'*/ references user_group (id),
+    modified timestamp /* comment 'date alias was created'*/,
+    primary key (alias)
+ );
+ create index group_alias_group_id_idx on group_alias (group_id);
+ create table session (
+     id varchar(32) primary key /* comment 'session ID'*/,
+     session_data text /* comment 'session data'*/,
+     created timestamp not null DEFAULT CURRENT_TIMESTAMP /* comment 'date this record was created'*/,
+     modified integer DEFAULT extract(epoch from CURRENT_TIMESTAMP) /* comment 'date this record was modified'*/ 
+ );
+ 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 */
+ create index textsearch_idx on profile using gist(textsearch);
+ create index noticecontent_idx on notice using gist(to_tsvector('english',content));
+ 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);
diff --combined doc-src/help
index 49b521983fd4faa06d73bb82ad63f824ba09099b,8d7acf63b4738e65bfe53fc98b43515bf322b4a5..93300ab242e3c517cc4f25b54d5c3ad898ef7e9c
@@@ -26,8 -26,9 +26,8 @@@ Here are some documents that you might 
  * [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 Laconica source code
- * [Badge](%%doc.badge%%) - How to put a Laconica badge on your blog or homepage
+ * [Source](%%doc.source%%) - How to get the StatusNet source code
+ * [Badge](%%doc.badge%%) - How to put a StatusNet badge on your blog or homepage
  * [Bookmarklet](%%doc.bookmarklet%%) - Bookmarklet for posting Web pages
diff --combined index.php
index b9ce43c607bbca4a852d362e961e9d015fa18a21,7669778f64e68a60081da00feb564494e4f78117..e2296549f3efd96e5b5ff284265ad14152ff5d08
+++ b/index.php
@@@ -1,7 -1,7 +1,7 @@@
  <?php
  /**
-  * Laconica - a distributed open-source microblogging tool
-  * Copyright (C) 2008, 2009, Control Yourself, Inc.
+  * 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
@@@ -18,7 -18,8 +18,8 @@@
   */
  
  define('INSTALLDIR', dirname(__FILE__));
- define('LACONICA', true);
+ define('STATUSNET', true);
+ define('LACONICA', true); // compatibility
  
  require_once INSTALLDIR . '/lib/common.php';
  
@@@ -93,7 -94,7 +94,7 @@@ function checkMirror($action_obj, $args
          // on the master DB
  
          $config['db']['database_rw'] = $config['db']['database'];
-         $config['db']['ini_rw'] = INSTALLDIR.'/classes/laconica.ini';
+         $config['db']['ini_rw'] = INSTALLDIR.'/classes/statusnet.ini';
  
          foreach ($alwaysRW as $table) {
              $config['db']['table_'.$table] = 'rw';
      }
  }
  
 +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
      // 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';
diff --combined install.php
index a03dfa775071a6352b2cb85c65d6614cd36f0cde,42d848911b820c9a599cda32a8849116834e94a2..dc42a5b3dba2d5d477c0e897929526f17a69ecf6
@@@ -1,7 -1,7 +1,7 @@@
  <?php
  /**
-  * Laconica - a distributed open-source microblogging tool
-  * Copyright (C) 2009, Control Yourself, Inc.
+  * StatusNet - the distributed open-source microblogging tool
+  * Copyright (C) 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
@@@ -87,7 -87,7 +87,7 @@@ function checkPrereqs(
  function checkExtension($name)
  {
      if (!extension_loaded($name)) {
-         if (!dl($name.'.so')) {
+         if (!@dl($name.'.so')) {
              return false;
          }
      }
@@@ -129,7 -129,7 +129,7 @@@ function showForm(
                  <p class="form_guide">Database hostname</p>
              </li>
              <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 />
@@@ -181,7 -181,7 +181,7 @@@ function handlePost(
      $fancy    = !empty($_POST['fancy']);
      $server = $_SERVER['HTTP_HOST'];
      $path = substr(dirname($_SERVER['PHP_SELF']), 1);
-     
  ?>
      <dl class="system_notice">
          <dt>Page notice</dt>
              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;
          default:
      }
-     
      if (!$db) {
          // database connection failed, do not move on to create config file.
          return false;
      }
-     
      updateStatus("Writing config file...");
      $res = writeConf($sitename, $server, $path, $fancy, $db);
-     
      if (!$res) {
          updateStatus("Can't write config file.", true);
          showForm();
          return;
      }
-     
      /*
          TODO https needs to be considered
      */
      $link = "http://".$server.'/'.$path;
-     
-     updateStatus("Laconica has been installed at $link");
-     updateStatus("You can visit your <a href='$link'>new Laconica site</a>.");
+     updateStatus("StatusNet has been installed at $link");
+     updateStatus("You can visit your <a href='$link'>new StatusNet site</a>.");
  ?>
  
  <?php
@@@ -266,7 -268,7 +268,7 @@@ function pgsql_db_installer($host, $dat
    updateStatus("Starting installation...");
    updateStatus("Checking database...");
    $conn = pg_connect($connstring);
-   
    if ($conn ===false) {
      updateStatus("Failed to connect to database: $connstring");
      showForm();
    //ensure database encoding is UTF8
    $record = pg_fetch_object(pg_query($conn, 'SHOW server_encoding'));
    if ($record->server_encoding != 'UTF8') {
-     updateStatus("Laconica requires UTF8 character encoding. Your database is ". htmlentities($record->server_encoding));
+     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/laconica_pg.sql', $conn, 'pgsql');
-   
+   $res = runDbScript(INSTALLDIR.'/db/statusnet_pg.sql', $conn, 'pgsql');
    if ($res === false) {
        updateStatus("Can't run database script.", true);
        showForm();
    else {
      $sqlUrl = "pgsql://$username:$password@$host/$database";
    }
-   
    $db = array('type' => 'pgsql', 'database' => $sqlUrl);
-   
    return $db;
  }
  
@@@ -335,7 -337,7 +337,7 @@@ function mysql_db_installer($host, $dat
        return false;
    }
    updateStatus("Running database script...");
-   $res = runDbScript(INSTALLDIR.'/db/laconica.sql', $conn);
+   $res = runDbScript(INSTALLDIR.'/db/statusnet.sql', $conn);
    if ($res === false) {
        updateStatus("Can't run database script.", true);
        showForm();
            return false;
        }
    }
-       
        $sqlUrl = "mysqli://$username:$password@$host/$database";
        $db = array('type' => 'mysql', 'database' => $sqlUrl);
        return $db;
@@@ -363,23 -365,23 +365,23 @@@ function writeConf($sitename, $server, 
  {
      // assemble configuration file in a string
      $cfg =  "<?php\n".
-             "if (!defined('LACONICA')) { exit(1); }\n\n".
-             
+             "if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }\n\n".
              // site name
              "\$config['site']['name'] = '$sitename';\n\n".
-             
              // site location
              "\$config['site']['server'] = '$server';\n".
              "\$config['site']['path'] = '$path'; \n\n".
-             
              // checks if fancy URLs are enabled
              ($fancy ? "\$config['site']['fancy'] = true;\n\n":'').
-             
              // database
              "\$config['db']['database'] = '{$db['database']}';\n\n".
 -            ($db['type'] == 'pgsql' ? "\$config['db']['quote_identifiers'] = true;\n\n":'').
 +            ($type == 'pgsql' ? "\$config['db']['quote_identifiers'] = true;\n\n":'').
              "\$config['db']['type'] = '{$db['type']}';\n\n".
-             
              "?>";
      // write configuration file out to install directory
      $res = file_put_contents(INSTALLDIR.'/config.php', $cfg);
@@@ -396,18 -398,25 +398,25 @@@ function runDbScript($filename, $conn, 
          if (!mb_strlen($stmt)) {
              continue;
          }
+         // FIXME: use PEAR::DB or PDO instead of our own switch
          switch ($type) {
          case 'mysql':
              $res = mysql_query($stmt, $conn);
+             if ($res === false) {
+                 $error = mysql_error();
+             }
              break;
          case 'pgsql':
              $res = pg_query($conn, $stmt);
+             if ($res === false) {
+                 $error = pg_last_error();
+             }
              break;
          default:
              updateStatus("runDbScript() error: unknown database type ". $type ." provided.");
          }
          if ($res === false) {
-             updateStatus("FAILED SQL: $stmt");
+             updateStatus("ERROR ($error) for SQL '$stmt'");
              return $res;
          }
      }
  
  ?>
  <?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 Laconica</title>
+         <title>Install StatusNet</title>
        <link rel="shortcut icon" href="favicon.ico"/>
          <link rel="stylesheet" type="text/css" href="theme/default/css/display.css?version=0.8" media="screen, projection, tv"/>
          <!--[if IE]><link rel="stylesheet" type="text/css" href="theme/base/css/ie.css?version=0.8" /><![endif]-->
              <div id="header">
                  <address id="site_contact" class="vcard">
                      <a class="url home bookmark" href=".">
-                         <img class="logo photo" src="theme/default/logo.png" alt="Laconica"/>
-                         <span class="fn org">Laconica</span>
+                         <img class="logo photo" src="theme/default/logo.png" alt="StatusNet"/>
+                         <span class="fn org">StatusNet</span>
                      </a>
                  </address>
              </div>
              <div id="core">
                  <div id="content">
-                     <h1>Install Laconica</h1>
+                     <h1>Install StatusNet</h1>
  <?php main(); ?>
                  </div>
              </div>
diff --combined js/util.js
index 4f731f30a1e62aa6e9d0b6001f2aa62cc889072c,2165957c3b925a929e121826811c8a541ea9e0dd..0a943512f2a33c4bd7c551a43c6f71d07e47c278
@@@ -1,6 -1,6 +1,6 @@@
  /*
-  * Laconica - a distributed open-source microblogging tool
-  * Copyright (C) 2008, Controlez-Vous, Inc.
+  * StatusNet - a distributed open-source microblogging tool
+  * Copyright (C) 2008, 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
@@@ -21,9 -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");
                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();
                                                                                                                                                                }
                                                                                                                                                                else {
                                                                                                                                                                        $("#notice_data-text").val("");
 -                                                                                                                                                                      counter();
 +                                                                                     if (maxLength > 0) {
 +                                                                                          counter();
 +                                                                                     }
                                                                                                                                                                }
                                                                                                                                                        }
                                                                                                                                                }
                                                                                                $("#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 9a7c69124d4ad8928139144ad956e384f029c863,798116163798b728fd2df6ae335dd57c2b944629..a004a3ed992f08a507e71763ab4f61887df302ba
@@@ -1,6 -1,6 +1,6 @@@
  <?php
  /**
-  * Laconica, the distributed open-source microblogging tool
+  * StatusNet, the distributed open-source microblogging tool
   *
   * Base class for account settings actions
   *
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   *
   * @category  Settings
-  * @package   Laconica
-  * @author    Evan Prodromou <evan@controlyourself.ca>
-  * @copyright 2008-2009 Control Yourself, Inc.
+  * @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://laconi.ca/
+  * @link      http://status.net/
   */
  
- if (!defined('LACONICA')) {
+ if (!defined('STATUSNET') && !defined('LACONICA')) {
      exit(1);
  }
  
@@@ -37,10 -37,10 +37,10 @@@ require_once INSTALLDIR.'/lib/settingsa
   * Base class for account settings actions
   *
   * @category Settings
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
+  * @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://laconi.ca/
+  * @link     http://status.net/
   *
   * @see      Widget
   */
@@@ -66,10 -66,10 +66,10 @@@ class AccountSettingsAction extends Set
   * A widget for showing the settings group local nav menu
   *
   * @category Widget
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
+  * @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://laconi.ca/
+  * @link     http://status.net/
   *
   * @see      HTMLOutputter
   */
@@@ -98,39 -98,42 +98,39 @@@ class AccountSettingsNav extends Widge
  
      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');
diff --combined lib/action.php
index ebd7227195b5ce82b7deb2eb7aeb26e87708739a,fafb2c6fc4c131a2340ed2bce2fcef0e22879779..8056cb9ecb375f92d7a0e1235027f6e5f2a37575
@@@ -1,6 -1,6 +1,6 @@@
  <?php
  /**
-  * Laconica, the distributed open-source microblogging tool
+  * StatusNet, the distributed open-source microblogging tool
   *
   * Base class for all actions (~views)
   *
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   *
   * @category  Action
-  * @package   Laconica
-  * @author    Evan Prodromou <evan@controlyourself.ca>
-  * @author    Sarven Capadisli <csarven@controlyourself.ca>
-  * @copyright 2008 Control Yourself, Inc.
+  * @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://laconi.ca/
+  * @link      http://status.net/
   */
  
- if (!defined('LACONICA')) {
+ if (!defined('STATUSNET') && !defined('LACONICA')) {
      exit(1);
  }
  
@@@ -45,11 -45,11 +45,11 @@@ require_once INSTALLDIR.'/lib/htmloutpu
   * model classes to read and write to the database; and doing ouput.
   *
   * @category Output
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
-  * @author   Sarven Capadisli <csarven@controlyourself.ca>
+  * @package  StatusNet
+  * @author   Evan Prodromou <evan@status.net>
+  * @author   Sarven Capadisli <csarven@status.net>
   * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
-  * @link     http://laconi.ca/
+  * @link     http://status.net/
   *
   * @see      HTMLOutputter
   */
@@@ -120,17 -120,14 +120,17 @@@ class Action extends HTMLOutputter // l
      {
          // XXX: attributes (profile?)
          $this->elementStart('head');
 -        $this->showTitle();
 -        $this->showShortcutIcon();
 -        $this->showStylesheets();
 -        $this->showScripts();
 -        $this->showOpenSearch();
 -        $this->showFeeds();
 -        $this->showDescription();
 -        $this->extraHead();
 +        if (Event::handle('StartHeadChildren', array($this))) {
 +            $this->showTitle();
 +            $this->showShortcutIcon();
 +            $this->showStylesheets();
 +            $this->showScripts();
 +            $this->showOpenSearch();
 +            $this->showFeeds();
 +            $this->showDescription();
 +            $this->extraHead();
 +            Event::handle('EndHeadChildren', array($this));
 +        }
          $this->elementEnd('head');
      }
  
      {
          if (Event::handle('StartShowStyles', array($this))) {
  
-             if (Event::handle('StartShowLaconicaStyles', array($this))) {
+             // Use old name for StatusNet for compatibility on events
+             if (Event::handle('StartShowStatusNetStyles', array($this)) &&
+                 Event::handle('StartShowLaconicaStyles', array($this))) {
                  $this->cssLink('css/display.css',null,'screen, projection, tv');
                  if (common_config('site', 'mobile')) {
                      // TODO: "handheld" CSS for other mobile devices
                      $this->cssLink('css/mobile.css','base','only screen and (max-device-width: 480px)'); // Mobile WebKit
                  }
                  $this->cssLink('css/print.css','base','print');
+                 Event::handle('EndShowStatusNetStyles', array($this));
                  Event::handle('EndShowLaconicaStyles', array($this));
              }
  
              if (Event::handle('StartShowUAStyles', array($this))) {
                  $this->comment('[if IE]><link rel="stylesheet" type="text/css" '.
-                                'href="'.theme_path('css/ie.css', 'base').'?version='.LACONICA_VERSION.'" /><![endif]');
+                                'href="'.theme_path('css/ie.css', 'base').'?version='.STATUSNET_VERSION.'" /><![endif]');
                  foreach (array(6,7) as $ver) {
                      if (file_exists(theme_file('css/ie'.$ver.'.css', 'base'))) {
                          // Yes, IE people should be put in jail.
                          $this->comment('[if lte IE '.$ver.']><link rel="stylesheet" type="text/css" '.
-                                        'href="'.theme_path('css/ie'.$ver.'.css', 'base').'?version='.LACONICA_VERSION.'" /><![endif]');
+                                        'href="'.theme_path('css/ie'.$ver.'.css', 'base').'?version='.STATUSNET_VERSION.'" /><![endif]');
                      }
                  }
                  $this->comment('[if IE]><link rel="stylesheet" type="text/css" '.
-                                'href="'.theme_path('css/ie.css', null).'?version='.LACONICA_VERSION.'" /><![endif]');
+                                'href="'.theme_path('css/ie.css', null).'?version='.STATUSNET_VERSION.'" /><![endif]');
                  Event::handle('EndShowUAStyles', array($this));
              }
  
                  $this->script('js/jquery.joverlay.min.js');
                  Event::handle('EndShowJQueryScripts', array($this));
              }
-             if (Event::handle('StartShowLaconicaScripts', array($this))) {
+             if (Event::handle('StartShowStatusNetScripts', array($this)) &&
+                 Event::handle('StartShowLaconicaScripts', array($this))) {
                  $this->script('js/xbImportNode.js');
                  $this->script('js/util.js');
                  // Frame-busting code to avoid clickjacking attacks.
                  $this->element('script', array('type' => 'text/javascript'),
                                 'if (window.top !== window.self) { window.top.location.href = window.self.location.href; }');
+                 Event::handle('EndShowStatusNetScripts', array($this));
                  Event::handle('EndShowLaconicaScripts', array($this));
              }
              Event::handle('EndShowScripts', array($this));
                                  _('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');
      function showLicenses()
      {
          $this->elementStart('dl', array('id' => 'licenses'));
-         $this->showLaconicaLicense();
+         $this->showStatusNetLicense();
          $this->showContentLicense();
          $this->elementEnd('dl');
      }
  
      /**
-      * Show Laconica license.
+      * Show StatusNet license.
       *
       * @return nothing
       */
-     function showLaconicaLicense()
+     function showStatusNetLicense()
      {
-         $this->element('dt', array('id' => 'site_laconica_license'), _('Laconica software license'));
+         $this->element('dt', array('id' => 'site_statusnet_license'), _('StatusNet software license'));
          $this->elementStart('dd', null);
          if (common_config('site', 'broughtby')) {
              $instr = _('**%%site.name%%** is a microblogging service brought to you by [%%site.broughtby%%](%%site.broughtbyurl%%). ');
          } else {
              $instr = _('**%%site.name%%** is a microblogging service. ');
          }
-         $instr .= sprintf(_('It runs the [Laconica](http://laconi.ca/) microblogging software, version %s, available under the [GNU Affero General Public License](http://www.fsf.org/licensing/licenses/agpl-3.0.html).'), LACONICA_VERSION);
+         $instr .= sprintf(_('It runs the [StatusNet](http://status.net/) microblogging software, version %s, available under the [GNU Affero General Public License](http://www.fsf.org/licensing/licenses/agpl-3.0.html).'), STATUSNET_VERSION);
          $output = common_markup_to_html($instr);
          $this->raw($output);
          $this->elementEnd('dd');
       */
      function showContentLicense()
      {
-         $this->element('dt', array('id' => 'site_content_license'), _('Laconica software license'));
+         $this->element('dt', array('id' => 'site_content_license'), _('Site content license'));
          $this->elementStart('dd', array('id' => 'site_content_license_cc'));
          $this->elementStart('p');
          $this->element('img', array('id' => 'license_cc',
diff --combined lib/command.php
index 371386dc58ea516155649ff895ed9eb37e7836eb,91a20b81092922abc8769df91b274ff95271dc3f..cae7a829872f98a3266cb399dd494e1d1a470d6a
@@@ -1,7 -1,7 +1,7 @@@
  <?php
  /*
-  * Laconica - a distributed open-source microblogging tool
-  * Copyright (C) 2008, 2009, Control Yourself, Inc.
+  * 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
@@@ -17,7 -17,7 +17,7 @@@
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  
- if (!defined('LACONICA')) { exit(1); }
+ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
  
  require_once(INSTALLDIR.'/lib/channel.php');
  
@@@ -211,20 -211,16 +211,20 @@@ class MessageCommand extends Comman
      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) {
diff --combined lib/common.php
index 31b81da597218c80ea85ebe1eb1e46c998785f72,39d4ffc9b9c3999dd715eea61b8121f8391fcec3..3b21b548cf9ef81ca0cb689b6bfd879e08117a90
@@@ -1,7 -1,7 +1,7 @@@
  <?php
  /*
-  * Laconica - a distributed open-source microblogging tool
-  * Copyright (C) 2008, 2009, Control Yourself, Inc.
+  * 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
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  
- if (!defined('LACONICA')) { exit(1); }
+ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
  
- define('LACONICA_VERSION', '0.9.0dev');
 -define('STATUSNET_VERSION', '0.8.1');
++define('STATUSNET_VERSION', '0.9.0dev');
+ define('LACONICA_VERSION', STATUSNET_VERSION); // compatibility
  
- // XXX: move these to class variables
 -define('STATUSNET_CODENAME', 'Second Guessing');
++define('STATUSNET_CODENAME', 'Stand');
  
  define('AVATAR_PROFILE_SIZE', 96);
  define('AVATAR_STREAM_SIZE', 48);
@@@ -96,7 -97,7 +97,7 @@@ if (isset($path)) 
  
  $config =
    array('site' =>
-         array('name' => 'Just another Laconica microblog',
+         array('name' => 'Just another StatusNet microblog',
                'server' => $_server,
                'theme' => 'default',
                'path' => $_path,
                '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
 +              'dupelimit' => 60, # default for same person saying the same thing
 +              'textlimit' => 140,
 +              ),
          'syslog' =>
-         array('appname' => 'laconica', # for 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' => 'laconica',
+               'queue_basename' => 'statusnet',
                'stomp_username' => null,
                'stomp_password' => null,
                ),
          array('blacklist' => array(),
                'featured' => array()),
          'profile' =>
 -        array('banned' => array()),
 +        array('banned' => array(),
 +              'biolimit' => null),
          'avatar' =>
          array('server' => null,
                'dir' => INSTALLDIR . '/avatar/',
                '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' =>
          'twitterbridge' =>
          array('enabled' => false),
          'integration' =>
-         array('source' => 'Laconica', # source attribute for Twitter
+         array('source' => 'StatusNet', # source attribute for Twitter
                'taguri' => $_server.',2009'), # base for tag URIs
        'twitter' =>
        array('consumer_key'    => null,
          'snapshot' =>
          array('run' => 'web',
                'frequency' => 10000,
-               'reporturl' => 'http://laconi.ca/stats/report'),
+               'reporturl' => 'http://status.net/stats/report'),
          'attachments' =>
          array('server' => null,
                'dir' => INSTALLDIR . '/file/',
          'filecommand' => '/usr/bin/file',
          ),
          'group' =>
 -        array('maxaliases' => 3),
 +        array('maxaliases' => 3,
 +              'desclimit' => null),
          'oohembed' => array('endpoint' => 'http://oohembed.com/oohembed/'),
          'search' =>
          array('type' => 'fulltext'),
                'linkcolor' => null,
                'backgroundimage' => null,
                'disposition' => null),
 +        'notice' =>
 +        array('contentlimit' => null),
 +        'message' =>
 +        array('contentlimit' => null),
          );
  
  $config['db'] = &PEAR::getStaticProperty('DB_DataObject','options');
@@@ -348,10 -344,14 +349,14 @@@ function addPlugin($name, $attrs = null
  if (isset($conffile)) {
      $_config_files = array($conffile);
  } else {
-     $_config_files = array('/etc/laconica/laconica.php',
+     $_config_files = array('/etc/statusnet/statusnet.php',
+                            '/etc/statusnet/laconica.php',
+                            '/etc/laconica/laconica.php',
+                            '/etc/statusnet/'.$_server.'.php',
                             '/etc/laconica/'.$_server.'.php');
  
      if (strlen($_path) > 0) {
+         $_config_files[] = '/etc/statusnet/'.$_server.'_'.$_path.'.php';
          $_config_files[] = '/etc/laconica/'.$_server.'_'.$_path.'.php';
      }
  
@@@ -375,33 -375,21 +380,33 @@@ function _have_config(
  
  // XXX: Throw a conniption if database not installed
  
- // Fixup for laconica.ini
+ // Fixup for statusnet.ini
  
  $_db_name = substr($config['db']['database'], strrpos($config['db']['database'], '/') + 1);
  
- if ($_db_name != 'laconica' && !array_key_exists('ini_'.$_db_name, $config['db'])) {
-     $config['db']['ini_'.$_db_name] = INSTALLDIR.'/classes/laconica.ini';
+ 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';
@@@ -417,14 -405,24 +422,14 @@@ require_once INSTALLDIR.'/lib/twitter.p
  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 --combined lib/facebookaction.php
index 1e0b2bda7a6209421f5876bfb94e7b12604672f4,ca67a094af0442821f5c74034199393ea70ba600..b86276a0aba0c47ad8e1c136dd48f05e32784fd1
@@@ -1,6 -1,6 +1,6 @@@
  <?php
  /**
-  * Laconica, the distributed open-source microblogging tool
+  * StatusNet, the distributed open-source microblogging tool
   *
   * Low-level generator for HTML
   *
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   *
   * @category  Faceboook
-  * @package   Laconica
-  * @author    Zach Copley <zach@controlyourself.ca>
-  * @copyright 2008 Control Yourself, Inc.
+  * @package   StatusNet
+  * @author    Zach Copley <zach@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://laconi.ca/
+  * @link      http://status.net/
   */
  
- if (!defined('LACONICA'))
+ if (!defined('STATUSNET') && !defined('LACONICA'))
  {
      exit(1);
  }
@@@ -35,6 -35,7 +35,6 @@@
  require_once INSTALLDIR.'/lib/facebookutil.php';
  require_once INSTALLDIR.'/lib/noticeform.php';
  
 -
  class FacebookAction extends Action
  {
  
  
      }
  
 -
      // Make this into a widget later
      function showLocalNav()
      {
          $this->endHTML();
      }
  
 -
      function showInstructions()
      {
  
          $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');
          $this->elementEnd('div');
      }
  
 -
      function showLoginForm($msg = null)
      {
  
  
      }
  
 -
      function updateProfileBox($notice)
      {
  
           display:inline-block;
           }
  
-      #facebook_laconica_app {
+      #facebook_statusnet_app {
       text-indent:-9999px;
       height:16px;
       width:16px;
          $this->xw->openURI('php://output');
      }
  
 -
      /**
       * Generate pagination links
       *
          } 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;
              }
          }
@@@ -662,7 -672,7 +662,7 @@@ class FacebookProfileBoxNotice extends 
          $this->app_uri = 'http://apps.facebook.com/' . $app_props['canvas_name'];
          $this->app_name = $app_props['application_name'];
  
-         $this->out->elementStart('a', array('id' => 'facebook_laconica_app',
+         $this->out->elementStart('a', array('id' => 'facebook_statusnet_app',
                                              'href' => $this->app_uri));
          $this->out->text($this->app_name);
          $this->out->elementEnd('a');
diff --combined lib/groupeditform.php
index 47e62d469ec3486d63d3af14a80359c512e5f0bc,a649c2ee3b258cecdecc67eb6bd2dea69064fab4..433f6a13871ccec4de2ad2d01837e95412df59ed
@@@ -1,6 -1,6 +1,6 @@@
  <?php
  /**
-  * Laconica, the distributed open-source microblogging tool
+  * StatusNet, the distributed open-source microblogging tool
   *
   * Form for editing a group
   *
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   *
   * @category  Form
-  * @package   Laconica
-  * @author    Evan Prodromou <evan@controlyourself.ca>
-  * @author    Sarven Capadisli <csarven@controlyourself.ca>
-  * @copyright 2009 Control Yourself, Inc.
+  * @package   StatusNet
+  * @author    Evan Prodromou <evan@status.net>
+  * @author    Sarven Capadisli <csarven@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://laconi.ca/
+  * @link      http://status.net/
   */
  
- if (!defined('LACONICA')) {
+ if (!defined('STATUSNET') && !defined('LACONICA')) {
      exit(1);
  }
  
@@@ -38,11 -38,11 +38,11 @@@ require_once INSTALLDIR.'/lib/form.php'
   * Form for editing a group
   *
   * @category Form
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
-  * @author   Sarven Capadisli <csarven@controlyourself.ca>
+  * @package  StatusNet
+  * @author   Evan Prodromou <evan@status.net>
+  * @author   Sarven Capadisli <csarven@status.net>
   * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
-  * @link     http://laconi.ca/
+  * @link     http://status.net/
   *
   * @see      UnsubscribeForm
   */
@@@ -150,33 -150,27 +150,33 @@@ class GroupEditForm extends For
          $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 --combined lib/logingroupnav.php
index 59f3133025a29310ac36924bc6cc18c962c42eaa,f740e329a4e0e4b787cede75577c0f0124a07079..b545fbf26914e7fbc44d6e2c83468b6fa9630f0b
@@@ -1,6 -1,6 +1,6 @@@
  <?php
  /**
-  * Laconica, the distributed open-source microblogging tool
+  * StatusNet, the distributed open-source microblogging tool
   *
   * Menu for login group of actions
   *
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   *
   * @category  Menu
-  * @package   Laconica
-  * @author    Evan Prodromou <evan@controlyourself.ca>
-  * @copyright 2008 Control Yourself, Inc.
+  * @package   StatusNet
+  * @author    Evan Prodromou <evan@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://laconi.ca/
+  * @link      http://status.net/
   */
  
- if (!defined('LACONICA')) {
+ if (!defined('STATUSNET') && !defined('LACONICA')) {
      exit(1);
  }
  
@@@ -37,10 -37,10 +37,10 @@@ require_once INSTALLDIR.'/lib/widget.ph
   * Menu for login group of actions
   *
   * @category Output
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
+  * @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://laconi.ca/
+  * @link     http://status.net/
   *
   * @see      Widget
   */
@@@ -69,25 -69,30 +69,25 @@@ class LoginGroupNav extends Widge
  
      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');
diff --combined lib/messageform.php
index 044fdc719d31c43c79608162d7ac4102ba948f04,6431bfdcc8297c880c05f23fb3cf4a38d12109db..e25ebfa08f29a2ecaf7a47ae17ff66156c0a34ca
@@@ -1,6 -1,6 +1,6 @@@
  <?php
  /**
-  * Laconica, the distributed open-source microblogging tool
+  * StatusNet, the distributed open-source microblogging tool
   *
   * Form for posting a direct message
   *
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   *
   * @category  Form
-  * @package   Laconica
-  * @author    Evan Prodromou <evan@controlyourself.ca>
-  * @author    Sarven Capadisli <csarven@controlyourself.ca>
-  * @copyright 2009 Control Yourself, Inc.
+  * @package   StatusNet
+  * @author    Evan Prodromou <evan@status.net>
+  * @author    Sarven Capadisli <csarven@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://laconi.ca/
+  * @link      http://status.net/
   */
  
- if (!defined('LACONICA')) {
+ if (!defined('STATUSNET') && !defined('LACONICA')) {
      exit(1);
  }
  
@@@ -38,11 -38,11 +38,11 @@@ require_once INSTALLDIR.'/lib/form.php'
   * Form for posting a direct message
   *
   * @category Form
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
-  * @author   Sarven Capadisli <csarven@controlyourself.ca>
+  * @package  StatusNet
+  * @author   Evan Prodromou <evan@status.net>
+  * @author   Sarven Capadisli <csarven@status.net>
   * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
-  * @link     http://laconi.ca/
+  * @link     http://status.net/
   *
   * @see      HTMLOutputter
   */
@@@ -140,19 -140,12 +140,19 @@@ class MessageForm extends For
                                                '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');
 +        }
      }
  
      /**
diff --combined lib/noticeform.php
index 35a21c6bd00518bf52ed0c6a6f98cb65c5abe80e,1e3a45142c678d7cdf5fd3a89a47b39303dcf709..cee46709e1f045d485e65ebe46124ea18d41e15b
@@@ -1,6 -1,6 +1,6 @@@
  <?php
  /**
-  * Laconica, the distributed open-source microblogging tool
+  * StatusNet, the distributed open-source microblogging tool
   *
   * Form for posting a notice
   *
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   *
   * @category  Form
-  * @package   Laconica
-  * @author    Evan Prodromou <evan@controlyourself.ca>
-  * @author    Sarven Capadisli <csarven@controlyourself.ca>
-  * @copyright 2009 Control Yourself, Inc.
+  * @package   StatusNet
+  * @author    Evan Prodromou <evan@status.net>
+  * @author    Sarven Capadisli <csarven@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://laconi.ca/
+  * @link      http://status.net/
   */
  
- if (!defined('LACONICA')) {
+ if (!defined('STATUSNET') && !defined('LACONICA')) {
      exit(1);
  }
  
@@@ -40,11 -40,11 +40,11 @@@ require_once INSTALLDIR.'/lib/form.php'
   * Frequently-used form for posting a notice
   *
   * @category Form
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
-  * @author   Sarven Capadisli <csarven@controlyourself.ca>
+  * @package  StatusNet
+  * @author   Evan Prodromou <evan@status.net>
+  * @author   Sarven Capadisli <csarven@status.net>
   * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
-  * @link     http://laconi.ca/
+  * @link     http://status.net/
   *
   * @see      HTMLOutputter
   */
@@@ -83,7 -83,7 +83,7 @@@ class NoticeForm extends For
  
          $this->action  = $action;
          $this->content = $content;
 -        
 +
          if ($user) {
              $this->user = $user;
          } else {
          return common_local_url('newnotice');
      }
  
 -
      /**
       * Legend of the Form
       *
          $this->out->element('legend', null, _('Send a notice'));
      }
  
 -
      /**
       * Data elements
       *
                                                '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',
diff --combined lib/oauthstore.php
index 87d8cf2137b197718d93b4d5803502e5c50f867f,6db07b20f781c9e2cf901a6e631760b681e80f41..e69a00f55f2ab61e588a763e882e926d2c962c06
@@@ -1,7 -1,7 +1,7 @@@
  <?php
  /*
-  * Laconica - a distributed open-source microblogging tool
-  * Copyright (C) 2008, 2009, Control Yourself, Inc.
+  * 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
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  
- if (!defined('LACONICA')) {
-     exit(1);
- }
+ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
  
 -require_once(INSTALLDIR.'/lib/omb.php');
 +require_once 'libomb/datastore.php';
  
- class LaconicaDataStore extends OMB_Datastore
+ 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);
@@@ -45,9 -44,7 +43,9 @@@
      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)) {
      {
          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());
 +        if (is_string($notice)) {
 +            throw new Exception($notice);
 +        }
 +        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));
 +        }
 +    }
  }
 +?>
diff --combined lib/omb.php
index b9d0eef64e6291e69e38d505590acd00a623f45c,0d62445991a4b5a08399217248c3556a1267c4d5..0566701ff16990fbc45b6e3003b6e4f67455c5f7
@@@ -1,7 -1,7 +1,7 @@@
  <?php
  /*
-  * Laconica - a distributed open-source microblogging tool
-  * Copyright (C) 2008, 2009, Control Yourself, Inc.
+  * 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
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  
- if (!defined('LACONICA')) {
-     exit(1);
- }
+ 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;
@@@ -41,7 -55,7 +39,7 @@@
  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());
      }
@@@ -51,8 -65,8 +49,8 @@@
  function omb_oauth_datastore()
  {
      static $store = null;
 -    if (!$store) {
 +    if (is_null($store)) {
-         $store = new LaconicaDataStore();
+         $store = new StatusNetOAuthDataStore();
      }
      return $store;
  }
  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 ' .
      $posted = array();
  
      while ($rp->fetch()) {
 -        if (!$posted[$rp->postnoticeurl]) {
 -            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 Laconica_OMB_Service_Consumer(
++        $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;
  }
  
- class Laconica_OMB_Service_Consumer extends OMB_Service_Consumer {
 -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 --combined lib/router.php
index 08bc0566dd3ed5799553799cbaaa94b696ba194b,00e728f5571078b512c01b3da684d6b043ed36d6..8f13b8852009997165c076c3ba22f4bee1a05e47
@@@ -1,6 -1,6 +1,6 @@@
  <?php
  /**
-  * Laconica, the distributed open-source microblogging tool
+  * StatusNet, the distributed open-source microblogging tool
   *
   * URL routing utilities
   *
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   *
   * @category  URL
-  * @package   Laconica
-  * @author    Evan Prodromou <evan@controlyourself.ca>
-  * @copyright 2009 Control Yourself, Inc.
+  * @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://laconi.ca/
+  * @link      http://status.net/
   */
  
- if (!defined('LACONICA')) {
+ if (!defined('STATUSNET') && !defined('LACONICA')) {
      exit(1);
  }
  
@@@ -39,10 -39,10 +39,10 @@@ require_once 'Net/URL/Mapper.php'
   * Cheap wrapper around Net_URL_Mapper
   *
   * @category URL
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
+  * @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://laconi.ca/
+  * @link     http://status.net/
   */
  
  class Router
@@@ -50,7 -50,8 +50,7 @@@
      var $m = null;
      static $inst = null;
      static $bare = array('requesttoken', 'accesstoken', 'userauthorization',
 -                         'postnotice', 'updateprofile', 'finishremotesubscribe',
 -                         'finishopenidlogin', 'finishaddopenid');
 +                         'postnotice', 'updateprofile', 'finishremotesubscribe');
  
      static function get()
      {
@@@ -75,6 -76,7 +75,6 @@@
  
          $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',
  
          // 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_-]+'));
  
  
          // 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'));
          }
                      array('action' => 'api',
                            'apiaction' => 'help'));
  
-         // laconica
+         // statusnet
  
-         $m->connect('api/laconica/:method',
+         $m->connect('api/statusnet/:method',
                      array('action' => 'api',
-                           'apiaction' => 'laconica'));
+                           'apiaction' => 'statusnet'));
+         // For older methods, we provide "laconica" base action
  
          $m->connect('api/laconica/:method',
                      array('action' => 'api',
-                           'apiaction' => 'laconica'));
+                           'apiaction' => 'statusnet'));
+         // Groups and tags are newer than 0.8.1 so no backward-compatibility
+         // necessary
  
          // Groups
          //'list' has to be handled differently, as php will not allow a method to be named 'list'
-         $m->connect('api/laconica/groups/list/:argument',
+         $m->connect('api/statusnet/groups/list/:argument',
                      array('action' => 'api',
                            'method' => 'list_groups',
                            'apiaction' => 'groups'));
          foreach (array('xml', 'json', 'rss', 'atom') as $e) {
-             $m->connect('api/laconica/groups/list.' . $e,
+             $m->connect('api/statusnet/groups/list.' . $e,
                      array('action' => 'api',
                            'method' => 'list_groups.' . $e,
                            'apiaction' => 'groups'));
          }
  
-         $m->connect('api/laconica/groups/:method',
+         $m->connect('api/statusnet/groups/:method',
                      array('action' => 'api',
                            'apiaction' => 'statuses'),
                      array('method' => '(list_all|)(\.(atom|rss|xml|json))?'));
                            'apiaction' => 'statuses'),
                      array('method' => '(|user_timeline|friends_timeline|replies|mentions|show|destroy|friends|followers)'));
  
-         $m->connect('api/laconica/groups/:method/:argument',
+         $m->connect('api/statusnet/groups/:method/:argument',
                      array('action' => 'api',
                            'apiaction' => 'groups'));
  
-         $m->connect('api/laconica/groups/:method',
+         $m->connect('api/statusnet/groups/:method',
                      array('action' => 'api',
                            'apiaction' => 'groups'));
  
          // Tags
-         $m->connect('api/laconica/tags/:method/:argument',
+         $m->connect('api/statusnet/tags/:method/:argument',
                      array('action' => 'api',
                            'apiaction' => 'tags'));
  
-         $m->connect('api/laconica/tags/:method',
+         $m->connect('api/statusnet/tags/:method',
                      array('action' => 'api',
                            'apiaction' => 'tags'));
  
          // 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),
diff --combined lib/settingsaction.php
index a923a98b2dd9862a6125973714d6bec9fb1e4862,a1f305f5b74e076cdda6256b8bb04a6e1d1cbdae..c3669868d4d3cb177401e1f1747b7f7c06cf6c0d
@@@ -1,6 -1,6 +1,6 @@@
  <?php
  /**
-  * Laconica, the distributed open-source microblogging tool
+  * StatusNet, the distributed open-source microblogging tool
   *
   * Base class for settings actions
   *
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   *
   * @category  Settings
-  * @package   Laconica
-  * @author    Evan Prodromou <evan@controlyourself.ca>
-  * @copyright 2008-2009 Control Yourself, Inc.
+  * @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://laconi.ca/
+  * @link      http://status.net/
   */
  
- if (!defined('LACONICA')) {
+ if (!defined('STATUSNET') && !defined('LACONICA')) {
      exit(1);
  }
  
   * Base class for settings group of actions
   *
   * @category Settings
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
+  * @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://laconi.ca/
+  * @link     http://status.net/
   *
   * @see      Widget
   */
@@@ -77,7 -77,9 +77,7 @@@ class SettingsAction extends CurrentUse
              // _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') {
diff --combined lib/unqueuemanager.php
index 920313c0c0c25479c766271039f8e43a3532f19b,3cdad0b54a7473a1478571f6dc8dd44ef1f4c34f..c5dc29d38640dd102602b0bd1ae56f7f3fca08ae
@@@ -1,6 -1,6 +1,6 @@@
  <?php
  /**
-  * Laconica, the distributed open-source microblogging tool
+  * StatusNet, the distributed open-source microblogging tool
   *
   * A queue manager interface for just doing things immediately
   *
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   *
   * @category  QueueManager
-  * @package   Laconica
-  * @author    Evan Prodromou <evan@controlyourself.ca>
-  * @author    Sarven Capadisli <csarven@controlyourself.ca>
-  * @copyright 2009 Control Yourself, Inc.
+  * @package   StatusNet
+  * @author    Evan Prodromou <evan@status.net>
+  * @author    Sarven Capadisli <csarven@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://laconi.ca/
+  * @link      http://status.net/
   */
  
  class UnQueueManager
@@@ -39,7 -39,7 +39,7 @@@
           case 'omb':
              if ($this->_isLocal($notice)) {
                  require_once(INSTALLDIR.'/lib/omb.php');
 -                omb_broadcast_remote_subscribers($notice);
 +                omb_broadcast_notice($notice);
              }
              break;
           case 'public':
diff --combined lib/util.php
index 3a35e15d2cf5a8b53d6dad5ba4bbb1eeea953260,4ad5c48f554de6c19a9a9229131a381202731eb0..337113c70189b6171a8cf6e8447862b181680afd
@@@ -1,7 -1,7 +1,7 @@@
  <?php
  /*
-  * Laconica - a distributed open-source microblogging tool
-  * Copyright (C) 2008, 2009, Control Yourself, Inc.
+  * 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
@@@ -54,9 -54,9 +54,9 @@@ function common_init_language(
      $language = common_language();
      // So we don't have to make people install the gettext locales
      $locale_set = common_init_locale($language);
-     bindtextdomain("laconica", common_config('site','locale_path'));
-     bind_textdomain_codeset("laconica", "UTF-8");
-     textdomain("laconica");
+     bindtextdomain("statusnet", common_config('site','locale_path'));
+     bind_textdomain_codeset("statusnet", "UTF-8");
+     textdomain("statusnet");
      setlocale(LC_CTYPE, 'C');
      if(!$locale_set) {
          common_log(LOG_INFO,'Language requested:'.$language.' - locale could not be set:',__FILE__);
@@@ -600,8 -600,7 +600,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'));
  }
  
@@@ -746,10 -745,14 +746,10 @@@ function common_relative_profile($sende
  
  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);
      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')."/" : '';
@@@ -1182,8 -1172,7 +1182,8 @@@ function common_negotiate_type($cprefs
  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)
@@@ -1330,7 -1319,7 +1330,7 @@@ function common_cache_key($extra
          $base_key = common_keyize(common_config('site', 'name'));
      }
  
-     return 'laconica:' . $base_key . ':' . $extra;
+     return 'statusnet:' . $base_key . ':' . $extra;
  }
  
  function common_keyize($str)
@@@ -1393,7 -1382,7 +1393,7 @@@ function common_shorten_url($long_url
  
      $curlh = curl_init();
      curl_setopt($curlh, CURLOPT_CONNECTTIMEOUT, 20); // # seconds to wait
-     curl_setopt($curlh, CURLOPT_USERAGENT, 'Laconica');
+     curl_setopt($curlh, CURLOPT_USERAGENT, 'StatusNet');
      curl_setopt($curlh, CURLOPT_RETURNTRANSFER, true);
  
      switch($svc) {
index 7753158d5d458dde42988307a10bd1164e9e17e3,0000000000000000000000000000000000000000..6e889205d7cc6130adc3aa072a93dcffb696a0d9
mode 100644,000000..100644
--- /dev/null
@@@ -1,185 -1,0 +1,185 @@@
-  * Laconica, the distributed open-source microblogging tool
 +<?php
 +/**
-  * @package   Laconica
-  * @author    Evan Prodromou <evan@controlyourself.ca>
-  * @copyright 2008-2009 Control Yourself, Inc.
++ * 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
-  * @link      http://laconi.ca/
++ * @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
- if (!defined('LACONICA')) {
++ * @link      http://status.net/
 + */
 +
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
++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
-  * @link     http://laconi.ca/
++ * @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);
 +        }
 +    }
 +}
index bc0d2d66c398e68f6f8949f75765653c165b8ec6,0000000000000000000000000000000000000000..50a9c15c87a8730e2b9fb7ce11f415e97cb93d32
mode 100644,000000..100644
--- /dev/null
@@@ -1,495 -1,0 +1,495 @@@
-  * Laconica - a distributed open-source microblogging tool
-  * Copyright (C) 2008, 2009, Control Yourself, Inc.
 +<?php
 +/*
- if (!defined('LACONICA')) { exit(1); }
++ * 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/>.
 + */
 +
-     # We try to use an OpenID URL as a legal Laconica user name in this order
++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);
 +    }
 +}
index 4787cd605dd65add814e770fdc6a064431819631,0000000000000000000000000000000000000000..0944117c00fc5e0b2a62401184ac7ea2ac3db822
mode 100644,000000..100644
--- /dev/null
@@@ -1,280 -1,0 +1,280 @@@
-  * Laconica - a distributed open-source microblogging tool
-  * Copyright (C) 2008, 2009, Control Yourself, Inc.
 +<?php
 +/*
- if (!defined('LACONICA')) { exit(1); }
++ * 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(); '.
 +                       '});');
 +    }
 +}
index 3d968c56e21404e622fa923fb781334f20097803,0000000000000000000000000000000000000000..07c82db3e1497f602d277160cc2e5549bcc40f78
mode 100644,000000..100644
--- /dev/null
@@@ -1,131 -1,0 +1,131 @@@
-  * Laconica - a distributed open-source microblogging tool
-  * Copyright (C) 2008, 2009, Control Yourself, Inc.
 +<?php
 +/*
- if (!defined('LACONICA')) { exit(1); }
++ * 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 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();
 +    }
 +}
index 26bf6483660823175c87367fe44ad9d08aa0f16f,0000000000000000000000000000000000000000..104ea8d889d95d5ea5d13037762a6f46f25bb6ea
mode 100644,000000..100644
--- /dev/null
@@@ -1,234 -1,0 +1,234 @@@
-  * Laconica, the distributed open-source microblogging tool
 +<?php
 +/**
-  * @package   Laconica
-  * @author    Evan Prodromou <evan@controlyourself.ca>
-  * @copyright 2008-2009 Control Yourself, Inc.
++ * 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
-  * @link      http://laconi.ca/
++ * @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
- if (!defined('LACONICA')) {
++ * @link      http://status.net/
 + */
 +
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
++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
-  * @link     http://laconi.ca/
++ * @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.');
 +    }
 +
 +    /**
 +     * 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;
 +    }
 +}
index f088c25d127b3f087b37f29beb279ae5373f0a73,0000000000000000000000000000000000000000..1b2b359caa43cf0a15d9adc689dd9ab381928bbd
mode 100644,000000..100644
--- /dev/null
@@@ -1,122 -1,0 +1,122 @@@
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
-  * @author   Robin Millette <millette@controlyourself.ca>
 +<?php
 +
 +/**
 + * Public XRDS for OpenID
 + *
 + * PHP version 5
 + *
 + * @category Action
-  * @link     http://laconi.ca/
++ * @package  StatusNet
++ * @author   Evan Prodromou <evan@status.net>
++ * @author   Robin Millette <millette@status.net>
 + * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
-  * Laconica - a distributed open-source microblogging tool
-  * Copyright (C) 2008, 2009, Control Yourself, Inc.
++ * @link     http://status.net/
 + *
- if (!defined('LACONICA')) {
++ * 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/>.
 + */
 +
-  * @package  Laconica
-  * @author   Evan Prodromou <evan@controlyourself.ca>
-  * @author   Robin Millette <millette@controlyourself.ca>
++if (!defined('STATUSNET') && !defined('LACONICA')) {
 +    exit(1);
 +}
 +
 +require_once INSTALLDIR.'/plugins/OpenID/openid.php';
 +
 +/**
 + * Public XRDS for OpenID
 + *
 + * @category Action
-  * @link     http://laconi.ca/
++ * @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 --combined scripts/maildaemon.php
index 67636d34263572099c674307ea6beb5cfdefc795,11911dcbdc6cbaba57d02438bc494a9178302715..5705cfd50e300fcf095d5cb55cc281bb957bc707
@@@ -1,8 -1,8 +1,8 @@@
  #!/usr/bin/env php
  <?php
  /*
-  * Laconica - a distributed open-source microblogging tool
-  * Copyright (C) 2008, 2009, Control Yourself, Inc.
+  * 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
@@@ -66,10 -66,9 +66,10 @@@ class MailerDaemo
          }
          $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){
@@@ -79,9 -78,9 +79,9 @@@
                  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.'));
              }
              $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
          $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));
          }
      }
      }
  
      function maybeAddRedir($file_id, $url)
 -    {   
 +    {
          $file_redir = File_redirection::staticGet('url', $url);
  
          if (empty($file_redir)) {
      }
  
      function attachFile($notice, $filerec)
 -    {   
 +    {
          File_to_post::processNew($filerec->id, $notice->id);
  
          $this->maybeAddRedir($filerec->id,
index cc5263ae23e8aeb20ea763e65b5fb33d33925e9f,8e685f1c8ec33bbfb72495aa7924720117cc26a0..be33b9821801f63b2e2a12b2da9fdcc5a2b9fe1f
@@@ -1,8 -1,8 +1,8 @@@
  #!/usr/bin/env php
  <?php
  /*
-  * Laconica - a distributed open-source microblogging tool
-  * Copyright (C) 2008, 2009, Control Yourself, Inc.
+  * 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
@@@ -57,7 -57,7 +57,7 @@@ class OmbQueueHandler extends QueueHand
              $this->log(LOG_DEBUG, 'Ignoring remote notice ' . $notice->id);
              return true;
          } else {
 -            return omb_broadcast_remote_subscribers($notice);
 +            return omb_broadcast_notice($notice);
          }
      }
  
diff --combined scripts/xmppdaemon.php
index f033025f8376f6a6086e3840555feb4fcfce6f4e,9e621e725a3fae3697c22cce4e8e8373607e65df..1b1aec3e6eded1030b368ddff41a6c5bddf3bb95
@@@ -1,8 -1,8 +1,8 @@@
  #!/usr/bin/env php
  <?php
  /*
-  * Laconica - a distributed open-source microblogging tool
-  * Copyright (C) 2008, 2009, Control Yourself, Inc.
+  * 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
@@@ -316,11 -316,9 +316,11 @@@ class XMPPDaemon extends Daemo
      {
          $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');