]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/OStatus/lib/salmonaction.php
saving notices in salmon actions
[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('STATUSNET')) {
26     exit(1);
27 }
28
29 class SalmonAction extends Action
30 {
31     var $xml      = null;
32     var $activity = null;
33
34     function prepare($args)
35     {
36         StatusNet::setApi(true); // Send smaller error pages
37
38         parent::prepare($args);
39
40         if ($_SERVER['REQUEST_METHOD'] != 'POST') {
41             $this->clientError(_('This method requires a POST.'));
42         }
43
44         if ($_SERVER['CONTENT_TYPE'] != 'application/atom+xml') {
45             $this->clientError(_('Salmon requires application/atom+xml'));
46         }
47
48         $xml = file_get_contents('php://input');
49
50         $dom = DOMDocument::loadXML($xml);
51
52         if ($dom->documentElement->namespaceURI != Activity::ATOM ||
53             $dom->documentElement->localName != 'entry') {
54             $this->clientError(_m('Salmon post must be an Atom entry.'));
55         }
56         // XXX: check the signature
57
58         $this->act = new Activity($dom->documentElement);
59         return true;
60     }
61
62     /**
63      * Check the posted activity type and break out to appropriate processing.
64      */
65
66     function handle($args)
67     {
68         StatusNet::setApi(true); // Send smaller error pages
69
70         // TODO : Insert new $xml -> notice code
71
72         if (Event::handle('StartHandleSalmon', array($this->activity))) {
73             switch ($this->act->verb)
74             {
75             case ActivityVerb::POST:
76                 $this->handlePost();
77                 break;
78             case ActivityVerb::SHARE:
79                 $this->handleShare();
80                 break;
81             case ActivityVerb::FAVORITE:
82                 $this->handleFavorite();
83                 break;
84             case ActivityVerb::UNFAVORITE:
85                 $this->handleUnfavorite();
86                 break;
87             case ActivityVerb::FOLLOW:
88             case ActivityVerb::FRIEND:
89                 $this->handleFollow();
90                 break;
91             case ActivityVerb::UNFOLLOW:
92                 $this->handleUnfollow();
93                 break;
94             case ActivityVerb::JOIN:
95                 $this->handleJoin();
96                 break;
97             default:
98                 throw new ClientException(_("Unimplemented."));
99             }
100             Event::handle('EndHandleSalmon', array($this->activity));
101         }
102     }
103
104     function handlePost()
105     {
106         throw new ClientException(_("Unimplemented!"));
107     }
108
109     function handleFollow()
110     {
111         throw new ClientException(_("Unimplemented!"));
112     }
113
114     function handleUnfollow()
115     {
116         throw new ClientException(_("Unimplemented!"));
117     }
118
119     function handleFavorite()
120     {
121         throw new ClientException(_("Unimplemented!"));
122     }
123
124     /**
125      * Remote user doesn't like one of our posts after all!
126      * Confirm the post is ours, and delete a local favorite event.
127      */
128
129     function handleUnfavorite()
130     {
131         throw new ClientException(_("Unimplemented!"));
132     }
133
134     /**
135      * Hmmmm
136      */
137     function handleShare()
138     {
139         throw new ClientException(_("Unimplemented!"));
140     }
141
142     /**
143      * Hmmmm
144      */
145     function handleJoin()
146     {
147         throw new ClientException(_("Unimplemented!"));
148     }
149
150     /**
151      * @return Ostatus_profile
152      */
153     function ensureProfile()
154     {
155         $actor = $this->act->actor;
156         if (empty($actor->id)) {
157             common_log(LOG_ERR, "broken actor: " . var_export($actor, true));
158             throw new Exception("Received a salmon slap from unidentified actor.");
159         }
160
161         return Ostatus_profile::ensureActorProfile($this->act);
162     }
163
164     /**
165      * @fixme merge into Ostatus_profile::ensureActorProfile and friends
166      */
167     function createProfile()
168     {
169         $actor = $this->act->actor;
170
171         $profile = new Profile();
172
173         $profile->nickname = $this->nicknameFromURI($actor->id);
174
175         if (empty($profile->nickname)) {
176             $profile->nickname = common_nicknamize($actor->title);
177         }
178
179         $profile->fullname   = $actor->title;
180         $profile->bio        = $actor->summary; // XXX: is that right?
181         $profile->profileurl = $actor->link; // XXX: is that right?
182         $profile->created    = common_sql_now();
183
184         $id = $profile->insert();
185
186         if (empty($id)) {
187             common_log_db_error($profile, 'INSERT', __FILE__);
188             throw new Exception("Couldn't save new profile for $actor->id\n");
189         }
190
191         // XXX: add avatars
192
193         $op = new Ostatus_profile();
194
195         $op->profile_id = $id;
196         $op->homeuri    = $actor->id;
197         $op->created    = $profile->created;
198
199         // XXX: determine feed URI from source or Webfinger or whatever
200
201         $id = $op->insert();
202
203         if (empty($id)) {
204             common_log_db_error($op, 'INSERT', __FILE__);
205             throw new Exception("Couldn't save new ostatus profile for $actor->id\n");
206         }
207
208         return $profile;
209     }
210
211     /**
212      * @fixme should be merged into Ostatus_profile
213      */
214     function nicknameFromURI($uri)
215     {
216         preg_match('/(\w+):/', $uri, $matches);
217
218         $protocol = $matches[1];
219
220         switch ($protocol) {
221         case 'acct':
222         case 'mailto':
223             if (preg_match("/^$protocol:(.*)?@.*\$/", $uri, $matches)) {
224                 return common_canonical_nickname($matches[1]);
225             }
226             return null;
227         case 'http':
228             return common_url_to_nickname($uri);
229             break;
230         default:
231             return null;
232         }
233     }
234
235     function saveNotice()
236     {
237         $oprofile = $this->ensureProfile();
238
239         // Get (safe!) HTML and text versions of the content
240
241         require_once(INSTALLDIR.'/extlib/HTMLPurifier/HTMLPurifier.auto.php');
242
243         $html = $this->act->object->content;
244
245         $rendered = HTMLPurifier::purify($html);
246         $content = html_entity_decode(strip_tags($rendered));
247
248         $options = array('is_local' => Notice::REMOTE_OMB,
249                          'uri' => $this->act->object->id,
250                          'url' => $this->act->object->link,
251                          'rendered' => $rendered);
252
253         if (!empty($this->act->context->location)) {
254             $options['lat'] = $location->lat;
255             $options['lon'] = $location->lon;
256             if ($location->location_id) {
257                 $options['location_ns'] = $location->location_ns;
258                 $options['location_id'] = $location->location_id;
259             }
260         }
261
262         if (!empty($this->act->context->replyToID)) {
263             $orig = Notice::staticGet('uri',
264                                       $this->act->context->replyToID);
265             if (!empty($orig)) {
266                 $options['reply_to'] = $orig->id;
267             }
268         }
269
270         if (!empty($this->act->time)) {
271             $options['created'] = common_sql_time($this->act->time);
272         }
273
274         return Notice::saveNew($oprofile->profile_id,
275                                $content,
276                                'ostatus+salmon',
277                                $options);
278     }
279 }