diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/accountsettingsaction.php | 64 | ||||
-rw-r--r-- | lib/action.php | 19 | ||||
-rw-r--r-- | lib/command.php | 16 | ||||
-rw-r--r-- | lib/common.php | 36 | ||||
-rw-r--r-- | lib/facebookaction.php | 11 | ||||
-rw-r--r-- | lib/groupeditform.php | 24 | ||||
-rw-r--r-- | lib/logingroupnav.php | 38 | ||||
-rw-r--r-- | lib/messageform.php | 17 | ||||
-rw-r--r-- | lib/noticeform.php | 23 | ||||
-rw-r--r-- | lib/oauthstore.php | 357 | ||||
-rw-r--r-- | lib/omb.php | 335 | ||||
-rw-r--r-- | lib/settingsaction.php | 10 | ||||
-rw-r--r-- | lib/unqueuemanager.php | 2 | ||||
-rw-r--r-- | lib/util.php | 26 |
14 files changed, 648 insertions, 330 deletions
diff --git a/lib/accountsettingsaction.php b/lib/accountsettingsaction.php index 1a21d871e..9814c48ab 100644 --- a/lib/accountsettingsaction.php +++ b/lib/accountsettingsaction.php @@ -98,42 +98,42 @@ class AccountSettingsNav extends Widget function show() { - # action => array('prompt', 'title') - $menu = - array('profilesettings' => - array(_('Profile'), - _('Change your profile settings')), - 'avatarsettings' => - array(_('Avatar'), - _('Upload an avatar')), - 'passwordsettings' => - array(_('Password'), - _('Change your password')), - 'emailsettings' => - array(_('Email'), - _('Change email handling')), - 'openidsettings' => - array(_('OpenID'), - _('Add or remove OpenIDs')), - 'userdesignsettings' => - array(_('Design'), - _('Design your profile')), - 'othersettings' => - array(_('Other'), - _('Other options'))); - $action_name = $this->action->trimmed('action'); $this->action->elementStart('ul', array('class' => 'nav')); - foreach ($menu as $menuaction => $menudesc) { - if ($menuaction == 'openidsettings' && - !common_config('openid', 'enabled')) { - continue; + if (Event::handle('StartAccountSettingsNav', array(&$this->action))) { + + $menu = + array('profilesettings' => + array(_('Profile'), + _('Change your profile settings')), + 'avatarsettings' => + array(_('Avatar'), + _('Upload an avatar')), + 'passwordsettings' => + array(_('Password'), + _('Change your password')), + 'emailsettings' => + array(_('Email'), + _('Change email handling')), + 'openidsettings' => + array(_('OpenID'), + _('Add or remove OpenIDs')), + '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 --git a/lib/action.php b/lib/action.php index 1bdc4daea..4d724fba5 100644 --- a/lib/action.php +++ b/lib/action.php @@ -120,14 +120,17 @@ class Action extends HTMLOutputter // lawsuit { // 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'); } diff --git a/lib/command.php b/lib/command.php index 4e2280bc8..371386dc5 100644 --- a/lib/command.php +++ b/lib/command.php @@ -211,16 +211,20 @@ class MessageCommand extends Command function execute($channel) { $other = User::staticGet('nickname', common_canonical_nickname($this->other)); + $len = mb_strlen($this->text); + if ($len == 0) { $channel->error($this->user, _('No content!')); return; - } else if ($len > 140) { - $content = common_shorten_links($content); - if (mb_strlen($content) > 140) { - $channel->error($this->user, sprintf(_('Message too long - maximum is 140 characters, you sent %d'), $len)); - return; - } + } + + $this->text = common_shorten_links($this->text); + + if (Message::contentTooLong($this->text)) { + $channel->error($this->user, sprintf(_('Message too long - maximum is %d characters, you sent %d'), + Message::maxContent(), mb_strlen($this->text))); + return; } if (!$other) { diff --git a/lib/common.php b/lib/common.php index 6c4b856e0..84ee5be15 100644 --- a/lib/common.php +++ b/lib/common.php @@ -19,7 +19,7 @@ if (!defined('LACONICA')) { exit(1); } -define('LACONICA_VERSION', '0.8.1dev'); +define('LACONICA_VERSION', '0.9.0dev'); define('AVATAR_PROFILE_SIZE', 96); define('AVATAR_STREAM_SIZE', 48); @@ -114,7 +114,9 @@ $config = '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 'priority' => 'debug', # XXX: currently ignored @@ -138,7 +140,8 @@ $config = array('blacklist' => array(), 'featured' => array()), 'profile' => - array('banned' => array()), + array('banned' => array(), + 'biolimit' => null), 'avatar' => array('server' => null, 'dir' => INSTALLDIR . '/avatar/', @@ -258,7 +261,8 @@ $config = 'filecommand' => '/usr/bin/file', ), 'group' => - array('maxaliases' => 3), + array('maxaliases' => 3, + 'desclimit' => null), 'oohembed' => array('endpoint' => 'http://oohembed.com/oohembed/'), 'search' => array('type' => 'fulltext'), @@ -273,6 +277,10 @@ $config = 'linkcolor' => null, 'backgroundimage' => null, 'disposition' => null), + 'notice' => + array('contentlimit' => null), + 'message' => + array('contentlimit' => null), ); $config['db'] = &PEAR::getStaticProperty('DB_DataObject','options'); @@ -399,17 +407,19 @@ require_once INSTALLDIR.'/lib/serverexception.php'; define('NICKNAME_FMT', VALIDATE_NUM.VALIDATE_ALPHA_LOWER); -function __autoload($class) +function __autoload($cls) { - if ($class == 'OAuthRequest') { + 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 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'); + } else { + Event::handle('Autoload', array(&$cls)); } } diff --git a/lib/facebookaction.php b/lib/facebookaction.php index 289e702c6..4edd3a077 100644 --- a/lib/facebookaction.php +++ b/lib/facebookaction.php @@ -35,7 +35,6 @@ if (!defined('LACONICA')) require_once INSTALLDIR.'/lib/facebookutil.php'; require_once INSTALLDIR.'/lib/noticeform.php'; - class FacebookAction extends Action { @@ -180,7 +179,6 @@ class FacebookAction extends Action } - // Make this into a widget later function showLocalNav() { @@ -240,7 +238,6 @@ class FacebookAction extends Action $this->endHTML(); } - function showInstructions() { @@ -271,7 +268,6 @@ class FacebookAction extends Action $this->elementEnd('div'); } - function showLoginForm($msg = null) { @@ -316,7 +312,6 @@ class FacebookAction extends Action } - function updateProfileBox($notice) { @@ -398,7 +393,6 @@ class FacebookAction extends Action $this->xw->openURI('php://output'); } - /** * Generate pagination links * @@ -457,8 +451,9 @@ class FacebookAction extends Action } else { $content_shortened = common_shorten_links($content); - if (mb_strlen($content_shortened) > 140) { - $this->showPage(_('That\'s too long. Max notice size is 140 chars.')); + if (Notice::contentTooLong($content_shortened)) { + $this->showPage(sprintf(_('That\'s too long. Max notice size is %d chars.'), + Notice::maxContent())); return; } } diff --git a/lib/groupeditform.php b/lib/groupeditform.php index fbb39129b..47e62d469 100644 --- a/lib/groupeditform.php +++ b/lib/groupeditform.php @@ -150,27 +150,33 @@ class GroupEditForm extends Form $this->out->elementStart('li'); $this->out->hidden('groupid', $id); $this->out->input('nickname', _('Nickname'), - ($this->out->arg('nickname')) ? $this->out->arg('nickname') : $nickname, - _('1-64 lowercase letters or numbers, no punctuation or spaces')); + ($this->out->arg('nickname')) ? $this->out->arg('nickname') : $nickname, + _('1-64 lowercase letters or numbers, no punctuation or spaces')); $this->out->elementEnd('li'); $this->out->elementStart('li'); $this->out->input('fullname', _('Full name'), - ($this->out->arg('fullname')) ? $this->out->arg('fullname') : $fullname); + ($this->out->arg('fullname')) ? $this->out->arg('fullname') : $fullname); $this->out->elementEnd('li'); $this->out->elementStart('li'); $this->out->input('homepage', _('Homepage'), - ($this->out->arg('homepage')) ? $this->out->arg('homepage') : $homepage, - _('URL of the homepage or blog of the group or topic')); + ($this->out->arg('homepage')) ? $this->out->arg('homepage') : $homepage, + _('URL of the homepage or blog of the group or topic')); $this->out->elementEnd('li'); $this->out->elementStart('li'); + $desclimit = User_group::maxDescription(); + if ($desclimit == 0) { + $descinstr = _('Describe the group or topic'); + } else { + $descinstr = sprintf(_('Describe the group or topic in %d characters'), $desclimit); + } $this->out->textarea('description', _('Description'), - ($this->out->arg('description')) ? $this->out->arg('description') : $description, - _('Describe the group or topic in 140 chars')); + ($this->out->arg('description')) ? $this->out->arg('description') : $description, + $descinstr); $this->out->elementEnd('li'); $this->out->elementStart('li'); $this->out->input('location', _('Location'), - ($this->out->arg('location')) ? $this->out->arg('location') : $location, - _('Location for the group, if any, like "City, State (or Region), Country"')); + ($this->out->arg('location')) ? $this->out->arg('location') : $location, + _('Location for the group, if any, like "City, State (or Region), Country"')); $this->out->elementEnd('li'); if (common_config('group', 'maxaliases') > 0) { $aliases = (empty($this->group)) ? array() : $this->group->getAliases(); diff --git a/lib/logingroupnav.php b/lib/logingroupnav.php index 2fb1828d6..bae45b077 100644 --- a/lib/logingroupnav.php +++ b/lib/logingroupnav.php @@ -69,30 +69,30 @@ class LoginGroupNav extends Widget function show() { - // action => array('prompt', 'title') - $menu = array(); + $action_name = $this->action->trimmed('action'); + + $this->action->elementStart('ul', array('class' => 'nav')); + + if (Event::handle('StartLoginGroupNav', array(&$this->action))) { + + $this->action->menuItem(common_local_url('login'), + _('Login'), + _('Login with a username and password'), + $action_name === 'login'); - if (!common_config('site','openidonly')) { - $menu['login'] = array(_('Login'), - _('Login with a username and password')); if (!(common_config('site','closed') || common_config('site','inviteonly'))) { - $menu['register'] = array(_('Register'), - _('Sign up for a new account')); + $this->action->menuItem(common_local_url('register'), + _('Register'), + _('Sign up for a new account'), + $action_name === 'register'); } - } - if (common_config('openid', 'enabled')) { - $menu['openidlogin'] = array(_('OpenID'), - _('Login or register with OpenID')); - } - $action_name = $this->action->trimmed('action'); - $this->action->elementStart('ul', array('class' => 'nav')); + $this->action->menuItem(common_local_url('openidlogin'), + _('OpenID'), + _('Login or register with OpenID'), + $action_name === 'openidlogin'); - 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 --git a/lib/messageform.php b/lib/messageform.php index 8ea2b36c2..044fdc719 100644 --- a/lib/messageform.php +++ b/lib/messageform.php @@ -140,12 +140,19 @@ class MessageForm extends Form 'rows' => 4, 'name' => 'content'), ($this->content) ? $this->content : ''); - $this->out->elementStart('dl', 'form_note'); - $this->out->element('dt', null, _('Available characters')); - $this->out->element('dd', array('id' => 'notice_text-count'), - '140'); - $this->out->elementEnd('dl'); + $contentLimit = Message::maxContent(); + + $this->out->element('script', array('type' => 'text/javascript'), + 'maxLength = ' . $contentLimit . ';'); + + if ($contentLimit > 0) { + $this->out->elementStart('dl', 'form_note'); + $this->out->element('dt', null, _('Available characters')); + $this->out->element('dd', array('id' => 'notice_text-count'), + $contentLimit); + $this->out->elementEnd('dl'); + } } /** diff --git a/lib/noticeform.php b/lib/noticeform.php index 4e2a2edd6..35a21c6bd 100644 --- a/lib/noticeform.php +++ b/lib/noticeform.php @@ -83,7 +83,7 @@ class NoticeForm extends Form $this->action = $action; $this->content = $content; - + if ($user) { $this->user = $user; } else { @@ -117,7 +117,6 @@ class NoticeForm extends Form return common_local_url('newnotice'); } - /** * Legend of the Form * @@ -128,7 +127,6 @@ class NoticeForm extends Form $this->out->element('legend', null, _('Send a notice')); } - /** * Data elements * @@ -145,11 +143,20 @@ class NoticeForm extends Form 'rows' => 4, 'name' => 'status_textarea'), ($this->content) ? $this->content : ''); - $this->out->elementStart('dl', 'form_note'); - $this->out->element('dt', null, _('Available characters')); - $this->out->element('dd', array('id' => 'notice_text-count'), - '140'); - $this->out->elementEnd('dl'); + + $contentLimit = Notice::maxContent(); + + $this->out->element('script', array('type' => 'text/javascript'), + 'maxLength = ' . $contentLimit . ';'); + + if ($contentLimit > 0) { + $this->out->elementStart('dl', 'form_note'); + $this->out->element('dt', null, _('Available characters')); + $this->out->element('dd', array('id' => 'notice_text-count'), + $contentLimit); + $this->out->elementEnd('dl'); + } + if (common_config('attachments', 'uploads')) { $this->out->element('label', array('for' => 'notice_data-attach'),_('Attach')); $this->out->element('input', array('id' => 'notice_data-attach', diff --git a/lib/oauthstore.php b/lib/oauthstore.php index f224c6c22..87d8cf213 100644 --- a/lib/oauthstore.php +++ b/lib/oauthstore.php @@ -17,15 +17,16 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -if (!defined('LACONICA')) { exit(1); } +if (!defined('LACONICA')) { + exit(1); +} -require_once(INSTALLDIR.'/lib/omb.php'); +require_once 'libomb/datastore.php'; -class LaconicaOAuthDataStore extends OAuthDataStore +class LaconicaDataStore extends OMB_Datastore { // We keep a record of who's contacted us - function lookup_consumer($consumer_key) { $con = Consumer::staticGet('consumer_key', $consumer_key); @@ -44,7 +45,9 @@ class LaconicaOAuthDataStore extends OAuthDataStore function lookup_token($consumer, $token_type, $token_key) { $t = new Token(); - $t->consumer_key = $consumer->key; + if (!is_null($consumer)) { + $t->consumer_key = $consumer->key; + } $t->tok = $token_key; $t->type = ($token_type == 'access') ? 1 : 0; if ($t->find(true)) { @@ -154,4 +157,348 @@ class LaconicaOAuthDataStore extends OAuthDataStore { return $this->new_access_token($consumer); } + + + /** + * Revoke specified OAuth token + * + * Revokes the authorization token specified by $token_key. + * Throws exceptions in case of error. + * + * @param string $token_key The token to be revoked + * + * @access public + **/ + public function revoke_token($token_key) { + $rt = new Token(); + $rt->tok = $token_key; + $rt->type = 0; + $rt->state = 0; + if (!$rt->find(true)) { + throw new Exception('Tried to revoke unknown token'); + } + if (!$rt->delete()) { + throw new Exception('Failed to delete revoked token'); + } + } + + /** + * Authorize specified OAuth token + * + * Authorizes the authorization token specified by $token_key. + * Throws exceptions in case of error. + * + * @param string $token_key The token to be authorized + * + * @access public + **/ + public function authorize_token($token_key) { + $rt = new Token(); + $rt->tok = $token_key; + $rt->type = 0; + $rt->state = 0; + if (!$rt->find(true)) { + throw new Exception('Tried to authorize unknown token'); + } + $orig_rt = clone($rt); + $rt->state = 1; # Authorized but not used + if (!$rt->update($orig_rt)) { + throw new Exception('Failed to authorize token'); + } + } + + /** + * Get profile by identifying URI + * + * Returns an OMB_Profile object representing the OMB profile identified by + * $identifier_uri. + * Returns null if there is no such OMB profile. + * Throws exceptions in case of other error. + * + * @param string $identifier_uri The OMB identifier URI specifying the + * requested profile + * + * @access public + * + * @return OMB_Profile The corresponding profile + **/ + public function getProfile($identifier_uri) { + /* getProfile is only used for remote profiles by libomb. + TODO: Make it work with local ones anyway. */ + $remote = Remote_profile::staticGet('uri', $identifier_uri); + if (!$remote) throw new Exception('No such remote profile'); + $profile = Profile::staticGet('id', $remote->id); + if (!$profile) throw new Exception('No profile for remote user'); + + require_once INSTALLDIR.'/lib/omb.php'; + return profile_to_omb_profile($identifier_uri, $profile); + } + + /** + * Save passed profile + * + * Stores the OMB profile $profile. Overwrites an existing entry. + * Throws exceptions in case of error. + * + * @param OMB_Profile $profile The OMB profile which should be saved + * + * @access public + **/ + public function saveProfile($omb_profile) { + if (common_profile_url($omb_profile->getNickname()) == + $omb_profile->getProfileURL()) { + throw new Exception('Not implemented'); + } else { + $remote = Remote_profile::staticGet('uri', $omb_profile->getIdentifierURI()); + + if ($remote) { + $exists = true; + $profile = Profile::staticGet($remote->id); + $orig_remote = clone($remote); + $orig_profile = clone($profile); + # XXX: compare current postNotice and updateProfile URLs to the ones + # stored in the DB to avoid (possibly...) above attack + } else { + $exists = false; + $remote = new Remote_profile(); + $remote->uri = $omb_profile->getIdentifierURI(); + $profile = new Profile(); + } + + $profile->nickname = $omb_profile->getNickname(); + $profile->profileurl = $omb_profile->getProfileURL(); + + $fullname = $omb_profile->getFullname(); + $profile->fullname = is_null($fullname) ? '' : $fullname; + $homepage = $omb_profile->getHomepage(); + $profile->homepage = is_null($homepage) ? '' : $homepage; + $bio = $omb_profile->getBio(); + $profile->bio = is_null($bio) ? '' : $bio; + $location = $omb_profile->getLocation(); + $profile->location = is_null($location) ? '' : $location; + + if ($exists) { + $profile->update($orig_profile); + } else { + $profile->created = DB_DataObject_Cast::dateTime(); # current time + $id = $profile->insert(); + if (!$id) { + throw new Exception(_('Error inserting new profile')); + } + $remote->id = $id; + } + + $avatar_url = $omb_profile->getAvatarURL(); + if ($avatar_url) { + if (!$this->add_avatar($profile, $avatar_url)) { + throw new Exception(_('Error inserting avatar')); + } + } else { + $avatar = $profile->getOriginalAvatar(); + if($avatar) $avatar->delete(); + $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); + if($avatar) $avatar->delete(); + $avatar = $profile->getAvatar(AVATAR_STREAM_SIZE); + if($avatar) $avatar->delete(); + $avatar = $profile->getAvatar(AVATAR_MINI_SIZE); + if($avatar) $avatar->delete(); + } + + if ($exists) { + if (!$remote->update($orig_remote)) { + throw new Exception(_('Error updating remote profile')); + } + } else { + $remote->created = DB_DataObject_Cast::dateTime(); # current time + if (!$remote->insert()) { + throw new Exception(_('Error inserting remote profile')); + } + } + } + } + + function add_avatar($profile, $url) + { + $temp_filename = tempnam(sys_get_temp_dir(), 'listener_avatar'); + copy($url, $temp_filename); + $imagefile = new ImageFile($profile->id, $temp_filename); + $filename = Avatar::filename($profile->id, + image_type_to_extension($imagefile->type), + null, + common_timestamp()); + rename($temp_filename, Avatar::path($filename)); + return $profile->setOriginal($filename); + } + + /** + * Save passed notice + * + * Stores the OMB notice $notice. The datastore may change the passed notice. + * This might by neccessary for URIs depending on a database key. Note that + * it is the user’s duty to present a mechanism for his OMB_Datastore to + * appropriately change his OMB_Notice. + * Throws exceptions in case of error. + * + * @param OMB_Notice $notice The OMB notice which should be saved + * + * @access public + **/ + public function saveNotice(&$omb_notice) { + if (Notice::staticGet('uri', $omb_notice->getIdentifierURI())) { + throw new Exception(_('Duplicate notice')); + } + $author_uri = $omb_notice->getAuthor()->getIdentifierURI(); + common_log(LOG_DEBUG, $author_uri, __FILE__); + $author = Remote_profile::staticGet('uri', $author_uri); + if (!$author) { + $author = User::staticGet('uri', $author_uri); + } + if (!$author) { + throw new Exception('No such user'); + } + + common_log(LOG_DEBUG, print_r($author, true), __FILE__); + + $notice = Notice::saveNew($author->id, + $omb_notice->getContent(), + 'omb', + false, + null, + $omb_notice->getIdentifierURI()); + 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 --git a/lib/omb.php b/lib/omb.php index 4f6a96095..b9d0eef64 100644 --- a/lib/omb.php +++ b/lib/omb.php @@ -17,36 +17,22 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -if (!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'); +if (!defined('LACONICA')) { + exit(1); +} -define('OMB_ENDPOINT_UPDATEPROFILE', OMB_NAMESPACE.'/updateProfile'); -define('OMB_ENDPOINT_POSTNOTICE', OMB_NAMESPACE.'/postNotice'); -define('OAUTH_ENDPOINT_REQUEST', OAUTH_NAMESPACE.'endpoint/request'); -define('OAUTH_ENDPOINT_AUTHORIZE', OAUTH_NAMESPACE.'endpoint/authorize'); -define('OAUTH_ENDPOINT_ACCESS', OAUTH_NAMESPACE.'endpoint/access'); -define('OAUTH_ENDPOINT_RESOURCE', OAUTH_NAMESPACE.'endpoint/resource'); -define('OAUTH_AUTH_HEADER', OAUTH_NAMESPACE.'parameters/auth-header'); -define('OAUTH_POST_BODY', OAUTH_NAMESPACE.'parameters/post-body'); -define('OAUTH_HMAC_SHA1', OAUTH_NAMESPACE.'signature/HMAC-SHA1'); +require_once INSTALLDIR.'/lib/oauthstore.php'; +require_once 'OAuth.php'; +require_once 'libomb/constants.php'; +require_once 'libomb/service_consumer.php'; +require_once 'libomb/notice.php'; +require_once 'libomb/profile.php'; +require_once 'Auth/Yadis/Yadis.php'; function omb_oauth_consumer() { static $con = null; - if (!$con) { + if (is_null($con)) { $con = new OAuthConsumer(common_root_url(), ''); } return $con; @@ -55,7 +41,7 @@ function omb_oauth_consumer() function omb_oauth_server() { static $server = null; - if (!$server) { + if (is_null($server)) { $server = new OAuthServer(omb_oauth_datastore()); $server->add_signature_method(omb_hmac_sha1()); } @@ -65,8 +51,8 @@ function omb_oauth_server() function omb_oauth_datastore() { static $store = null; - if (!$store) { - $store = new LaconicaOAuthDataStore(); + if (is_null($store)) { + $store = new LaconicaDataStore(); } return $store; } @@ -74,57 +60,18 @@ function omb_oauth_datastore() function omb_hmac_sha1() { static $hmac_method = null; - if (!$hmac_method) { + if (is_null($hmac_method)) { $hmac_method = new OAuthSignatureMethod_HMAC_SHA1(); } return $hmac_method; } -function omb_get_services($xrd, $type) +function omb_broadcast_notice($notice) { - return $xrd->services(array(omb_service_filter($type))); -} -function omb_service_filter($type) -{ - return create_function('$s', - 'return omb_match_service($s, \''.$type.'\');'); -} + $omb_notice = notice_to_omb_notice($notice); -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) -{ - - # First, get remote users subscribed to this profile + /* Get remote users subscribed to this profile. */ $rp = new Remote_profile(); $rp->query('SELECT postnoticeurl, token, secret ' . @@ -135,170 +82,148 @@ function omb_broadcast_remote_subscribers($notice) $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(); - - $token = new OAuthToken($tk, $secret); - - $url = $postnoticeurl; - $parsed = parse_url($url); - $params = array(); - parse_str($parsed['query'], $params); + $profile = $user->getProfile(); - $req = OAuthRequest::from_consumer_and_token($con, $token, - 'POST', $url, $params); + $omb_profile = profile_to_omb_profile($user->uri, $profile, true); - $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')); - - $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( + 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: Laconica/' . LACONICA_VERSION)); - - if ($result->status == 403) { # not authorized, don't send again - common_debug('403 result, deleting subscription', __FILE__); - # FIXME: figure out how to delete this - # $subscription->delete(); - return false; - } else if ($result->status != 200) { - common_debug('Error status '.$result->status, __FILE__); - return false; - } else { # success! - parse_str($result->body, $return); - if ($return['omb_version'] == OMB_VERSION_01) { - return true; - } else { - return false; - } - } + return; } -function omb_broadcast_profile($profile) -{ - # First, get remote users subscribed to this profile - # XXX: use a join here rather than looping through results - $sub = new Subscription(); - $sub->subscribed = $profile->id; - if ($sub->find()) { - $updated = array(); - while ($sub->fetch()) { - $rp = Remote_profile::staticGet('id', $sub->subscriber); - if ($rp) { - if (!array_key_exists($rp->updateprofileurl, $updated)) { - if (omb_update_profile($profile, $rp, $sub)) { - $updated[$rp->updateprofileurl] = true; - } - } - } - } +class Laconica_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: Laconica/' . LACONICA_VERSION)); + $omb_notice = new OMB_Notice(profile_to_omb_profile($user->uri, $profile), + $notice->uri, + $notice->content); + $omb_notice->setURL(common_local_url('shownotice', array('notice' => + $notice->id))); + $omb_notice->setLicenseURL(common_config('license', 'url')); - if (empty($result) || !$result) { - common_debug("Unable to contact " . $req->get_normalized_http_url()); - } else if ($result->status == 403) { # not authorized, don't send again - common_debug('403 result, deleting subscription', __FILE__); - $subscription->delete(); - return false; - } else if ($result->status != 200) { - common_debug('Error status '.$result->status, __FILE__); - return false; - } else { # success! - parse_str($result->body, $return); - if (isset($return['omb_version']) && $return['omb_version'] === OMB_VERSION_01) { - return true; - } else { - return false; - } - } + return $omb_notice; } +?> diff --git a/lib/settingsaction.php b/lib/settingsaction.php index 17d3a2f64..4cf9b80c4 100644 --- a/lib/settingsaction.php +++ b/lib/settingsaction.php @@ -77,10 +77,12 @@ class SettingsAction extends CurrentUserDesignAction // _all_ our settings are important common_set_returnto($this->selfUrl()); $user = common_current_user(); - if ($user->hasOpenID()) { - common_redirect(common_local_url('openidlogin'), 303); - } else { - common_redirect(common_local_url('login'), 303); + if (Event::handle('RedirectToLogin', array($this, $user))) { + if ($user->hasOpenID()) { + common_redirect(common_local_url('openidlogin'), 303); + } else { + common_redirect(common_local_url('login'), 303); + } } } else if ($_SERVER['REQUEST_METHOD'] == 'POST') { $this->handlePost(); diff --git a/lib/unqueuemanager.php b/lib/unqueuemanager.php index a10ca339a..920313c0c 100644 --- a/lib/unqueuemanager.php +++ b/lib/unqueuemanager.php @@ -39,7 +39,7 @@ class UnQueueManager case 'omb': if ($this->_isLocal($notice)) { require_once(INSTALLDIR.'/lib/omb.php'); - omb_broadcast_remote_subscribers($notice); + omb_broadcast_notice($notice); } break; case 'public': diff --git a/lib/util.php b/lib/util.php index 748c8332f..3fa90d31a 100644 --- a/lib/util.php +++ b/lib/util.php @@ -540,7 +540,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')); } @@ -685,14 +686,10 @@ function common_relative_profile($sender, $nickname, $dt=null) function common_local_url($action, $args=null, $params=null, $fragment=null) { - static $sensitive = array('login', 'register', 'passwordsettings', - 'twittersettings', 'finishopenidlogin', - 'finishaddopenid', 'api'); - $r = Router::get(); $path = $r->build($action, $args, $params, $fragment); - $ssl = in_array($action, $sensitive); + $ssl = common_is_sensitive($action); if (common_config('site','fancy')) { $url = common_path(mb_substr($path, 1), $ssl); @@ -706,6 +703,20 @@ function common_local_url($action, $args=null, $params=null, $fragment=null) return $url; } +function common_is_sensitive($action) +{ + static $sensitive = array('login', 'register', 'passwordsettings', + 'twittersettings', 'finishopenidlogin', + 'finishaddopenid', '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')."/" : ''; @@ -1112,7 +1123,8 @@ function common_negotiate_type($cprefs, $sprefs) function common_config($main, $sub) { global $config; - return isset($config[$main][$sub]) ? $config[$main][$sub] : false; + return (array_key_exists($main, $config) && + array_key_exists($sub, $config[$main])) ? $config[$main][$sub] : false; } function common_copy_args($from) |