1<?php
2// +-----------------------------------------------------------------------+
3// | This file is part of Piwigo.                                          |
4// |                                                                       |
5// | For copyright and license information, please view the COPYING.txt    |
6// | file that was distributed with this source code.                      |
7// +-----------------------------------------------------------------------+
8
9
10/**
11 * returns a prefix for each url link on displayed page
12 * and return an empty string for current path
13 * @return string
14 */
15function get_root_url()
16{
17  global $page;
18  if ( ($root_url = @$page['root_path']) == null )
19  {// TODO - add HERE the possibility to call PWG functions from external scripts
20    $root_url = PHPWG_ROOT_PATH;
21    if ( strncmp($root_url, './', 2) == 0 )
22    {
23      return substr($root_url, 2);
24    }
25  }
26  return $root_url;
27}
28
29/**
30 * returns the absolute url to the root of PWG
31 * @param boolean with_scheme if false - does not add http://toto.com
32 */
33function get_absolute_root_url($with_scheme=true)
34{
35  // TODO - add HERE the possibility to call PWG functions from external scripts
36  $url = '';
37  if ($with_scheme)
38  {
39    $is_https = false;
40    if (isset($_SERVER['HTTPS']) &&
41      ((strtolower($_SERVER['HTTPS']) == 'on') or ($_SERVER['HTTPS'] == 1)))
42    {
43      $is_https = true;
44      $url .= 'https://';
45    }
46    else
47    {
48      $url .= 'http://';
49    }
50    if (isset($_SERVER['HTTP_X_FORWARDED_HOST']))
51    {
52      $url .= $_SERVER['HTTP_X_FORWARDED_HOST'];
53    }
54    else
55    {
56      $url .= $_SERVER['HTTP_HOST'];
57      if ( (!$is_https && $_SERVER['SERVER_PORT'] != 80)
58            ||($is_https && $_SERVER['SERVER_PORT'] != 443))
59      {
60        $url_port = ':'.$_SERVER['SERVER_PORT'];
61        if (strrchr($url, ':') != $url_port)
62        {
63          $url .= $url_port;
64        }
65      }
66    }
67  }
68  $url .= cookie_path();
69  return $url;
70}
71
72/**
73 * adds one or more _GET style parameters to an url
74 * example: add_url_params('/x', array('a'=>'b')) returns /x?a=b
75 * add_url_params('/x?cat_id=10', array('a'=>'b')) returns /x?cat_id=10&amp;a=b
76 * @param string url
77 * @param array params
78 * @return string
79 */
80function add_url_params($url, $params, $arg_separator='&amp;' )
81{
82  if ( !empty($params) )
83  {
84    assert( is_array($params) );
85    $is_first = true;
86    foreach($params as $param=>$val)
87    {
88      if ($is_first)
89      {
90        $is_first = false;
91        $url .= ( strpos($url, '?')===false ) ? '?' : $arg_separator;
92      }
93      else
94      {
95        $url .= $arg_separator;
96      }
97      $url .= $param;
98      if (isset($val))
99      {
100        $url .= '='.$val;
101      }
102    }
103  }
104  return $url;
105}
106
107/**
108 * build an index URL for a specific section
109 *
110 * @param array
111 * @return string
112 */
113function make_index_url($params = array())
114{
115  global $conf;
116  $url = get_root_url().'index';
117  if ($conf['php_extension_in_urls'])
118  {
119    $url .= '.php';
120  }
121  if ($conf['question_mark_in_urls'])
122  {
123    $url .= '?';
124  }
125
126  $url_before_params = $url;
127
128  $url.= make_section_in_url($params);
129  $url = add_well_known_params_in_url($url, $params);
130
131  if ($url == $url_before_params)
132  {
133    $url = get_absolute_root_url( url_is_remote($url) );
134  }
135
136  return $url;
137}
138
139/**
140 * build an index URL with current page parameters, but with redefinitions
141 * and removes.
142 *
143 * duplicate_index_url( array(
144 *   'category' => array('id'=>12, 'name'=>'toto'),
145 *   array('start')
146 * ) will create an index URL on the current section (categories), but on
147 * a redefined category and without the start URL parameter.
148 *
149 * @param array redefined keys
150 * @param array removed keys
151 * @return string
152 */
153function duplicate_index_url($redefined = array(), $removed = array())
154{
155  return make_index_url(
156    params_for_duplication($redefined, $removed)
157    );
158}
159
160/**
161 * returns $page global array with key redefined and key removed
162 *
163 * @param array redefined keys
164 * @param array removed keys
165 * @return array
166 */
167function params_for_duplication($redefined, $removed)
168{
169  global $page;
170
171  $params = $page;
172
173  foreach ($removed as $param_key)
174  {
175    unset($params[$param_key]);
176  }
177
178  foreach ($redefined as $redefined_param => $redefined_value)
179  {
180    $params[$redefined_param] = $redefined_value;
181  }
182
183  return $params;
184}
185
186/**
187 * create a picture URL with current page parameters, but with redefinitions
188 * and removes. See duplicate_index_url.
189 *
190 * @param array redefined keys
191 * @param array removed keys
192 * @return string
193 */
194function duplicate_picture_url($redefined = array(), $removed = array())
195{
196  return make_picture_url(
197    params_for_duplication($redefined, $removed)
198    );
199}
200
201/**
202 * create a picture URL on a specific section for a specific picture
203 *
204 * @param array
205 * @return string
206 */
207function make_picture_url($params)
208{
209  global $conf;
210
211  $url = get_root_url().'picture';
212  if ($conf['php_extension_in_urls'])
213  {
214    $url .= '.php';
215  }
216  if ($conf['question_mark_in_urls'])
217  {
218    $url .= '?';
219  }
220  $url.= '/';
221  switch ( $conf['picture_url_style'] )
222  {
223    case 'id-file':
224      $url .= $params['image_id'];
225      if ( isset($params['image_file']) )
226      {
227        $url .= '-'.str2url(get_filename_wo_extension($params['image_file']));
228      }
229      break;
230    case 'file':
231      if ( isset($params['image_file']) )
232      {
233        $fname_wo_ext = get_filename_wo_extension($params['image_file']);
234        if ( ord($fname_wo_ext)>ord('9') or !preg_match('/^\d+(-|$)/', $fname_wo_ext) )
235        {
236          $url .= $fname_wo_ext;
237          break;
238        }
239      }
240    default:
241      $url .= $params['image_id'];
242  }
243  if ( !isset($params['category'] ) )
244  {// make urls shorter ...
245    unset( $params['flat'] );
246  }
247  $url .= make_section_in_url($params);
248  $url = add_well_known_params_in_url($url, $params);
249  return $url;
250}
251
252/**
253 *adds to the url the chronology and start parameters
254*/
255function add_well_known_params_in_url($url, $params)
256{
257  if ( isset($params['chronology_field']) )
258  {
259    $url .= '/'. $params['chronology_field'];
260    $url .= '-'. $params['chronology_style'];
261    if ( isset($params['chronology_view']) )
262    {
263      $url .= '-'. $params['chronology_view'];
264    }
265    if ( !empty($params['chronology_date']) )
266    {
267      $url .= '-'. implode('-', $params['chronology_date'] );
268    }
269  }
270
271  if (isset($params['flat']))
272  {
273    $url.= '/flat';
274  }
275
276  if (isset($params['start']) and $params['start'] > 0)
277  {
278    $url.= '/start-'.$params['start'];
279  }
280  return $url;
281}
282
283/**
284 * return the section token of an index or picture URL.
285 *
286 * Depending on section, other parameters are required (see function code
287 * for details)
288 *
289 * @param array
290 * @return string
291 */
292function make_section_in_url($params)
293{
294  global $conf;
295  $section_string = '';
296  $section = @$params['section'];
297  if (!isset($section))
298  {
299    $section_of = array(
300      'category' => 'categories',
301      'tags'     => 'tags',
302      'list'     => 'list',
303      'search'   => 'search',
304      );
305
306    foreach ($section_of as $param => $s)
307    {
308      if (isset($params[$param]))
309      {
310        $section = $s;
311      }
312    }
313
314    if (!isset($section))
315    {
316      $section = 'none';
317    }
318  }
319
320  switch($section)
321  {
322    case 'categories' :
323    {
324      if (!isset($params['category']))
325      {
326        $section_string.= '/categories';
327      }
328      else
329      {
330        isset($params['category']['name']) or trigger_error(
331            'make_section_in_url category name not set', E_USER_WARNING
332            );
333
334        array_key_exists('permalink', $params['category']) or trigger_error(
335            'make_section_in_url category permalink not set', E_USER_WARNING
336            );
337
338        $section_string.= '/category/';
339        if ( empty($params['category']['permalink']) )
340        {
341          $section_string.= $params['category']['id'];
342          if ( $conf['category_url_style']=='id-name' )
343          {
344            $section_string.= '-'.str2url($params['category']['name']);
345          }
346        }
347        else
348        {
349          $section_string.= $params['category']['permalink'];
350        }
351
352        if (isset($params['combined_categories']))
353        {
354          foreach ($params['combined_categories'] as $category)
355          {
356            $section_string.= '/';
357
358            if ( empty($category['permalink']) )
359            {
360              $section_string.= $category['id'];
361              if ( $conf['category_url_style']=='id-name' )
362              {
363                $section_string.= '-'.str2url($category['name']);
364              }
365            }
366            else
367            {
368              $section_string.= $category['permalink'];
369            }
370          }
371        }
372      }
373
374      break;
375    }
376    case 'tags' :
377    {
378      $section_string.= '/tags';
379
380      foreach ($params['tags'] as $tag)
381      {
382        switch ( $conf['tag_url_style'] )
383        {
384          case 'id':
385            $section_string.= '/'.$tag['id'];
386            break;
387          case 'tag':
388            if (isset($tag['url_name']))
389            {
390              $section_string.= '/'.$tag['url_name'];
391              break;
392            }
393          default:
394            $section_string.= '/'.$tag['id'];
395            if (isset($tag['url_name']))
396            {
397              $section_string.= '-'.$tag['url_name'];
398            }
399        }
400      }
401
402      break;
403    }
404    case 'search' :
405    {
406      $section_string.= '/search/'.$params['search'];
407      break;
408    }
409    case 'list' :
410    {
411      $section_string.= '/list/'.implode(',', $params['list']);
412      break;
413    }
414    case 'none' :
415    {
416      break;
417    }
418    default :
419    {
420      $section_string.= '/'.$section;
421    }
422  }
423
424  return $section_string;
425}
426
427/**
428 * the reverse of make_section_in_url
429 * returns the 'section' (categories/tags/...) and the data associated with it
430 *
431 * Depending on section, other parameters are returned (category/tags/list/...)
432 *
433 * @param array of url tokens to parse
434 * @param int the index in the array of url tokens; in/out
435 * @return array
436 */
437function parse_section_url( $tokens, &$next_token)
438{
439  $page=array();
440  if (strncmp(@$tokens[$next_token], 'categor', 7)==0 )
441  {
442    $page['section'] = 'categories';
443    $next_token++;
444
445    $i = $next_token;
446    $loop_counter = 0;
447
448    while (isset($tokens[$next_token]))
449    {
450      if ($loop_counter++ > count($tokens)+10){die('infinite loop?');}
451
452      if (
453        strpos($tokens[$next_token], 'created-')===0
454        or strpos($tokens[$next_token], 'posted-')===0
455        or strpos($tokens[$next_token], 'start-')===0
456        or strpos($tokens[$next_token], 'startcat-')===0
457        or 'flat' == $tokens[$next_token]
458      )
459      {
460        break;
461      }
462
463      if (preg_match('/^(\d+)(?:-(.+))?$/', $tokens[$next_token], $matches))
464      {
465        if ( isset($matches[2]) )
466          $page['hit_by']['cat_url_name'] = $matches[2];
467
468        if (!isset($page['category']))
469        {
470          $page['category'] = $matches[1];
471        }
472        else
473        {
474          $page['combined_categories'][] = $matches[1];
475        }
476        $next_token++;
477      }
478      else
479      {// try a permalink
480        $maybe_permalinks = array();
481        $current_token = $next_token;
482        while ( isset($tokens[$current_token])
483            and strpos($tokens[$current_token], 'created-')!==0
484            and strpos($tokens[$current_token], 'posted-')!==0
485            and strpos($tokens[$next_token], 'start-')!==0
486            and strpos($tokens[$next_token], 'startcat-')!==0
487            and $tokens[$current_token] != 'flat')
488        {
489          if (empty($maybe_permalinks))
490          {
491            $maybe_permalinks[] = $tokens[$current_token];
492          }
493          else
494          {
495            $maybe_permalinks[] =
496                $maybe_permalinks[count($maybe_permalinks)-1]
497                . '/' . $tokens[$current_token];
498          }
499          $current_token++;
500        }
501
502        if ( count($maybe_permalinks) )
503        {
504          $cat_id = get_cat_id_from_permalinks($maybe_permalinks, $perma_index);
505          if ( isset($cat_id) )
506          {
507            $next_token += $perma_index+1;
508
509            if (!isset($page['category']))
510            {
511              $page['category'] = $cat_id;
512              $page['hit_by']['cat_permalink'] = $maybe_permalinks[$perma_index];
513            }
514            else
515            {
516              $page['combined_categories'][] = $cat_id;
517            }
518          }
519          else
520          {
521            page_not_found(l10n('Permalink for album not found'));
522          }
523        }
524      }
525    }
526
527    if (isset($page['category']))
528    {
529      $result = get_cat_info($page['category']);
530      if (empty($result))
531      {
532         page_not_found(l10n('Requested album does not exist'));
533      }
534      $page['category']=$result;
535    }
536
537    if (isset($page['combined_categories']))
538    {
539      $combined_categories = array();
540
541      foreach ($page['combined_categories'] as $cat_id)
542      {
543        $result = get_cat_info($cat_id);
544        if (empty($result))
545        {
546          page_not_found(l10n('Requested album does not exist'));
547        }
548
549        $combined_categories[] = $result;
550      }
551
552      $page['combined_categories'] = $combined_categories;
553    }
554  }
555  elseif ( 'tags' == @$tokens[$next_token] )
556  {
557    global $conf;
558
559    $page['section'] = 'tags';
560    $page['tags'] = array();
561
562    $next_token++;
563    $i = $next_token;
564
565    $requested_tag_ids = array();
566    $requested_tag_url_names = array();
567
568    while (isset($tokens[$i]))
569    {
570      if (strpos($tokens[$i], 'created-')===0
571           or strpos($tokens[$i], 'posted-')===0
572           or strpos($tokens[$i], 'start-')===0 )
573        break;
574
575      if ( $conf['tag_url_style'] != 'tag' and preg_match('/^(\d+)(?:-(.*)|)$/', $tokens[$i], $matches) )
576      {
577        $requested_tag_ids[] = $matches[1];
578      }
579      else
580      {
581        $requested_tag_url_names[] = $tokens[$i];
582      }
583      $i++;
584    }
585    $next_token = $i;
586
587    if ( empty($requested_tag_ids) && empty($requested_tag_url_names) )
588    {
589      bad_request('at least one tag required');
590    }
591
592    $page['tags'] = find_tags($requested_tag_ids, $requested_tag_url_names);
593    if ( empty($page['tags']) )
594    {
595      page_not_found(l10n('Requested tag does not exist'), get_root_url().'tags.php' );
596    }
597  }
598  elseif ( 'favorites' == @$tokens[$next_token] )
599  {
600    $page['section'] = 'favorites';
601    $next_token++;
602  }
603  elseif ('most_visited' == @$tokens[$next_token])
604  {
605    $page['section'] = 'most_visited';
606    $next_token++;
607  }
608  elseif ('best_rated' == @$tokens[$next_token])
609  {
610    $page['section'] = 'best_rated';
611    $next_token++;
612  }
613  elseif ('recent_pics' == @$tokens[$next_token])
614  {
615    $page['section'] = 'recent_pics';
616    $next_token++;
617  }
618  elseif ('recent_cats' == @$tokens[$next_token])
619  {
620    $page['section'] = 'recent_cats';
621    $next_token++;
622  }
623  elseif ('search' == @$tokens[$next_token])
624  {
625    $page['section'] = 'search';
626    $next_token++;
627
628    preg_match('/(\d+)/', @$tokens[$next_token], $matches);
629    if (!isset($matches[1]))
630    {
631      bad_request('search identifier is missing');
632    }
633    $page['search'] = $matches[1];
634    $next_token++;
635  }
636  elseif ('list' == @$tokens[$next_token])
637  {
638    $page['section'] = 'list';
639    $next_token++;
640
641    $page['list'] = array();
642
643    // No pictures
644    if (empty($tokens[$next_token]))
645    {
646      // Add dummy element list
647      $page['list'][] = -1;
648    }
649    // With pictures list
650    else
651    {
652      if (!preg_match('/^\d+(,\d+)*$/', $tokens[$next_token]))
653      {
654        bad_request('wrong format on list GET parameter');
655      }
656      foreach (explode(',', $tokens[$next_token]) as $image_id)
657      {
658        $page['list'][] = $image_id;
659      }
660    }
661    $next_token++;
662  }
663  return $page;
664}
665
666/**
667 * the reverse of add_well_known_params_in_url
668 * parses start, flat and chronology from url tokens
669*/
670function parse_well_known_params_url($tokens, &$i)
671{
672  $page = array();
673  while (isset($tokens[$i]))
674  {
675    if ( 'flat' == $tokens[$i] )
676    {
677      // indicate a special list of images
678      $page['flat'] = true;
679    }
680    elseif (strpos($tokens[$i], 'created-')===0 or strpos($tokens[$i], 'posted-')===0)
681    {
682      $chronology_tokens = explode('-', $tokens[$i] );
683
684      $page['chronology_field'] = $chronology_tokens[0];
685
686      array_shift($chronology_tokens);
687      $page['chronology_style'] = $chronology_tokens[0];
688
689      array_shift($chronology_tokens);
690      if ( count($chronology_tokens)>0 )
691      {
692        if ('list'==$chronology_tokens[0] or
693            'calendar'==$chronology_tokens[0])
694        {
695          $page['chronology_view'] = $chronology_tokens[0];
696          array_shift($chronology_tokens);
697        }
698        $page['chronology_date'] = $chronology_tokens;
699      }
700    }
701    elseif (preg_match('/^start-(\d+)/', $tokens[$i], $matches))
702    {
703      $page['start'] = $matches[1];
704    }
705    elseif (preg_match('/^startcat-(\d+)/', $tokens[$i], $matches))
706    {
707      $page['startcat'] = $matches[1];
708    }
709    $i++;
710  }
711  return $page;
712}
713
714
715/**
716 * @param id image id
717 * @param what_part string one of 'e' (element), 'r' (representative)
718 */
719function get_action_url($id, $what_part, $download)
720{
721  $params = array(
722        'id' => $id,
723        'part' => $what_part,
724      );
725  if ($download)
726  {
727    $params['download'] = null;
728  }
729
730  return add_url_params(get_root_url().'action.php', $params);
731}
732
733/*
734 * @param element_info array containing element information from db;
735 * at least 'id', 'path' should be present
736 */
737function get_element_url($element_info)
738{
739  $url = $element_info['path'];
740  if ( !url_is_remote($url) )
741  {
742    $url = embellish_url(get_root_url().$url);
743  }
744  return $url;
745}
746
747
748/**
749 * Indicate to build url with full path
750 *
751 * @param null
752 * @return null
753 */
754function set_make_full_url()
755{
756  global $page;
757
758  if (!isset($page['save_root_path']))
759  {
760    if (isset($page['root_path']))
761    {
762      $page['save_root_path']['path'] = $page['root_path'];
763    }
764    $page['save_root_path']['count'] = 1;
765    $page['root_path'] = get_absolute_root_url();
766  }
767  else
768  {
769    $page['save_root_path']['count'] += 1;
770  }
771}
772
773/**
774 * Restore old parameter to build url with full path
775 *
776 * @param null
777 * @return null
778 */
779function unset_make_full_url()
780{
781  global $page;
782
783  if (isset($page['save_root_path']))
784  {
785    if ($page['save_root_path']['count'] == 1)
786    {
787      if (isset($page['save_root_path']['path']))
788      {
789        $page['root_path'] = $page['save_root_path']['path'];
790      }
791      else
792      {
793        unset($page['root_path']);
794      }
795      unset($page['save_root_path']);
796    }
797    else
798    {
799      $page['save_root_path']['count'] -= 1;
800    }
801  }
802}
803
804/**
805 * Embellish the url argument
806 *
807 * @param $url
808 * @return $url embellished
809 */
810function embellish_url($url)
811{
812  $url = str_replace('/./', '/', $url);
813  while ( ($dotdot = strpos($url, '/../', 1) ) !== false )
814  {
815    $before = strrpos($url, '/', -(strlen($url)-$dotdot+1) );
816    if ($before !== false)
817    {
818      $url = substr_replace($url, '', $before, $dotdot-$before+3);
819    }
820    else
821      break;
822  }
823  return $url;
824}
825
826/**
827 * Returns the 'home page' of this gallery
828 */
829function get_gallery_home_url()
830{
831  global $conf;
832  if (!empty($conf['gallery_url']))
833  {
834    if (url_is_remote($conf['gallery_url']) or $conf['gallery_url'][0]=='/' )
835    {
836      return $conf['gallery_url'];
837    }
838    return get_root_url().$conf['gallery_url'];
839  }
840  else
841  {
842    return make_index_url();
843  }
844}
845
846/**
847 * returns $_SERVER['QUERY_STRING'] whithout keys given in parameters
848 *
849 * @param string[] $rejects
850 * @param boolean $escape escape *&* to *&amp;*
851 * @returns string
852 */
853function get_query_string_diff($rejects=array(), $escape=true)
854{
855  if (empty($_SERVER['QUERY_STRING']))
856  {
857    return '';
858  }
859
860  parse_str($_SERVER['QUERY_STRING'], $vars);
861
862  $vars = array_diff_key($vars, array_flip($rejects));
863
864  return '?' . http_build_query($vars, '', $escape ? '&amp;' : '&');
865}
866
867/**
868 * returns true if the url is absolute (begins with http)
869 *
870 * @param string $url
871 * @returns boolean
872 */
873function url_is_remote($url)
874{
875  if ( strncmp($url, 'http://', 7)==0
876    or strncmp($url, 'https://', 8)==0 )
877  {
878    return true;
879  }
880  return false;
881}
882
883?>
884