Implement #015359: access.php - new MatchOrder=host_uri
[tinyz:tinyz.git] / kernel / classes / ezsiteaccess.php
1 <?php
2 /**
3  * File containing (site)access functionality
4  *
5  * @copyright Copyright (C) 1999-2010 eZ Systems AS. All rights reserved.
6  * @license http://ez.no/licenses/gnu_gpl GNU GPL v2
7  * @version //autogentag//
8  * @package kernel
9  */
10
11 /**
12  * Provides functions for siteaccess handling
13  *
14  * @package kernel
15  */
16 class eZSiteAccess
17 {
18     /**
19      * Integer constants that identify the siteaccess matching used
20      *
21      * @since 4.4 Was earlier in access.php as normal constants
22      */
23     const TYPE_DEFAULT = 1;
24     const TYPE_URI = 2;
25     const TYPE_PORT = 3;
26     const TYPE_HTTP_HOST = 4;
27     const TYPE_INDEX_FILE = 5;
28     const TYPE_STATIC = 6;
29     const TYPE_SERVER_VAR = 7;
30     const TYPE_URL = 8;
31
32     const SUBTYPE_PRE = 1;
33     const SUBTYPE_POST = 2;
34
35     /*!
36      Constructor
37     */
38     function eZSiteAccess()
39     {
40     }
41
42     static function siteAccessList()
43     {
44         $siteAccessList = array();
45         $ini = eZINI::instance();
46         $availableSiteAccessList = $ini->variable( 'SiteAccessSettings', 'AvailableSiteAccessList' );
47         if ( !is_array( $availableSiteAccessList ) )
48             $availableSiteAccessList = array();
49
50         $serverSiteAccess = eZSys::serverVariable( $ini->variable( 'SiteAccessSettings', 'ServerVariableName' ), true );
51         if ( $serverSiteAccess )
52             $availableSiteAccessList[] = $serverSiteAccess;
53
54         $availableSiteAccessList = array_unique( $availableSiteAccessList );
55         foreach ( $availableSiteAccessList as $siteAccessName )
56         {
57             $siteAccessItem = array();
58             $siteAccessItem['name'] = $siteAccessName;
59             $siteAccessItem['id'] = eZSys::ezcrc32( $siteAccessName );
60             $siteAccessList[] = $siteAccessItem;
61         }
62         return $siteAccessList;
63     }
64
65     /**
66      * Returns path to site access
67      *
68      * @param string $siteAccess
69      * @return string|false Return path to siteacces or false if invalid
70      */
71     static function findPathToSiteAccess( $siteAccess )
72     {
73         $ini = eZINI::instance();
74         $siteAccessList = $ini->variable( 'SiteAccessSettings', 'AvailableSiteAccessList' );
75         if ( !in_array( $siteAccess, $siteAccessList )  )
76             return false;
77
78         $currentPath = 'settings/siteaccess/' . $siteAccess;
79         if ( file_exists( $currentPath ) )
80             return $currentPath;
81
82         $activeExtensions = eZExtension::activeExtensions();
83         $baseDir = eZExtension::baseDirectory();
84         foreach ( $activeExtensions as $extension )
85         {
86             $currentPath = $baseDir . '/' . $extension . '/settings/siteaccess/' . $siteAccess;
87             if ( file_exists( $currentPath ) )
88                 return $currentPath;
89         }
90
91         return 'settings/siteaccess/' . $siteAccess;
92     }
93
94     /**
95      * Re-initialises the current site access
96      *
97      * - clears all in-memory caches used by the INI system
98      * - re-builds the list of paths where INI files are searched for
99      * - re-searches module paths
100      *
101      * @return bool True if re-initialisation was successful
102      */
103     static function reInitialise()
104     {
105         if ( isset( $GLOBALS['eZCurrentAccess'] ) )
106         {
107             eZINI::resetAllGlobals();
108
109             eZExtension::activateExtensions( 'default' );
110             $accessName = $GLOBALS['eZCurrentAccess']['name'];
111             if ( file_exists( "settings/siteaccess/$accessName" ) )
112             {
113                 $ini = eZINI::instance();
114                 $ini->prependOverrideDir( "siteaccess/$accessName", false, 'siteaccess' );
115             }
116             eZExtension::prependExtensionSiteAccesses( $accessName );
117             eZExtension::activateExtensions( 'access' );
118
119             $moduleRepositories = eZModule::activeModuleRepositories();
120             eZModule::setGlobalPathList( $moduleRepositories );
121
122             return true;
123         }
124         else
125         {
126             return false;
127         }
128     }
129
130     /**
131      * Goes trough the access matching rules and returns the access match.
132      * The returned match is an associative array with:
133      *  name     => string Name of the siteaccess (same as folder name)
134      *  type     => int The constant that represent the matching used
135      *  uri_part => array(string) List of path elements that was used in start of url for the match
136      *
137      * @since 4.4
138      * @param eZURI $uri
139      * @param string $host
140      * @param string(numeric) $port
141      * @param string $file Example '/index.php'
142      * @return array
143      */
144     public static function match( eZURI $uri, $host, $port, $file )
145     {
146         $ini = eZINI::instance();
147         if ( $ini->hasVariable( 'SiteAccessSettings', 'StaticMatch' ) )
148         {
149             $match = $ini->variable( 'SiteAccessSettings', 'StaticMatch' );
150             if ( $match != '' )
151             {
152                 $access = array( 'name' => $match,
153                                  'type' => eZSiteAccess::TYPE_STATIC,
154                                  'uri_part' => array() );
155                 return $access;
156             }
157         }
158
159         list( $siteAccessList, $order ) =
160             $ini->variableMulti( 'SiteAccessSettings', array( 'AvailableSiteAccessList', 'MatchOrder' ) );
161         $access = array( 'name' => $ini->variable( 'SiteSettings', 'DefaultAccess' ),
162                          'type' => eZSiteAccess::TYPE_DEFAULT,
163                          'uri_part' => array() );
164
165         if ( $order == 'none' )
166             return $access;
167
168         $order = $ini->variableArray( 'SiteAccessSettings', 'MatchOrder' );
169
170         // Change the default type to eZSiteAccess::TYPE_URI if we're using URI MatchOrder.
171         // This is to keep backward compatiblity with the ezurl operator. ezurl has since
172         // rev 4949 added default siteaccess to generated URLs, even when there is
173         // no siteaccess in the current URL.
174         if ( in_array( 'uri', $order ) )
175         {
176             $access['type'] = eZSiteAccess::TYPE_URI;
177         }
178
179         foreach ( $order as $matchprobe )
180         {
181             $name = '';
182             $type = '';
183             $match_type = '';
184             $uri_part = array();
185
186             switch( $matchprobe )
187             {
188                 case 'servervar':
189                 {
190                     if ( $serversiteaccess = eZSys::serverVariable( $ini->variable( 'SiteAccessSettings', 'ServerVariableName' ), true ) )
191                     {
192                         $access['name'] = $serversiteaccess;
193                         $access['type'] = eZSiteAccess::TYPE_SERVER_VAR;
194                         return $access;
195                     }
196                     else
197                         continue;
198                 } break;
199                 case 'port':
200                 {
201                     if ( $ini->hasVariable( 'PortAccessSettings', $port ) )
202                     {
203                         $access['name'] = $ini->variable( 'PortAccessSettings', $port );
204                         $access['type'] = eZSiteAccess::TYPE_PORT;
205                         return $access;
206                     }
207                     else
208                         continue;
209                 } break;
210                 case 'uri':
211                 {
212                     $type = eZSiteAccess::TYPE_URI;
213                     $match_type = $ini->variable( 'SiteAccessSettings', 'URIMatchType' );
214
215                     if ( $match_type == 'map' )
216                     {
217                         if ( $ini->hasVariable( 'SiteAccessSettings', 'URIMatchMapItems' ) )
218                         {
219                             $match_item = $uri->element( 0 );
220                             $matchMapItems = $ini->variableArray( 'SiteAccessSettings', 'URIMatchMapItems' );
221                             foreach ( $matchMapItems as $matchMapItem )
222                             {
223                                 $matchMapURI = $matchMapItem[0];
224                                 $matchMapAccess = $matchMapItem[1];
225                                 if ( $access['name']  == $matchMapAccess and in_array( $matchMapAccess, $siteAccessList ) )
226                                 {
227                                     $uri_part = array( $matchMapURI );
228                                 }
229                                 if ( $matchMapURI == $match_item and in_array( $matchMapAccess, $siteAccessList ) )
230                                 {
231                                     $uri->increase( 1 );
232                                     $uri->dropBase();
233                                     $access['name'] = $matchMapAccess;
234                                     $access['type'] = $type;
235                                     $access['uri_part'] = array( $matchMapURI );
236                                     return $access;
237                                 }
238                             }
239                         }
240                     }
241                     else if ( $match_type == 'element' )
242                     {
243                         $match_index = $ini->variable( 'SiteAccessSettings', 'URIMatchElement' );
244                         $elements = $uri->elements( false );
245                         $elements = array_slice( $elements, 0, $match_index );
246                         $name = implode( '_', $elements );
247                         $uri_part = $elements;
248                     }
249                     else if ( $match_type == 'text' )
250                     {
251                         $match_item = $uri->elements();
252                         $matcher_pre = $ini->variable( 'SiteAccessSettings', 'URIMatchSubtextPre' );
253                         $matcher_post = $ini->variable( 'SiteAccessSettings', 'URIMatchSubtextPost' );
254                     }
255                     else if ( $match_type == 'regexp' )
256                     {
257                         $match_item = $uri->elements();
258                         $matcher = $ini->variable( 'SiteAccessSettings', 'URIMatchRegexp' );
259                         $match_num = $ini->variable( 'SiteAccessSettings', 'URIMatchRegexpItem' );
260                     }
261                     else
262                         continue;
263                 } break;
264                 case 'host':
265                 {
266                     $type = eZSiteAccess::TYPE_HTTP_HOST;
267                     $match_type = $ini->variable( 'SiteAccessSettings', 'HostMatchType' );
268                     $match_item = $host;
269                     if ( $match_type == 'map' )
270                     {
271                         if ( $ini->hasVariable( 'SiteAccessSettings', 'HostMatchMapItems' ) )
272                         {
273                             $matchMapItems = $ini->variableArray( 'SiteAccessSettings', 'HostMatchMapItems' );
274                             foreach ( $matchMapItems as $matchMapItem )
275                             {
276                                 $matchMapHost = $matchMapItem[0];
277                                 $matchMapAccess = $matchMapItem[1];
278                                 if ( $matchMapHost == $host )
279                                 {
280                                     $access['name'] = $matchMapAccess;
281                                     $access['type'] = $type;
282                                     return $access;
283                                 }
284                             }
285                         }
286                     }
287                     else if ( $match_type == 'element' )
288                     {
289                         $match_index = $ini->variable( 'SiteAccessSettings', 'HostMatchElement' );
290                         $match_arr = explode( '.', $match_item );
291                         $name = $match_arr[$match_index];
292                     }
293                     else if ( $match_type == 'text' )
294                     {
295                         $matcher_pre = $ini->variable( 'SiteAccessSettings', 'HostMatchSubtextPre' );
296                         $matcher_post = $ini->variable( 'SiteAccessSettings', 'HostMatchSubtextPost' );
297                     }
298                     else if ( $match_type == 'regexp' )
299                     {
300                         $matcher = $ini->variable( 'SiteAccessSettings', 'HostMatchRegexp' );
301                         $match_num = $ini->variable( 'SiteAccessSettings', 'HostMatchRegexpItem' );
302                     }
303                     else
304                         continue;
305                 } break;
306                 case 'index':
307                 {
308                     $type = eZSiteAccess::TYPE_INDEX_FILE;
309                     $match_type = $ini->variable( 'SiteAccessSettings', 'IndexMatchType' );
310                     $match_item = $file;
311                     if ( $match_type == 'element' )
312                     {
313                         $match_index = $ini->variable( 'SiteAccessSettings', 'IndexMatchElement' );
314                         $match_pos = strpos( $match_item, '.php' );
315                         if ( $match_pos !== false )
316                         {
317                             $match_item = substr( $match_item, 0, $match_pos );
318                             $match_arr = explode( '_', $match_item );
319                             $name = $match_arr[$match_index];
320                         }
321                     }
322                     else if ( $match_type == 'text' )
323                     {
324                         $matcher_pre = $ini->variable( 'SiteAccessSettings', 'IndexMatchSubtextPre' );
325                         $matcher_post = $ini->variable( 'SiteAccessSettings', 'IndexMatchSubtextPost' );
326                     }
327                     else if ( $match_type == 'regexp' )
328                     {
329                         $matcher = $ini->variable( 'SiteAccessSettings', 'IndexMatchRegexp' );
330                         $match_num = $ini->variable( 'SiteAccessSettings', 'IndexMatchRegexpItem' );
331                     }
332                     else
333                         continue;
334                 } break;
335                 default:
336                 {
337                     eZDebug::writeError( "Unknown access match: $match", "access" );
338                 } break;
339             }
340
341             if ( $match_type == 'regexp' )
342                 $name = self::matchRegexp( $match_item, $matcher, $match_num );
343             else if ( $match_type == 'text' )
344                 $name = self::matchText( $match_item, $matcher_pre, $matcher_post );
345
346             if ( isset( $name ) && $name != '' )
347             {
348                 $name = preg_replace( array( '/[^a-zA-Z0-9]+/', '/_+/', '/^_/', '/_$/' ),
349                                       array( '_', '_', '', '' ),
350                                       $name );
351
352                 if ( in_array( $name, $siteAccessList ) )
353                 {
354                     if ( $type == eZSiteAccess::TYPE_URI )
355                     {
356                         if ( $match_type == 'element' )
357                         {
358                             $uri->increase( $match_index );
359                             $uri->dropBase();
360                         }
361                         else if ( $match_type == 'regexp' )
362                         {
363                             $uri->setURIString( $match_item );
364                         }
365                         else if ( $match_type == 'text' )
366                         {
367                             $uri->setURIString( $match_item );
368                         }
369                     }
370                     $access['type']     = $type;
371                     $access['name']     = $name;
372                     $access['uri_part'] = $uri_part;
373                     return $access;
374                 }
375             }
376         }
377         return $access;
378     }
379
380     /**
381      * Match a regex expression
382      *
383      * @since 4.4
384      * @param string $text
385      * @param string $reg
386      * @param int $num
387      * @return string|null
388      */
389     static function matchRegexp( &$text, $reg, $num )
390     {
391         $reg = str_replace( '/', "\\/", $reg );
392         if ( preg_match( "/$reg/", $text, $regs ) && $num < count( $regs ) )
393         {
394             $text = str_replace( $regs[$num], '', $text );
395             return $regs[$num];
396         }
397         return null;
398     }
399
400     /**
401      * Match a text string with pre or/or post text strings
402      *
403      * @since 4.4
404      * @param string $text
405      * @param string $match_pre
406      * @param string $match_post
407      * @return string|null
408      */
409     static function matchText( &$text, $match_pre, $match_post )
410     {
411         $ret = null;
412         if ( $match_pre !== '' )
413         {
414             $pos = strpos( $text, $match_pre );
415             if ( $pos === false )
416                 return null;
417
418             $ret = substr( $text, $pos + strlen( $match_pre ) );
419             $text = substr( $text, 0, $pos );
420         }
421         if ( $match_post !== '' )
422         {
423             $pos = strpos( $ret, $match_post );
424             if ( $pos === false )
425                 return null;
426
427             $text .= substr( $ret, $pos + 1 );
428             $ret = substr( $ret, 0, $pos );
429         }
430         return $ret;
431     }
432
433    /**
434     * Changes the site access to what's defined in $access. It will change the
435     * access path in eZSys and prepend an override dir to eZINI
436     *
437     * @since 4.4
438     * @param array $access An associative array with 'name' (string), 'type' (int) and 'uri_part' (array).
439     * @return array The $access parameter
440     */
441     static function change( array $access )
442     {
443         eZSys::clearAccessPath();
444         $GLOBALS['eZCurrentAccess'] =& $access;
445
446         $name = $access['name'];
447         if ( isset( $access['uri_part'] ) &&
448              $access['uri_part'] !== null )
449         {
450             eZSys::addAccessPath( $access['uri_part'] );
451         }
452
453         $ini = eZINI::instance();
454         if ( file_exists( "settings/siteaccess/$name" ) )
455         {
456             $ini->prependOverrideDir( "siteaccess/$name", false, 'siteaccess' );
457         }
458
459         /* Make sure extension siteaccesses are prepended */
460         eZExtension::prependExtensionSiteAccesses( $name );
461
462         $ini->loadCache();
463
464         eZUpdateDebugSettings();
465         if ( self::debugEnabled() )
466         {
467             eZDebug::writeDebug( "Updated settings to use siteaccess '$name'", __METHOD__ );
468         }
469
470         return $access;
471     }
472
473     /**
474      * Get current siteaccess data
475      *
476      * @since 4.4
477      * return array|null
478      */
479     static function current()
480     {
481         if ( isset( $GLOBALS['eZCurrentAccess']['name'] ) )
482             return $GLOBALS['eZCurrentAccess'];
483         return null;
484     }
485
486     /**
487      * Checks if site access debug is enabled
488      *
489      * @since 4.4
490      * @return bool
491      */
492     static function debugEnabled()
493     {
494         $ini = eZINI::instance();
495         return $ini->variable( 'SiteAccessSettings', 'DebugAccess' ) === 'enabled';
496     }
497
498     /**
499      * Checks if extra site access debug is enabled
500      *
501      * @since 4.4
502      * @return bool
503      */
504     static function extraDebugEnabled()
505     {
506         $ini = eZINI::instance();
507         return $ini->variable( 'SiteAccessSettings', 'DebugExtraAccess' ) === 'enabled';
508     }
509 }
510
511 ?>