]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/OStatus/lib/salmonaction.php
Improve debugging for Salmon slaps
[quix0rs-gnu-social.git] / plugins / OStatus / lib / salmonaction.php
1 <?php
2 /*
3  * StatusNet - the distributed open-source microblogging tool
4  * Copyright (C) 2010, StatusNet, Inc.
5  *
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.
10  *
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.
15  *
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/>.
18  */
19
20 /**
21  * @package OStatusPlugin
22  * @author James Walker <james@status.net>
23  */
24
25 if (!defined('GNUSOCIAL')) { exit(1); }
26
27 class SalmonAction extends Action
28 {
29     protected $needPost = true;
30
31     var $xml      = null;
32     var $activity = null;
33     var $target   = null;
34
35     protected function prepare(array $args=array())
36     {
37         StatusNet::setApi(true); // Send smaller error pages
38
39         parent::prepare($args);
40
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".'));
44         }
45
46         try {
47             $envxml = file_get_contents('php://input');
48             $magic_env = new MagicEnvelope($envxml);   // parse incoming XML as a MagicEnvelope
49
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());
56         }
57
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.'));
63         }
64
65         return true;
66     }
67
68     /**
69      * Check the posted activity type and break out to appropriate processing.
70      */
71
72     protected function handle()
73     {
74         parent::handle();
75
76         common_log(LOG_DEBUG, "Got a " . $this->activity->verb);
77         try {
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:
82                     $this->handlePost();
83                     break;
84                 case ActivityVerb::SHARE:
85                     $this->handleShare();
86                     break;
87                 case ActivityVerb::FAVORITE:
88                     $this->handleFavorite();
89                     break;
90                 case ActivityVerb::UNFAVORITE:
91                     $this->handleUnfavorite();
92                     break;
93                 case ActivityVerb::FOLLOW:
94                 case ActivityVerb::FRIEND:
95                     $this->handleFollow();
96                     break;
97                 case ActivityVerb::UNFOLLOW:
98                     $this->handleUnfollow();
99                     break;
100                 case ActivityVerb::JOIN:
101                     $this->handleJoin();
102                     break;
103                 case ActivityVerb::LEAVE:
104                     $this->handleLeave();
105                     break;
106                 case ActivityVerb::TAG:
107                     $this->handleTag();
108                     break;
109                 case ActivityVerb::UNTAG:
110                     $this->handleUntag();
111                     break;
112                 case ActivityVerb::UPDATE_PROFILE:
113                     $this->handleUpdateProfile();
114                     break;
115                 default:
116                     // TRANS: Client exception.
117                     throw new ClientException(_m('Unrecognized activity type.'));
118                 }
119                 Event::handle('EndHandleSalmon', array($this->activity));
120                 Event::handle('EndHandleSalmonTarget', array($this->activity, $this->target));
121             }
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.');
127         }
128     }
129
130     function handlePost()
131     {
132         // TRANS: Client exception.
133         throw new ClientException(_m('This target does not understand posts.'));
134     }
135
136     function handleFollow()
137     {
138         // TRANS: Client exception.
139         throw new ClientException(_m('This target does not understand follows.'));
140     }
141
142     function handleUnfollow()
143     {
144         // TRANS: Client exception.
145         throw new ClientException(_m('This target does not understand unfollows.'));
146     }
147
148     function handleFavorite()
149     {
150         // TRANS: Client exception.
151         throw new ClientException(_m('This target does not understand favorites.'));
152     }
153
154     function handleUnfavorite()
155     {
156         // TRANS: Client exception.
157         throw new ClientException(_m('This target does not understand unfavorites.'));
158     }
159
160     function handleShare()
161     {
162         // TRANS: Client exception.
163         throw new ClientException(_m('This target does not understand share events.'));
164     }
165
166     function handleJoin()
167     {
168         // TRANS: Client exception.
169         throw new ClientException(_m('This target does not understand joins.'));
170     }
171
172     function handleLeave()
173     {
174         // TRANS: Client exception.
175         throw new ClientException(_m('This target does not understand leave events.'));
176     }
177
178     function handleTag()
179     {
180         // TRANS: Client exception.
181         throw new ClientException(_m('This target does not understand list events.'));
182     }
183
184     function handleUntag()
185     {
186         // TRANS: Client exception.
187         throw new ClientException(_m('This target does not understand unlist events.'));
188     }
189
190     /**
191      * Remote user sent us an update to their profile.
192      * If we already know them, accept the updates.
193      */
194     function handleUpdateProfile()
195     {
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);
200         } else {
201             common_log(LOG_INFO, "Ignoring profile-update ping from unknown " . $this->activity->actor->id);
202         }
203     }
204
205     /**
206      * @return Ostatus_profile
207      */
208     function ensureProfile()
209     {
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));
214             // TRANS: Exception.
215             throw new Exception(_m('Received a salmon slap from unidentified actor.'));
216         }
217
218         return Ostatus_profile::ensureActivityObjectProfile($actor);
219     }
220
221     function saveNotice()
222     {
223         $oprofile = $this->ensureProfile();
224         return $oprofile->processPost($this->activity, 'salmon');
225     }
226 }