]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - lib/router.php
move xrd and hostmeta out of the OStatus plugin and into core
[quix0rs-gnu-social.git] / lib / router.php
1 <?php
2 /**
3  * StatusNet, the distributed open-source microblogging tool
4  *
5  * URL routing utilities
6  *
7  * PHP version 5
8  *
9  * LICENCE: This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU Affero General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU Affero General Public License for more details.
18  *
19  * You should have received a copy of the GNU Affero General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  *
22  * @category  URL
23  * @package   StatusNet
24  * @author    Evan Prodromou <evan@status.net>
25  * @copyright 2009 StatusNet, Inc.
26  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
27  * @link      http://status.net/
28  */
29
30 if (!defined('STATUSNET') && !defined('LACONICA')) {
31     exit(1);
32 }
33
34 require_once 'Net/URL/Mapper.php';
35
36 class StatusNet_URL_Mapper extends Net_URL_Mapper {
37     private static $_singleton = null;
38
39     private function __construct()
40     {
41     }
42
43     public static function getInstance($id = '__default__')
44     {
45         if (empty(self::$_singleton)) {
46             self::$_singleton = new StatusNet_URL_Mapper();
47         }
48         return self::$_singleton;
49     }
50
51     public function connect($path, $defaults = array(), $rules = array())
52     {
53         $result = null;
54         if (Event::handle('StartConnectPath', array(&$path, &$defaults, &$rules, &$result))) {
55             $result = parent::connect($path, $defaults, $rules);
56             Event::handle('EndConnectPath', array($path, $defaults, $rules, $result));
57         }
58         return $result;
59     }
60 }
61
62 /**
63  * URL Router
64  *
65  * Cheap wrapper around Net_URL_Mapper
66  *
67  * @category URL
68  * @package  StatusNet
69  * @author   Evan Prodromou <evan@status.net>
70  * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
71  * @link     http://status.net/
72  */
73 class Router
74 {
75     var $m = null;
76     static $inst = null;
77     static $bare = array('requesttoken', 'accesstoken', 'userauthorization',
78                          'postnotice', 'updateprofile', 'finishremotesubscribe');
79
80     static function get()
81     {
82         if (!Router::$inst) {
83             Router::$inst = new Router();
84         }
85         return Router::$inst;
86     }
87
88     function __construct()
89     {
90         if (!$this->m) {
91             $this->m = $this->initialize();
92         }
93     }
94
95     function initialize()
96     {
97         $m = StatusNet_URL_Mapper::getInstance();
98
99         if (Event::handle('StartInitializeRouter', array(&$m))) {
100
101             $m->connect('robots.txt', array('action' => 'robotstxt'));
102
103             $m->connect('opensearch/people', array('action' => 'opensearch',
104                                                    'type' => 'people'));
105             $m->connect('opensearch/notice', array('action' => 'opensearch',
106                                                    'type' => 'notice'));
107
108             // docs
109
110             $m->connect('doc/:title', array('action' => 'doc'));
111
112             $m->connect('main/otp/:user_id/:token',
113                         array('action' => 'otp'),
114                         array('user_id' => '[0-9]+',
115                               'token' => '.+'));
116
117             // main stuff is repetitive
118
119             $main = array('login', 'logout', 'register', 'subscribe',
120                           'unsubscribe', 'confirmaddress', 'recoverpassword',
121                           'invite', 'favor', 'disfavor', 'sup',
122                           'block', 'unblock', 'subedit',
123                           'groupblock', 'groupunblock',
124                           'sandbox', 'unsandbox',
125                           'silence', 'unsilence',
126                           'grantrole', 'revokerole',
127                           'repeat',
128                           'deleteuser',
129                           'geocode',
130                           'version',
131                           );
132
133             foreach ($main as $a) {
134                 $m->connect('main/'.$a, array('action' => $a));
135             }
136
137             // Also need a block variant accepting ID on URL for mail links
138             $m->connect('main/block/:profileid',
139                         array('action' => 'block'),
140                         array('profileid' => '[0-9]+'));
141
142             $m->connect('main/sup/:seconds', array('action' => 'sup'),
143                         array('seconds' => '[0-9]+'));
144
145             $m->connect('main/tagother/:id', array('action' => 'tagother'));
146
147             $m->connect('main/oembed',
148                         array('action' => 'oembed'));
149
150             $m->connect('main/xrds',
151                         array('action' => 'publicxrds'));
152             $m->connect('.well-known/host-meta',
153                         array('action' => 'hostmeta'));
154
155             // these take a code
156
157             foreach (array('register', 'confirmaddress', 'recoverpassword') as $c) {
158                 $m->connect('main/'.$c.'/:code', array('action' => $c));
159             }
160
161             // exceptional
162
163             $m->connect('main/remote', array('action' => 'remotesubscribe'));
164             $m->connect('main/remote?nickname=:nickname', array('action' => 'remotesubscribe'), array('nickname' => '[A-Za-z0-9_-]+'));
165
166             foreach (Router::$bare as $action) {
167                 $m->connect('index.php?action=' . $action, array('action' => $action));
168             }
169
170             // settings
171
172             foreach (array('profile', 'avatar', 'password', 'im', 'oauthconnections',
173                            'oauthapps', 'email', 'sms', 'userdesign', 'other') as $s) {
174                 $m->connect('settings/'.$s, array('action' => $s.'settings'));
175             }
176
177             $m->connect('settings/oauthapps/show/:id',
178                 array('action' => 'showapplication'),
179                 array('id' => '[0-9]+')
180             );
181             $m->connect('settings/oauthapps/new',
182                 array('action' => 'newapplication')
183             );
184             $m->connect('settings/oauthapps/edit/:id',
185                 array('action' => 'editapplication'),
186                 array('id' => '[0-9]+')
187             );
188             $m->connect('settings/oauthapps/delete/:id',
189                 array('action' => 'deleteapplication'),
190                 array('id' => '[0-9]+')
191             );
192
193             // search
194
195             foreach (array('group', 'people', 'notice') as $s) {
196                 $m->connect('search/'.$s, array('action' => $s.'search'));
197                 $m->connect('search/'.$s.'?q=:q',
198                             array('action' => $s.'search'),
199                             array('q' => '.+'));
200             }
201
202             // The second of these is needed to make the link work correctly
203             // when inserted into the page. The first is needed to match the
204             // route on the way in. Seems to be another Net_URL_Mapper bug to me.
205             $m->connect('search/notice/rss', array('action' => 'noticesearchrss'));
206             $m->connect('search/notice/rss?q=:q', array('action' => 'noticesearchrss'),
207                         array('q' => '.+'));
208
209             $m->connect('attachment/:attachment',
210                         array('action' => 'attachment'),
211                         array('attachment' => '[0-9]+'));
212
213             $m->connect('attachment/:attachment/ajax',
214                         array('action' => 'attachment_ajax'),
215                         array('attachment' => '[0-9]+'));
216
217             $m->connect('attachment/:attachment/thumbnail',
218                         array('action' => 'attachment_thumbnail'),
219                         array('attachment' => '[0-9]+'));
220
221             $m->connect('notice/new', array('action' => 'newnotice'));
222             $m->connect('notice/new?replyto=:replyto',
223                         array('action' => 'newnotice'),
224                         array('replyto' => '[A-Za-z0-9_-]+'));
225             $m->connect('notice/new?replyto=:replyto&inreplyto=:inreplyto',
226                         array('action' => 'newnotice'),
227                         array('replyto' => '[A-Za-z0-9_-]+'),
228                         array('inreplyto' => '[0-9]+'));
229
230             $m->connect('notice/:notice/file',
231                         array('action' => 'file'),
232                         array('notice' => '[0-9]+'));
233
234             $m->connect('notice/:notice',
235                         array('action' => 'shownotice'),
236                         array('notice' => '[0-9]+'));
237             $m->connect('notice/delete', array('action' => 'deletenotice'));
238             $m->connect('notice/delete/:notice',
239                         array('action' => 'deletenotice'),
240                         array('notice' => '[0-9]+'));
241
242             $m->connect('bookmarklet/new', array('action' => 'bookmarklet'));
243
244             // conversation
245
246             $m->connect('conversation/:id',
247                         array('action' => 'conversation'),
248                         array('id' => '[0-9]+'));
249
250             $m->connect('message/new', array('action' => 'newmessage'));
251             $m->connect('message/new?to=:to', array('action' => 'newmessage'), array('to' => '[A-Za-z0-9_-]+'));
252             $m->connect('message/:message',
253                         array('action' => 'showmessage'),
254                         array('message' => '[0-9]+'));
255
256             $m->connect('user/:id',
257                         array('action' => 'userbyid'),
258                         array('id' => '[0-9]+'));
259
260             $m->connect('tags/', array('action' => 'publictagcloud'));
261             $m->connect('tag/', array('action' => 'publictagcloud'));
262             $m->connect('tags', array('action' => 'publictagcloud'));
263             $m->connect('tag', array('action' => 'publictagcloud'));
264             $m->connect('tag/:tag/rss',
265                         array('action' => 'tagrss'),
266                         array('tag' => '[\pL\pN_\-\.]{1,64}'));
267             $m->connect('tag/:tag',
268                         array('action' => 'tag'),
269                         array('tag' => '[\pL\pN_\-\.]{1,64}'));
270
271             $m->connect('peopletag/:tag',
272                         array('action' => 'peopletag'),
273                         array('tag' => '[a-zA-Z0-9]+'));
274
275             // groups
276
277             $m->connect('group/new', array('action' => 'newgroup'));
278
279             foreach (array('edit', 'join', 'leave', 'delete') as $v) {
280                 $m->connect('group/:nickname/'.$v,
281                             array('action' => $v.'group'),
282                             array('nickname' => '[a-zA-Z0-9]+'));
283                 $m->connect('group/:id/id/'.$v,
284                             array('action' => $v.'group'),
285                             array('id' => '[0-9]+'));
286             }
287
288             foreach (array('members', 'logo', 'rss', 'designsettings') as $n) {
289                 $m->connect('group/:nickname/'.$n,
290                             array('action' => 'group'.$n),
291                             array('nickname' => '[a-zA-Z0-9]+'));
292             }
293
294             $m->connect('group/:nickname/foaf',
295                         array('action' => 'foafgroup'),
296                         array('nickname' => '[a-zA-Z0-9]+'));
297
298             $m->connect('group/:nickname/blocked',
299                         array('action' => 'blockedfromgroup'),
300                         array('nickname' => '[a-zA-Z0-9]+'));
301
302             $m->connect('group/:nickname/makeadmin',
303                         array('action' => 'makeadmin'),
304                         array('nickname' => '[a-zA-Z0-9]+'));
305
306             $m->connect('group/:id/id',
307                         array('action' => 'groupbyid'),
308                         array('id' => '[0-9]+'));
309
310             $m->connect('group/:nickname',
311                         array('action' => 'showgroup'),
312                         array('nickname' => '[a-zA-Z0-9]+'));
313
314             $m->connect('group/', array('action' => 'groups'));
315             $m->connect('group', array('action' => 'groups'));
316             $m->connect('groups/', array('action' => 'groups'));
317             $m->connect('groups', array('action' => 'groups'));
318
319             // Twitter-compatible API
320
321             // statuses API
322
323             $m->connect('api/statuses/public_timeline.:format',
324                         array('action' => 'ApiTimelinePublic',
325                               'format' => '(xml|json|rss|atom)'));
326
327             $m->connect('api/statuses/friends_timeline.:format',
328                         array('action' => 'ApiTimelineFriends',
329                               'format' => '(xml|json|rss|atom)'));
330
331             $m->connect('api/statuses/friends_timeline/:id.:format',
332                         array('action' => 'ApiTimelineFriends',
333                               'id' => '[a-zA-Z0-9]+',
334                               'format' => '(xml|json|rss|atom)'));
335
336             $m->connect('api/statuses/home_timeline.:format',
337                         array('action' => 'ApiTimelineHome',
338                               'format' => '(xml|json|rss|atom)'));
339
340             $m->connect('api/statuses/home_timeline/:id.:format',
341                         array('action' => 'ApiTimelineHome',
342                               'id' => '[a-zA-Z0-9]+',
343                               'format' => '(xml|json|rss|atom)'));
344
345             $m->connect('api/statuses/user_timeline.:format',
346                         array('action' => 'ApiTimelineUser',
347                               'format' => '(xml|json|rss|atom)'));
348
349             $m->connect('api/statuses/user_timeline/:id.:format',
350                         array('action' => 'ApiTimelineUser',
351                               'id' => '[a-zA-Z0-9]+',
352                               'format' => '(xml|json|rss|atom)'));
353
354             $m->connect('api/statuses/mentions.:format',
355                         array('action' => 'ApiTimelineMentions',
356                               'format' => '(xml|json|rss|atom)'));
357
358             $m->connect('api/statuses/mentions/:id.:format',
359                         array('action' => 'ApiTimelineMentions',
360                               'id' => '[a-zA-Z0-9]+',
361                               'format' => '(xml|json|rss|atom)'));
362
363             $m->connect('api/statuses/replies.:format',
364                         array('action' => 'ApiTimelineMentions',
365                               'format' => '(xml|json|rss|atom)'));
366
367             $m->connect('api/statuses/replies/:id.:format',
368                         array('action' => 'ApiTimelineMentions',
369                               'id' => '[a-zA-Z0-9]+',
370                               'format' => '(xml|json|rss|atom)'));
371
372             $m->connect('api/statuses/retweeted_by_me.:format',
373                         array('action' => 'ApiTimelineRetweetedByMe',
374                               'format' => '(xml|json|atom)'));
375
376             $m->connect('api/statuses/retweeted_to_me.:format',
377                         array('action' => 'ApiTimelineRetweetedToMe',
378                               'format' => '(xml|json|atom)'));
379
380             $m->connect('api/statuses/retweets_of_me.:format',
381                         array('action' => 'ApiTimelineRetweetsOfMe',
382                               'format' => '(xml|json|atom)'));
383
384             $m->connect('api/statuses/friends.:format',
385                         array('action' => 'ApiUserFriends',
386                               'format' => '(xml|json)'));
387
388             $m->connect('api/statuses/friends/:id.:format',
389                         array('action' => 'ApiUserFriends',
390                               'id' => '[a-zA-Z0-9]+',
391                               'format' => '(xml|json)'));
392
393             $m->connect('api/statuses/followers.:format',
394                         array('action' => 'ApiUserFollowers',
395                               'format' => '(xml|json)'));
396
397             $m->connect('api/statuses/followers/:id.:format',
398                         array('action' => 'ApiUserFollowers',
399                               'id' => '[a-zA-Z0-9]+',
400                               'format' => '(xml|json)'));
401
402             $m->connect('api/statuses/show.:format',
403                         array('action' => 'ApiStatusesShow',
404                               'format' => '(xml|json|atom)'));
405
406             $m->connect('api/statuses/show/:id.:format',
407                         array('action' => 'ApiStatusesShow',
408                               'id' => '[0-9]+',
409                               'format' => '(xml|json|atom)'));
410
411             $m->connect('api/statuses/update.:format',
412                         array('action' => 'ApiStatusesUpdate',
413                               'format' => '(xml|json)'));
414
415             $m->connect('api/statuses/destroy.:format',
416                         array('action' => 'ApiStatusesDestroy',
417                               'format' => '(xml|json)'));
418
419             $m->connect('api/statuses/destroy/:id.:format',
420                         array('action' => 'ApiStatusesDestroy',
421                               'id' => '[0-9]+',
422                               'format' => '(xml|json)'));
423
424             $m->connect('api/statuses/retweet/:id.:format',
425                         array('action' => 'ApiStatusesRetweet',
426                               'id' => '[0-9]+',
427                               'format' => '(xml|json)'));
428
429             $m->connect('api/statuses/retweets/:id.:format',
430                         array('action' => 'ApiStatusesRetweets',
431                               'id' => '[0-9]+',
432                               'format' => '(xml|json)'));
433
434             // users
435
436             $m->connect('api/users/show.:format',
437                         array('action' => 'ApiUserShow',
438                               'format' => '(xml|json)'));
439
440             $m->connect('api/users/show/:id.:format',
441                         array('action' => 'ApiUserShow',
442                               'id' => '[a-zA-Z0-9]+',
443                               'format' => '(xml|json)'));
444
445             // direct messages
446
447             $m->connect('api/direct_messages.:format',
448                         array('action' => 'ApiDirectMessage',
449                               'format' => '(xml|json|rss|atom)'));
450
451             $m->connect('api/direct_messages/sent.:format',
452                         array('action' => 'ApiDirectMessage',
453                               'format' => '(xml|json|rss|atom)',
454                               'sent' => true));
455
456             $m->connect('api/direct_messages/new.:format',
457                         array('action' => 'ApiDirectMessageNew',
458                               'format' => '(xml|json)'));
459
460             // friendships
461
462             $m->connect('api/friendships/show.:format',
463                         array('action' => 'ApiFriendshipsShow',
464                               'format' => '(xml|json)'));
465
466             $m->connect('api/friendships/exists.:format',
467                         array('action' => 'ApiFriendshipsExists',
468                               'format' => '(xml|json)'));
469
470             $m->connect('api/friendships/create.:format',
471                         array('action' => 'ApiFriendshipsCreate',
472                               'format' => '(xml|json)'));
473
474             $m->connect('api/friendships/destroy.:format',
475                         array('action' => 'ApiFriendshipsDestroy',
476                               'format' => '(xml|json)'));
477
478             $m->connect('api/friendships/create/:id.:format',
479                         array('action' => 'ApiFriendshipsCreate',
480                               'id' => '[a-zA-Z0-9]+',
481                               'format' => '(xml|json)'));
482
483             $m->connect('api/friendships/destroy/:id.:format',
484                         array('action' => 'ApiFriendshipsDestroy',
485                               'id' => '[a-zA-Z0-9]+',
486                               'format' => '(xml|json)'));
487
488             // Social graph
489
490             $m->connect('api/friends/ids/:id.:format',
491                         array('action' => 'ApiUserFriends',
492                               'ids_only' => true));
493
494             $m->connect('api/followers/ids/:id.:format',
495                         array('action' => 'ApiUserFollowers',
496                               'ids_only' => true));
497
498             $m->connect('api/friends/ids.:format',
499                         array('action' => 'ApiUserFriends',
500                               'ids_only' => true));
501
502             $m->connect('api/followers/ids.:format',
503                         array('action' => 'ApiUserFollowers',
504                               'ids_only' => true));
505
506             // account
507
508             $m->connect('api/account/verify_credentials.:format',
509                         array('action' => 'ApiAccountVerifyCredentials'));
510
511             $m->connect('api/account/update_profile.:format',
512                         array('action' => 'ApiAccountUpdateProfile'));
513
514             $m->connect('api/account/update_profile_image.:format',
515                         array('action' => 'ApiAccountUpdateProfileImage'));
516
517             $m->connect('api/account/update_profile_background_image.:format',
518                         array('action' => 'ApiAccountUpdateProfileBackgroundImage'));
519
520             $m->connect('api/account/update_profile_colors.:format',
521                         array('action' => 'ApiAccountUpdateProfileColors'));
522
523             $m->connect('api/account/update_delivery_device.:format',
524                         array('action' => 'ApiAccountUpdateDeliveryDevice'));
525
526             // special case where verify_credentials is called w/out a format
527
528             $m->connect('api/account/verify_credentials',
529                         array('action' => 'ApiAccountVerifyCredentials'));
530
531             $m->connect('api/account/rate_limit_status.:format',
532                         array('action' => 'ApiAccountRateLimitStatus'));
533
534             // favorites
535
536             $m->connect('api/favorites.:format',
537                         array('action' => 'ApiTimelineFavorites',
538                               'format' => '(xml|json|rss|atom)'));
539
540             $m->connect('api/favorites/:id.:format',
541                         array('action' => 'ApiTimelineFavorites',
542                               'id' => '[a-zA-Z0-9]+',
543                               'format' => '(xml|json|rss|atom)'));
544
545             $m->connect('api/favorites/create/:id.:format',
546                         array('action' => 'ApiFavoriteCreate',
547                               'id' => '[a-zA-Z0-9]+',
548                               'format' => '(xml|json)'));
549
550             $m->connect('api/favorites/destroy/:id.:format',
551                         array('action' => 'ApiFavoriteDestroy',
552                               'id' => '[a-zA-Z0-9]+',
553                               'format' => '(xml|json)'));
554             // blocks
555
556             $m->connect('api/blocks/create.:format',
557                         array('action' => 'ApiBlockCreate',
558                               'format' => '(xml|json)'));
559
560             $m->connect('api/blocks/create/:id.:format',
561                         array('action' => 'ApiBlockCreate',
562                               'id' => '[a-zA-Z0-9]+',
563                               'format' => '(xml|json)'));
564
565             $m->connect('api/blocks/destroy.:format',
566                         array('action' => 'ApiBlockDestroy',
567                               'format' => '(xml|json)'));
568
569             $m->connect('api/blocks/destroy/:id.:format',
570                         array('action' => 'ApiBlockDestroy',
571                               'id' => '[a-zA-Z0-9]+',
572                               'format' => '(xml|json)'));
573             // help
574
575             $m->connect('api/help/test.:format',
576                         array('action' => 'ApiHelpTest',
577                               'format' => '(xml|json)'));
578
579             // statusnet
580
581             $m->connect('api/statusnet/version.:format',
582                         array('action' => 'ApiStatusnetVersion',
583                               'format' => '(xml|json)'));
584
585             $m->connect('api/statusnet/config.:format',
586                         array('action' => 'ApiStatusnetConfig',
587                               'format' => '(xml|json)'));
588
589             // For older methods, we provide "laconica" base action
590
591             $m->connect('api/laconica/version.:format',
592                         array('action' => 'ApiStatusnetVersion',
593                               'format' => '(xml|json)'));
594
595             $m->connect('api/laconica/config.:format',
596                         array('action' => 'ApiStatusnetConfig',
597                               'format' => '(xml|json)'));
598
599             // Groups and tags are newer than 0.8.1 so no backward-compatibility
600             // necessary
601
602             // Groups
603             //'list' has to be handled differently, as php will not allow a method to be named 'list'
604
605             $m->connect('api/statusnet/groups/timeline/:id.:format',
606                         array('action' => 'ApiTimelineGroup',
607                               'id' => '[a-zA-Z0-9]+',
608                               'format' => '(xml|json|rss|atom)'));
609
610             $m->connect('api/statusnet/groups/show.:format',
611                         array('action' => 'ApiGroupShow',
612                               'format' => '(xml|json)'));
613
614             $m->connect('api/statusnet/groups/show/:id.:format',
615                         array('action' => 'ApiGroupShow',
616                               'id' => '[a-zA-Z0-9]+',
617                               'format' => '(xml|json)'));
618
619             $m->connect('api/statusnet/groups/join.:format',
620                         array('action' => 'ApiGroupJoin',
621                               'id' => '[a-zA-Z0-9]+',
622                               'format' => '(xml|json)'));
623
624             $m->connect('api/statusnet/groups/join/:id.:format',
625                         array('action' => 'ApiGroupJoin',
626                               'format' => '(xml|json)'));
627
628             $m->connect('api/statusnet/groups/leave.:format',
629                         array('action' => 'ApiGroupLeave',
630                               'id' => '[a-zA-Z0-9]+',
631                               'format' => '(xml|json)'));
632
633             $m->connect('api/statusnet/groups/leave/:id.:format',
634                         array('action' => 'ApiGroupLeave',
635                               'format' => '(xml|json)'));
636
637             $m->connect('api/statusnet/groups/is_member.:format',
638                         array('action' => 'ApiGroupIsMember',
639                               'format' => '(xml|json)'));
640
641             $m->connect('api/statusnet/groups/list.:format',
642                         array('action' => 'ApiGroupList',
643                               'format' => '(xml|json|rss|atom)'));
644
645             $m->connect('api/statusnet/groups/list/:id.:format',
646                         array('action' => 'ApiGroupList',
647                               'id' => '[a-zA-Z0-9]+',
648                               'format' => '(xml|json|rss|atom)'));
649
650             $m->connect('api/statusnet/groups/list_all.:format',
651                         array('action' => 'ApiGroupListAll',
652                               'format' => '(xml|json|rss|atom)'));
653
654             $m->connect('api/statusnet/groups/membership.:format',
655                         array('action' => 'ApiGroupMembership',
656                               'format' => '(xml|json)'));
657
658             $m->connect('api/statusnet/groups/membership/:id.:format',
659                         array('action' => 'ApiGroupMembership',
660                               'id' => '[a-zA-Z0-9]+',
661                               'format' => '(xml|json)'));
662
663             $m->connect('api/statusnet/groups/create.:format',
664                         array('action' => 'ApiGroupCreate',
665                               'format' => '(xml|json)'));
666             // Tags
667             $m->connect('api/statusnet/tags/timeline/:tag.:format',
668                         array('action' => 'ApiTimelineTag',
669                               'format' => '(xml|json|rss|atom)'));
670
671             // media related
672             $m->connect(
673                 'api/statusnet/media/upload',
674                 array('action' => 'ApiMediaUpload')
675             );
676
677             // search
678             $m->connect('api/search.atom', array('action' => 'ApiSearchAtom'));
679             $m->connect('api/search.json', array('action' => 'ApiSearchJSON'));
680             $m->connect('api/trends.json', array('action' => 'ApiTrends'));
681
682             $m->connect('api/oauth/request_token',
683                         array('action' => 'ApiOauthRequestToken'));
684
685             $m->connect('api/oauth/access_token',
686                         array('action' => 'ApiOauthAccessToken'));
687
688             $m->connect('api/oauth/authorize',
689                         array('action' => 'ApiOauthAuthorize'));
690
691             $m->connect('api/statusnet/app/service/:id.xml',
692                         array('action' => 'ApiAtomService',
693                               'id' => '[a-zA-Z0-9]+'));
694
695             $m->connect('api/statusnet/app/service.xml',
696                         array('action' => 'ApiAtomService'));
697
698             // Admin
699
700             $m->connect('admin/site', array('action' => 'siteadminpanel'));
701             $m->connect('admin/design', array('action' => 'designadminpanel'));
702             $m->connect('admin/user', array('action' => 'useradminpanel'));
703                 $m->connect('admin/access', array('action' => 'accessadminpanel'));
704             $m->connect('admin/paths', array('action' => 'pathsadminpanel'));
705             $m->connect('admin/sessions', array('action' => 'sessionsadminpanel'));
706             $m->connect('admin/sitenotice', array('action' => 'sitenoticeadminpanel'));
707             $m->connect('admin/snapshot', array('action' => 'snapshotadminpanel'));
708             $m->connect('admin/license', array('action' => 'licenseadminpanel'));
709
710             $m->connect('getfile/:filename',
711                         array('action' => 'getfile'),
712                         array('filename' => '[A-Za-z0-9._-]+'));
713
714             // In the "root"
715
716             if (common_config('singleuser', 'enabled')) {
717
718                 $user = User::singleUser();
719                 $nickname = $user->nickname;
720
721                 foreach (array('subscriptions', 'subscribers',
722                                'all', 'foaf', 'xrds',
723                                'replies', 'microsummary', 'hcard') as $a) {
724                     $m->connect($a,
725                                 array('action' => $a,
726                                       'nickname' => $nickname));
727                 }
728
729                 foreach (array('subscriptions', 'subscribers') as $a) {
730                     $m->connect($a.'/:tag',
731                                 array('action' => $a,
732                                       'nickname' => $nickname),
733                                 array('tag' => '[a-zA-Z0-9]+'));
734                 }
735
736                 foreach (array('rss', 'groups') as $a) {
737                     $m->connect($a,
738                                 array('action' => 'user'.$a,
739                                       'nickname' => $nickname));
740                 }
741
742                 foreach (array('all', 'replies', 'favorites') as $a) {
743                     $m->connect($a.'/rss',
744                                 array('action' => $a.'rss',
745                                       'nickname' => $nickname));
746                 }
747
748                 $m->connect('favorites',
749                             array('action' => 'showfavorites',
750                                   'nickname' => $nickname));
751
752                 $m->connect('avatar/:size',
753                             array('action' => 'avatarbynickname',
754                                   'nickname' => $nickname),
755                             array('size' => '(original|96|48|24)'));
756
757                 $m->connect('tag/:tag/rss',
758                             array('action' => 'userrss',
759                                   'nickname' => $nickname),
760                             array('tag' => '[\pL\pN_\-\.]{1,64}'));
761
762                 $m->connect('tag/:tag',
763                             array('action' => 'showstream',
764                                   'nickname' => $nickname),
765                             array('tag' => '[\pL\pN_\-\.]{1,64}'));
766
767                 $m->connect('rsd.xml',
768                             array('action' => 'rsd',
769                                   'nickname' => $nickname));
770
771                 $m->connect('',
772                             array('action' => 'showstream',
773                                   'nickname' => $nickname));
774             } else {
775                 $m->connect('', array('action' => 'public'));
776                 $m->connect('rss', array('action' => 'publicrss'));
777                 $m->connect('featuredrss', array('action' => 'featuredrss'));
778                 $m->connect('favoritedrss', array('action' => 'favoritedrss'));
779                 $m->connect('featured/', array('action' => 'featured'));
780                 $m->connect('featured', array('action' => 'featured'));
781                 $m->connect('favorited/', array('action' => 'favorited'));
782                 $m->connect('favorited', array('action' => 'favorited'));
783                 $m->connect('rsd.xml', array('action' => 'rsd'));
784
785                 foreach (array('subscriptions', 'subscribers',
786                                'nudge', 'all', 'foaf', 'xrds',
787                                'replies', 'inbox', 'outbox', 'microsummary', 'hcard') as $a) {
788                     $m->connect(':nickname/'.$a,
789                                 array('action' => $a),
790                                 array('nickname' => '[a-zA-Z0-9]{1,64}'));
791                 }
792
793                 foreach (array('subscriptions', 'subscribers') as $a) {
794                     $m->connect(':nickname/'.$a.'/:tag',
795                                 array('action' => $a),
796                                 array('tag' => '[a-zA-Z0-9]+',
797                                       'nickname' => '[a-zA-Z0-9]{1,64}'));
798                 }
799
800                 foreach (array('rss', 'groups') as $a) {
801                     $m->connect(':nickname/'.$a,
802                                 array('action' => 'user'.$a),
803                                 array('nickname' => '[a-zA-Z0-9]{1,64}'));
804                 }
805
806                 foreach (array('all', 'replies', 'favorites') as $a) {
807                     $m->connect(':nickname/'.$a.'/rss',
808                                 array('action' => $a.'rss'),
809                                 array('nickname' => '[a-zA-Z0-9]{1,64}'));
810                 }
811
812                 $m->connect(':nickname/favorites',
813                             array('action' => 'showfavorites'),
814                             array('nickname' => '[a-zA-Z0-9]{1,64}'));
815
816                 $m->connect(':nickname/avatar/:size',
817                             array('action' => 'avatarbynickname'),
818                             array('size' => '(original|96|48|24)',
819                                   'nickname' => '[a-zA-Z0-9]{1,64}'));
820
821                 $m->connect(':nickname/tag/:tag/rss',
822                             array('action' => 'userrss'),
823                             array('nickname' => '[a-zA-Z0-9]{1,64}'),
824                             array('tag' => '[\pL\pN_\-\.]{1,64}'));
825
826                 $m->connect(':nickname/tag/:tag',
827                             array('action' => 'showstream'),
828                             array('nickname' => '[a-zA-Z0-9]{1,64}'),
829                             array('tag' => '[\pL\pN_\-\.]{1,64}'));
830
831                 $m->connect(':nickname/rsd.xml',
832                             array('action' => 'rsd'),
833                             array('nickname' => '[a-zA-Z0-9]{1,64}'));
834
835                 $m->connect(':nickname',
836                             array('action' => 'showstream'),
837                             array('nickname' => '[a-zA-Z0-9]{1,64}'));
838             }
839
840             // user stuff
841
842             Event::handle('RouterInitialized', array($m));
843         }
844
845         return $m;
846     }
847
848     function map($path)
849     {
850         try {
851             $match = $this->m->match($path);
852         } catch (Net_URL_Mapper_InvalidException $e) {
853             common_log(LOG_ERR, "Problem getting route for $path - " .
854                        $e->getMessage());
855             // TRANS: Client error on action trying to visit a non-existing page.
856             $cac = new ClientErrorAction(_('Page not found.'), 404);
857             $cac->showPage();
858         }
859
860         return $match;
861     }
862
863     function build($action, $args=null, $params=null, $fragment=null)
864     {
865         $action_arg = array('action' => $action);
866
867         if ($args) {
868             $args = array_merge($action_arg, $args);
869         } else {
870             $args = $action_arg;
871         }
872
873         $url = $this->m->generate($args, $params, $fragment);
874
875         // Due to a bug in the Net_URL_Mapper code, the returned URL may
876         // contain a malformed query of the form ?p1=v1?p2=v2?p3=v3. We
877         // repair that here rather than modifying the upstream code...
878
879         $qpos = strpos($url, '?');
880         if ($qpos !== false) {
881             $url = substr($url, 0, $qpos+1) .
882               str_replace('?', '&', substr($url, $qpos+1));
883
884             // @fixme this is a hacky workaround for http_build_query in the
885             // lower-level code and bad configs that set the default separator
886             // to &amp; instead of &. Encoded &s in parameters will not be
887             // affected.
888             $url = substr($url, 0, $qpos+1) .
889               str_replace('&amp;', '&', substr($url, $qpos+1));
890
891         }
892
893         return $url;
894     }
895 }