]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/Favorite/actions/atompubfavoritefeed.php
XSS vulnerability when remote-subscribing
[quix0rs-gnu-social.git] / plugins / Favorite / actions / atompubfavoritefeed.php
1 <?php
2 /**
3  * StatusNet - the distributed open-source microblogging tool
4  * Copyright (C) 2010, StatusNet, Inc.
5  *
6  * Feed of ActivityStreams 'favorite' actions
7  *
8  * PHP version 5
9  *
10  * This program is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU Affero General Public License as published by
12  * the Free Software Foundation, either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Affero General Public License for more details.
19  *
20  * You should have received a copy of the GNU Affero General Public License
21  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
22  *
23  * @category  AtomPub
24  * @package   StatusNet
25  * @author    Evan Prodromou <evan@status.net>
26  * @copyright 2010 StatusNet, Inc.
27  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
28  * @link      http://status.net/
29  */
30
31 if (!defined('GNUSOCIAL') && !defined('STATUSNET')) { exit(1); }
32
33 /**
34  * Feed of ActivityStreams 'favorite' actions
35  *
36  * @category  AtomPub
37  * @package   StatusNet
38  * @author    Evan Prodromou <evan@status.net>
39  * @copyright 2010 StatusNet, Inc.
40  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
41  * @link      http://status.net/
42  */
43 class AtompubfavoritefeedAction extends ApiAuthAction
44 {
45     private $_profile = null;
46     private $_faves   = null;
47
48     protected function prepare(array $args=array())
49     {
50         parent::prepare($args);
51
52         $this->_profile = Profile::getKV('id', $this->trimmed('profile'));
53
54         if (!$this->_profile instanceof Profile) {
55             // TRANS: Client exception thrown when requesting a favorite feed for a non-existing profile.
56             throw new ClientException(_('No such profile.'), 404);
57         }
58
59         $offset = ($this->page-1) * $this->count;
60         $limit  = $this->count + 1;
61
62         $this->_faves = Fave::byProfile($this->_profile->id,
63                                         $offset,
64                                         $limit);
65
66         return true;
67     }
68
69     protected function handle()
70     {
71         parent::handle();
72
73         switch ($_SERVER['REQUEST_METHOD']) {
74         case 'HEAD':
75         case 'GET':
76             $this->showFeed();
77             break;
78         case 'POST':
79             $this->addFavorite();
80             break;
81         default:
82             // TRANS: Client exception thrown when using an unsupported HTTP method.
83             throw new ClientException(_('HTTP method not supported.'), 405);
84         }
85
86         return true;
87     }
88
89     /**
90      * Show a feed of favorite activity streams objects
91      *
92      * @return void
93      */
94     function showFeed()
95     {
96         header('Content-Type: application/atom+xml; charset=utf-8');
97
98         $url = common_local_url('AtomPubFavoriteFeed',
99                                 array('profile' => $this->_profile->id));
100
101         $feed = new Atom10Feed(true);
102
103         $feed->addNamespace('activity',
104                             'http://activitystrea.ms/spec/1.0/');
105
106         $feed->addNamespace('poco',
107                             'http://portablecontacts.net/spec/1.0');
108
109         $feed->addNamespace('media',
110                             'http://purl.org/syndication/atommedia');
111
112         $feed->id = $url;
113
114         $feed->setUpdated('now');
115
116         $feed->addAuthor($this->_profile->getBestName(),
117                          $this->_profile->getUri());
118
119         // TRANS: Title for Atom favorites feed.
120         // TRANS: %s is a user nickname.
121         $feed->setTitle(sprintf(_("%s favorites"),
122                                 $this->_profile->getBestName()));
123
124         // TRANS: Subtitle for Atom favorites feed.
125         // TRANS: %1$s is a user nickname, %2$s is the StatusNet sitename.
126         $feed->setSubtitle(sprintf(_('Notices %1$s has favorited on %2$s'),
127                                    $this->_profile->getBestName(),
128                                    common_config('site', 'name')));
129
130         $feed->addLink(common_local_url('showfavorites',
131                                         array('nickname' =>
132                                               $this->_profile->nickname)));
133
134         $feed->addLink($url,
135                        array('rel' => 'self',
136                              'type' => 'application/atom+xml'));
137
138         // If there's more...
139
140         if ($this->page > 1) {
141             $feed->addLink($url,
142                            array('rel' => 'first',
143                                  'type' => 'application/atom+xml'));
144
145             $feed->addLink(common_local_url('AtomPubFavoriteFeed',
146                                             array('profile' =>
147                                                   $this->_profile->id),
148                                             array('page' =>
149                                                   $this->page - 1)),
150                            array('rel' => 'prev',
151                                  'type' => 'application/atom+xml'));
152         }
153
154         if ($this->_faves->N > $this->count) {
155
156             $feed->addLink(common_local_url('AtomPubFavoriteFeed',
157                                             array('profile' =>
158                                                   $this->_profile->id),
159                                             array('page' =>
160                                                   $this->page + 1)),
161                            array('rel' => 'next',
162                                  'type' => 'application/atom+xml'));
163         }
164
165         $i = 0;
166
167         while ($this->_faves->fetch()) {
168
169             // We get one more than needed; skip that one
170
171             $i++;
172
173             if ($i > $this->count) {
174                 break;
175             }
176
177             $act = $this->_faves->asActivity();
178             $feed->addEntryRaw($act->asString(false, false, false));
179         }
180
181         $this->raw($feed->getString());
182     }
183
184     /**
185      * add a new favorite
186      *
187      * @return void
188      */
189     function addFavorite()
190     {
191         // XXX: Refactor this; all the same for atompub
192
193         if (!$this->scoped instanceof Profile ||
194             $this->scoped->id != $this->_profile->id) {
195             // TRANS: Client exception thrown when trying to set a favorite for another user.
196             throw new ClientException(_("Cannot add someone else's subscription."), 403);
197         }
198
199         $xml = file_get_contents('php://input');
200
201         $dom = DOMDocument::loadXML($xml);
202
203         if ($dom->documentElement->namespaceURI != Activity::ATOM ||
204             $dom->documentElement->localName != 'entry') {
205             // TRANS: Client error displayed when not using an Atom entry.
206             throw new ClientException(_('Atom post must be an Atom entry.'));
207         }
208
209         $activity = new Activity($dom->documentElement);
210         $notice = null;
211
212         // Favorite plugin handles these as ActivityHandlerPlugin through Notice->saveActivity
213         // which in turn uses "StoreActivityObject" event.
214         Event::handle('StartAtomPubNewActivity', array(&$activity, $this->scoped, &$notice));
215         assert($notice instanceof Notice);
216
217         $act = $notice->asActivity();
218
219         header('Content-Type: application/atom+xml; charset=utf-8');
220         header('Content-Location: ' . $act->selfLink);
221
222         $this->startXML();
223         $this->raw($act->asString(true, true, true));
224         $this->endXML();
225     }
226
227     /**
228      * Return true if read only.
229      *
230      * MAY override
231      *
232      * @param array $args other arguments
233      *
234      * @return boolean is read only action?
235      */
236     function isReadOnly($args)
237     {
238         if ($_SERVER['REQUEST_METHOD'] == 'GET' ||
239             $_SERVER['REQUEST_METHOD'] == 'HEAD') {
240             return true;
241         } else {
242             return false;
243         }
244     }
245
246     /**
247      * Return last modified, if applicable.
248      *
249      * MAY override
250      *
251      * @return string last modified http header
252      */
253     function lastModified()
254     {
255         // For comparison with If-Last-Modified
256         // If not applicable, return null
257         return null;
258     }
259
260     /**
261      * Return etag, if applicable.
262      *
263      * MAY override
264      *
265      * @return string etag http header
266      */
267     function etag()
268     {
269         return null;
270     }
271
272     /**
273      * Does this require authentication?
274      *
275      * @return boolean true if delete, else false
276      */
277     function requiresAuth()
278     {
279         if ($_SERVER['REQUEST_METHOD'] == 'GET' ||
280             $_SERVER['REQUEST_METHOD'] == 'HEAD') {
281             return false;
282         } else {
283             return true;
284         }
285     }
286 }