3 * StatusNet - the distributed open-source microblogging tool
4 * Copyright (C) 2010, StatusNet, Inc.
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
16 * You should have received a copy of the GNU Affero General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 * @package OStatusPlugin
22 * @author James Walker <james@status.net>
25 if (!defined('GNUSOCIAL')) { exit(1); }
27 class SalmonAction extends Action
29 protected $needPost = true;
35 protected function prepare(array $args=array())
37 StatusNet::setApi(true); // Send smaller error pages
39 parent::prepare($args);
41 if (!isset($_SERVER['CONTENT_TYPE']) || $_SERVER['CONTENT_TYPE'] != 'application/magic-envelope+xml') {
42 // TRANS: Client error. Do not translate "application/magic-envelope+xml".
43 $this->clientError(_m('Salmon requires "application/magic-envelope+xml".'));
47 $envxml = file_get_contents('php://input');
48 $magic_env = new MagicEnvelope($envxml); // parse incoming XML as a MagicEnvelope
50 $entry = $magic_env->getPayload(); // Not cryptographically verified yet!
51 $this->activity = new Activity($entry->documentElement);
52 $oprofile = $this->ensureProfile();
53 } catch (Exception $e) {
54 common_debug('Salmon envelope parsing failed with: '.$e->getMessage());
55 $this->clientError($e->getMessage());
58 // Cryptographic verification test
59 if (!$magic_env->verify($oprofile->localProfile())) {
60 common_log(LOG_DEBUG, "Salmon signature verification failed.");
61 // TRANS: Client error.
62 $this->clientError(_m('Salmon signature verification failed.'));
69 * Check the posted activity type and break out to appropriate processing.
72 protected function handle()
76 common_log(LOG_DEBUG, "Got a " . $this->activity->verb);
78 if (Event::handle('StartHandleSalmonTarget', array($this->activity, $this->target)) &&
79 Event::handle('StartHandleSalmon', array($this->activity))) {
80 switch ($this->activity->verb) {
81 case ActivityVerb::POST:
84 case ActivityVerb::SHARE:
87 case ActivityVerb::FAVORITE:
88 $this->handleFavorite();
90 case ActivityVerb::UNFAVORITE:
91 $this->handleUnfavorite();
93 case ActivityVerb::FOLLOW:
94 case ActivityVerb::FRIEND:
95 $this->handleFollow();
97 case ActivityVerb::UNFOLLOW:
98 $this->handleUnfollow();
100 case ActivityVerb::JOIN:
103 case ActivityVerb::LEAVE:
104 $this->handleLeave();
106 case ActivityVerb::TAG:
109 case ActivityVerb::UNTAG:
110 $this->handleUntag();
112 case ActivityVerb::UPDATE_PROFILE:
113 $this->handleUpdateProfile();
116 // TRANS: Client exception.
117 throw new ClientException(_m('Unrecognized activity type.'));
119 Event::handle('EndHandleSalmon', array($this->activity));
120 Event::handle('EndHandleSalmonTarget', array($this->activity, $this->target));
122 } catch (AlreadyFulfilledException $e) {
123 // The action's results are already fulfilled. Maybe it was a
124 // duplicate? Maybe someone's database is out of sync?
125 // Let's just accept it and move on.
126 common_log(LOG_INFO, 'Salmon slap carried an event which had already been fulfilled.');
130 function handlePost()
132 // TRANS: Client exception.
133 throw new ClientException(_m('This target does not understand posts.'));
136 function handleFollow()
138 // TRANS: Client exception.
139 throw new ClientException(_m('This target does not understand follows.'));
142 function handleUnfollow()
144 // TRANS: Client exception.
145 throw new ClientException(_m('This target does not understand unfollows.'));
148 function handleFavorite()
150 // TRANS: Client exception.
151 throw new ClientException(_m('This target does not understand favorites.'));
154 function handleUnfavorite()
156 // TRANS: Client exception.
157 throw new ClientException(_m('This target does not understand unfavorites.'));
160 function handleShare()
162 // TRANS: Client exception.
163 throw new ClientException(_m('This target does not understand share events.'));
166 function handleJoin()
168 // TRANS: Client exception.
169 throw new ClientException(_m('This target does not understand joins.'));
172 function handleLeave()
174 // TRANS: Client exception.
175 throw new ClientException(_m('This target does not understand leave events.'));
180 // TRANS: Client exception.
181 throw new ClientException(_m('This target does not understand list events.'));
184 function handleUntag()
186 // TRANS: Client exception.
187 throw new ClientException(_m('This target does not understand unlist events.'));
191 * Remote user sent us an update to their profile.
192 * If we already know them, accept the updates.
194 function handleUpdateProfile()
196 $oprofile = Ostatus_profile::getActorProfile($this->activity);
197 if ($oprofile instanceof Ostatus_profile) {
198 common_log(LOG_INFO, "Got a profile-update ping from $oprofile->uri");
199 $oprofile->updateFromActivityObject($this->activity->actor);
201 common_log(LOG_INFO, "Ignoring profile-update ping from unknown " . $this->activity->actor->id);
206 * @return Ostatus_profile
208 function ensureProfile()
210 $actor = $this->activity->actor;
211 if (empty($actor->id)) {
212 common_log(LOG_ERR, "broken actor: " . var_export($actor, true));
213 common_log(LOG_ERR, "activity with no actor: " . var_export($this->activity, true));
215 throw new Exception(_m('Received a salmon slap from unidentified actor.'));
218 return Ostatus_profile::ensureActivityObjectProfile($actor);
221 function saveNotice()
223 $oprofile = $this->ensureProfile();
224 return $oprofile->processPost($this->activity, 'salmon');