source: trunk/i.php @ 19989

Last change on this file since 19989 was 19703, checked in by plg, 12 years ago

update Piwigo headers to 2013 (the end of the world didn't occur as expected on r12922)

File size: 16.9 KB
RevLine 
[12796]1<?php
2// +-----------------------------------------------------------------------+
3// | Piwigo - a PHP based photo gallery                                    |
4// +-----------------------------------------------------------------------+
[19703]5// | Copyright(C) 2008-2013 Piwigo Team                  http://piwigo.org |
[12796]6// +-----------------------------------------------------------------------+
7// | This program is free software; you can redistribute it and/or modify  |
8// | it under the terms of the GNU General Public License as published by  |
9// | the Free Software Foundation                                          |
10// |                                                                       |
11// | This program is distributed in the hope that it will be useful, but   |
12// | WITHOUT ANY WARRANTY; without even the implied warranty of            |
13// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      |
14// | General Public License for more details.                              |
15// |                                                                       |
16// | You should have received a copy of the GNU General Public License     |
17// | along with this program; if not, write to the Free Software           |
18// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
19// | USA.                                                                  |
20// +-----------------------------------------------------------------------+
21
[12798]22define('PHPWG_ROOT_PATH','./');
[12796]23
24// fast bootstrap - no db connection
25include(PHPWG_ROOT_PATH . 'include/config_default.inc.php');
26@include(PHPWG_ROOT_PATH. 'local/config/config.inc.php');
27
28defined('PWG_LOCAL_DIR') or define('PWG_LOCAL_DIR', 'local/');
[12802]29defined('PWG_DERIVATIVE_DIR') or define('PWG_DERIVATIVE_DIR', $conf['data_location'].'i/');
[12796]30
[13736]31@include(PHPWG_ROOT_PATH.PWG_LOCAL_DIR .'config/database.inc.php');
32
33
[12796]34function trigger_action() {}
35function get_extension( $filename )
36{
37  return substr( strrchr( $filename, '.' ), 1, strlen ( $filename ) );
38}
39
40function mkgetdir($dir)
41{
42  if ( !is_dir($dir) )
43  {
44    global $conf;
45    if (substr(PHP_OS, 0, 3) == 'WIN')
46    {
47      $dir = str_replace('/', DIRECTORY_SEPARATOR, $dir);
48    }
49    $umask = umask(0);
50    $mkd = @mkdir($dir, $conf['chmod_value'], true);
51    umask($umask);
52    if ($mkd==false)
53    {
54      return false;
55    }
56
57    $file = $dir.'/index.htm';
58    file_exists($file) or @file_put_contents( $file, 'Not allowed!' );
59  }
60  if ( !is_writable($dir) )
61  {
62    return false;
63  }
64  return true;
65}
66
67// end fast bootstrap
68
[12908]69function ilog()
70{
[13021]71  global $conf;
[12908]72  if (!$conf['enable_i_log']) return;
[13021]73
[12958]74  $line = date("c");
[12908]75  foreach( func_get_args() as $arg)
76  {
[12958]77    $line .= ' ';
[12908]78    if (is_array($arg))
79    {
[12958]80      $line .= implode(' ', $arg);
[12908]81    }
82    else
83    {
[12958]84      $line .= $arg;
[12908]85    }
86  }
[13021]87        $file=PHPWG_ROOT_PATH.$conf['data_location'].'tmp/i.log';
88  if (false == file_put_contents($file, $line."\n", FILE_APPEND))
89        {
90                mkgetdir(dirname($file));
91        }
[12908]92}
[12796]93
94function ierror($msg, $code)
95{
96  if ($code==301 || $code==302)
97  {
98    if (ob_get_length () !== FALSE)
99    {
100      ob_clean();
101    }
102    // default url is on html format
103    $url = html_entity_decode($msg);
104    header('Request-URI: '.$url);
105    header('Content-Location: '.$url);
106    header('Location: '.$url);
[16989]107    ilog('WARN', $code, $url, $_SERVER['REQUEST_URI']);
[12796]108    exit;
109  }
110  if ($code>=400)
111  {
112    $protocol = $_SERVER["SERVER_PROTOCOL"];
113    if ( ('HTTP/1.1' != $protocol) && ('HTTP/1.0' != $protocol) )
114      $protocol = 'HTTP/1.0';
115
116    header( "$protocol $code $msg", true, $code );
117  }
118  //todo improve
119  echo $msg;
[15551]120  ilog('ERROR', $code, $msg, $_SERVER['REQUEST_URI']);
[12796]121  exit;
122}
123
[12908]124function time_step( &$step )
125{
126  $tmp = $step;
[12920]127  $step = microtime(true);
[12908]128  return intval(1000*($step - $tmp));
129}
[12796]130
[13021]131function url_to_size($s)
132{
133  $pos = strpos($s, 'x');
134  if ($pos===false)
135  {
136    return array((int)$s, (int)$s);
137  }
138  return array((int)substr($s,0,$pos), (int)substr($s,$pos+1));
139}
140
141function parse_custom_params($tokens)
142{
143  if (count($tokens)<1)
144    ierror('Empty array while parsing Sizing', 400);
145
146  $crop = 0;
147  $min_size = null;
148
149  $token = array_shift($tokens);
150  if ($token[0]=='s')
151  {
152    $size = url_to_size( substr($token,1) );
153  }
154  elseif ($token[0]=='e')
155  {
156    $crop = 1;
157    $size = $min_size = url_to_size( substr($token,1) );
158  }
159  else
160  {
161    $size = url_to_size( $token );
162    if (count($tokens)<2)
163      ierror('Sizing arr', 400);
164
165    $token = array_shift($tokens);
166    $crop = char_to_fraction($token);
167
168    $token = array_shift($tokens);
169    $min_size = url_to_size( $token );
170  }
171  return new DerivativeParams( new SizingParams($size, $crop, $min_size) );
172}
173
[12796]174function parse_request()
175{
176  global $conf, $page;
177
178  if ( $conf['question_mark_in_urls']==false and
179       isset($_SERVER["PATH_INFO"]) and !empty($_SERVER["PATH_INFO"]) )
180  {
181    $req = $_SERVER["PATH_INFO"];
182    $req = str_replace('//', '/', $req);
183    $path_count = count( explode('/', $req) );
184    $page['root_path'] = PHPWG_ROOT_PATH.str_repeat('../', $path_count-1);
185  }
186  else
187  {
188    $req = $_SERVER["QUERY_STRING"];
[12865]189    if ($pos=strpos($req, '&'))
190    {
191      $req = substr($req, 0, $pos);
192    }
[17315]193    $req = rawurldecode($req);
[12796]194    /*foreach (array_keys($_GET) as $keynum => $key)
195    {
196      $req = $key;
197      break;
198    }*/
199    $page['root_path'] = PHPWG_ROOT_PATH;
200  }
201
202  $req = ltrim($req, '/');
203
[13527]204  foreach (preg_split('#/+#', $req) as $token)
205  {
206    preg_match($conf['sync_chars_regex'], $token) or ierror('Invalid chars in request', 400);
207  }
[16989]208
[12796]209  $page['derivative_path'] = PHPWG_ROOT_PATH.PWG_DERIVATIVE_DIR.$req;
210
211  $pos = strrpos($req, '.');
212  $pos!== false || ierror('Missing .', 400);
213  $ext = substr($req, $pos);
214  $page['derivative_ext'] = $ext;
215  $req = substr($req, 0, $pos);
216
217  $pos = strrpos($req, '-');
218  $pos!== false || ierror('Missing -', 400);
219  $deriv = substr($req, $pos+1);
220  $req = substr($req, 0, $pos);
221
222  $deriv = explode('_', $deriv);
223  foreach (ImageStdParams::get_defined_type_map() as $type => $params)
224  {
[12797]225    if ( derivative_to_url($type) == $deriv[0])
[12796]226    {
227      $page['derivative_type'] = $type;
228      $page['derivative_params'] = $params;
229      break;
230    }
231  }
232
233  if (!isset($page['derivative_type']))
234  {
[12797]235    if (derivative_to_url(IMG_CUSTOM) == $deriv[0])
[12796]236    {
237      $page['derivative_type'] = IMG_CUSTOM;
238    }
239    else
240    {
241      ierror('Unknown parsing type', 400);
242    }
243  }
244  array_shift($deriv);
245
246  if ($page['derivative_type'] == IMG_CUSTOM)
247  {
[13021]248    $params = $page['derivative_params'] = parse_custom_params($deriv);
[13651]249    ImageStdParams::apply_global($params);
[13021]250
[12865]251    if ($params->sizing->ideal_size[0] < 20 or $params->sizing->ideal_size[1] < 20)
252    {
253      ierror('Invalid size', 400);
254    }
255    if ($params->sizing->max_crop < 0 or $params->sizing->max_crop > 1)
256    {
257      ierror('Invalid crop', 400);
258    }
[13021]259    $greatest = ImageStdParams::get_by_type(IMG_XXLARGE);
[13038]260
[13021]261    $key = array();
262    $params->add_url_tokens($key);
263    $key = implode('_', $key);
264    if (!isset(ImageStdParams::$custom[$key]))
265    {
266      ierror('Size not allowed', 403);
267    }
[12796]268  }
269
[13038]270  if (is_file(PHPWG_ROOT_PATH.$req.$ext))
271  {
272    $req = './'.$req; // will be used to match #iamges.path
273  }
274  elseif (is_file(PHPWG_ROOT_PATH.'../'.$req.$ext))
275  {
[12796]276    $req = '../'.$req;
[13038]277  }
[12796]278
279  $page['src_location'] = $req.$ext;
280  $page['src_path'] = PHPWG_ROOT_PATH.$page['src_location'];
281  $page['src_url'] = $page['root_path'].$page['src_location'];
282}
283
[13038]284function try_switch_source(DerivativeParams $params, $original_mtime)
285{
286  global $page;
[17229]287  $original_size = null;
288  if (isset($page['original_size']))
289  {
290    $original_size = $page['original_size'];
291    if ($page['rotation_angle']==90 || $page['rotation_angle']==270)
292    {
293      $tmp = $original_size[0];
294      $original_size[0] = $original_size[1];
295      $original_size[1] = $tmp;
296    }
297  }
298
299  $use_watermark = $params->use_watermark;
300  if ($use_watermark)
301  {
302    if (!isset($original_size))
[17230]303      return false; // cannot really know if a watermark is required
[17229]304    $dsize = $params->compute_final_size($original_size);
305    $use_watermark = $params->will_watermark($dsize);
306    ilog($use_watermark, $dsize);
307  }
308
[13038]309  $candidates = array();
310  foreach(ImageStdParams::get_defined_type_map() as $candidate)
311  {
312    if ($candidate->type == $params->type)
313      continue;
[17229]314    if ($candidate->use_watermark != $use_watermark)
[13038]315      continue;
316    if ($candidate->max_width() < $params->max_width() || $candidate->max_height() < $params->max_height())
317      continue;
318    if ($params->sizing->max_crop==0)
319    {
320      if ($candidate->sizing->max_crop!=0)
321        continue;
322    }
323    else
324    {
325      if ($candidate->sizing->max_crop!=0)
326        continue; // this could be optimized
[17229]327      if (!isset($original_size))
[13038]328        continue;
[17229]329      $candidate_size = $candidate->compute_final_size($original_size);
[13038]330      if ($candidate_size[0] < $params->sizing->min_size[0] || $candidate_size[1] < $params->sizing->min_size[1] )
331        continue;
332    }
333    $candidates[] = $candidate;
334  }
[12796]335
[13038]336  foreach( array_reverse($candidates) as $candidate)
337  {
338    $candidate_path = $page['derivative_path'];
339    $candidate_path = str_replace( '-'.derivative_to_url($params->type), '-'.derivative_to_url($candidate->type), $candidate_path);
340    $candidate_mtime = @filemtime($candidate_path);
341    if ($candidate_mtime === false
342      || $candidate_mtime < $original_mtime
343      || $candidate_mtime < $candidate->last_mod_time)
344      continue;
345    $params->use_watermark = false;
346    $params->sharpen = min(1, $params->sharpen);
347    $page['src_path'] = $candidate_path;
348    $page['src_url'] = $page['root_path'] . substr($candidate_path, strlen(PHPWG_ROOT_PATH));
[13848]349    $page['rotation_angle'] = 0;
[17230]350                return true;
[13038]351  }
[17230]352        return false;
[13038]353}
354
[12820]355function send_derivative($expires)
356{
357  global $page;
[13444]358
359  if (isset($_GET['ajaxload']) and $_GET['ajaxload'] == 'true')
360  {
361    include_once(PHPWG_ROOT_PATH.'include/functions_cookie.inc.php');
362    include_once(PHPWG_ROOT_PATH.'include/functions_url.inc.php');
363
[18746]364    echo json_encode( array( 'url'=>embellish_url(get_absolute_root_url().$page['derivative_path']) ) );
[13444]365    return;
366  }
[12820]367  $fp = fopen($page['derivative_path'], 'rb');
[12796]368
[12820]369  $fstat = fstat($fp);
370  header('Last-Modified: '.gmdate('D, d M Y H:i:s', $fstat['mtime']).' GMT');
371  if ($expires!==false)
372  {
373    header('Expires: '.gmdate('D, d M Y H:i:s', $expires).' GMT');
374  }
375  header('Content-length: '.$fstat['size']);
376  header('Connection: close');
377
378  $ctype="application/octet-stream";
379  switch (strtolower($page['derivative_ext']))
380  {
381    case ".jpe": case ".jpeg": case ".jpg": $ctype="image/jpeg"; break;
382    case ".png": $ctype="image/png"; break;
383    case ".gif": $ctype="image/gif"; break;
384  }
385  header("Content-Type: $ctype");
386
387  fpassthru($fp);
388  fclose($fp);
389}
390
[12796]391$page=array();
[12920]392$begin = $step = microtime(true);
[12908]393$timing=array();
394foreach( explode(',','load,rotate,crop,scale,sharpen,watermark,save,send') as $k )
395{
396  $timing[$k] = '';
397}
[12796]398
[13736]399include_once(PHPWG_ROOT_PATH .'include/dblayer/functions_'.$conf['dblayer'].'.inc.php');
[12796]400include_once( PHPWG_ROOT_PATH .'/include/derivative_params.inc.php');
401include_once( PHPWG_ROOT_PATH .'/include/derivative_std_params.inc.php');
402
[13736]403try
404{
405  $pwg_db_link = pwg_db_connect($conf['db_host'], $conf['db_user'],
406                                $conf['db_password'], $conf['db_base']);
407}
408catch (Exception $e)
409{
410  ilog("db error", $e->getMessage());
411}
[17681]412pwg_db_check_charset();
413
[13736]414list($conf['derivatives']) = pwg_db_fetch_row(pwg_query('SELECT value FROM '.$prefixeTable.'config WHERE param=\'derivatives\''));
415ImageStdParams::load_from_db();
[12796]416
417
418parse_request();
419//var_export($page);
420
421$params = $page['derivative_params'];
422
423$src_mtime = @filemtime($page['src_path']);
424if ($src_mtime === false)
425{
426  ierror('Source not found', 404);
427}
428
429$need_generate = false;
430$derivative_mtime = @filemtime($page['derivative_path']);
431if ($derivative_mtime === false or
432    $derivative_mtime < $src_mtime or
433    $derivative_mtime < $params->last_mod_time)
434{
435  $need_generate = true;
436}
437
[12820]438$expires=false;
439$now = time();
[12865]440if ( isset($_GET['b']) )
441{
442  $expires = $now + 100;
443  header("Cache-control: no-store, max-age=100");
444}
445elseif ( $now > (max($src_mtime, $params->last_mod_time) + 24*3600) )
[12820]446{// somehow arbitrary - if derivative params or src didn't change for the last 24 hours, we send an expire header for several days
447  $expires = $now + 10*24*3600;
448}
449
[12796]450if (!$need_generate)
451{
452  if ( isset( $_SERVER['HTTP_IF_MODIFIED_SINCE'] )
453    and strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $derivative_mtime)
454  {// send the last mod time of the file back
455    header('Last-Modified: '.gmdate('D, d M Y H:i:s', $derivative_mtime).' GMT', true, 304);
456    header('Expires: '.gmdate('D, d M Y H:i:s', time()+10*24*3600).' GMT', true, 304);
457    exit;
458  }
[12820]459  send_derivative($expires);
[13444]460  exit;
[12796]461}
462
[13864]463include_once(PHPWG_ROOT_PATH . 'admin/include/image.class.php');
[13038]464$page['coi'] = null;
465if (strpos($page['src_location'], '/pwg_representative/')===false
466    && strpos($page['src_location'], 'themes/')===false
467    && strpos($page['src_location'], 'plugins/')===false)
468{
469  try
470  {
[13843]471    $query = '
[13864]472SELECT *
[13843]473  FROM '.$prefixeTable.'images
474  WHERE path=\''.$page['src_location'].'\'
475;';
[16989]476
[13038]477    if ( ($row=pwg_db_fetch_assoc(pwg_query($query))) )
478    {
479      if (isset($row['width']))
480      {
481        $page['original_size'] = array($row['width'],$row['height']);
482      }
483      $page['coi'] = $row['coi'];
[13843]484
[13864]485      if (!isset($row['rotation']))
[13843]486      {
487        $page['rotation_angle'] = pwg_image::get_rotation_angle($page['src_path']);
[16989]488
[13843]489        single_update(
490          $prefixeTable.'images',
491          array('rotation' => pwg_image::get_rotation_code_from_angle($page['rotation_angle'])),
492          array('id' => $row['id'])
493          );
494      }
495      else
496      {
497        $page['rotation_angle'] = pwg_image::get_rotation_angle_from_code($row['rotation']);
498      }
[13038]499    }
500    if (!$row)
501    {
502      ierror('Db file path not found', 404);
503    }
504  }
505  catch (Exception $e)
506  {
507    ilog("db error", $e->getMessage());
508  }
509}
[15551]510else
511{
512  $page['rotation_angle'] = 0;
513}
[18634]514pwg_db_close($pwg_db_link);
[13038]515
[17230]516if (!try_switch_source($params, $src_mtime) && $params->type==IMG_CUSTOM)
517{
518        $sharpen = 0;
519        foreach (ImageStdParams::get_defined_type_map() as $std_params)
520        {
521                $sharpen += $std_params->sharpen;
522        }
523        $params->sharpen = round($sharpen / count(ImageStdParams::get_defined_type_map()) );
524}
[13038]525
[12865]526if (!mkgetdir(dirname($page['derivative_path'])))
527{
528  ierror("dir create error", 500);
529}
[12796]530
[12851]531ignore_user_abort(true);
[16072]532@set_time_limit(0);
[12851]533
[12796]534$image = new pwg_image($page['src_path']);
[12908]535$timing['load'] = time_step($step);
[12796]536
537$changes = 0;
538
[13843]539// rotate
[15551]540if (0 != $page['rotation_angle'])
[13843]541{
542  $image->rotate($page['rotation_angle']);
[13848]543  $changes++;
544  $timing['rotate'] = time_step($step);
[13843]545}
[12796]546
547// Crop & scale
[12851]548$o_size = $d_size = array($image->get_width(),$image->get_height());
549$params->sizing->compute($o_size , $page['coi'], $crop_rect, $scaled_size );
[12796]550if ($crop_rect)
551{
552  $changes++;
553  $image->crop( $crop_rect->width(), $crop_rect->height(), $crop_rect->l, $crop_rect->t);
[12908]554  $timing['crop'] = time_step($step);
[12796]555}
556
[12851]557if ($scaled_size)
[12796]558{
559  $changes++;
[12851]560  $image->resize( $scaled_size[0], $scaled_size[1] );
561  $d_size = $scaled_size;
[12908]562  $timing['scale'] = time_step($step);
[12796]563}
564
[12851]565if ($params->sharpen)
566{
567  $changes += $image->sharpen( $params->sharpen );
[12908]568  $timing['sharpen'] = time_step($step);
[12851]569}
570
[16989]571if ($params->will_watermark($d_size))
[12851]572{
573  $wm = ImageStdParams::get_watermark();
574  $wm_image = new pwg_image(PHPWG_ROOT_PATH.$wm->file);
575  $wm_size = array($wm_image->get_width(),$wm_image->get_height());
576  if ($d_size[0]<$wm_size[0] or $d_size[1]<$wm_size[1])
577  {
578    $wm_scaling_params = SizingParams::classic($d_size[0], $d_size[1]);
579    $wm_scaling_params->compute($wm_size, null, $tmp, $wm_scaled_size);
580    $wm_size = $wm_scaled_size;
581    $wm_image->resize( $wm_scaled_size[0], $wm_scaled_size[1] );
582  }
583  $x = round( ($wm->xpos/100)*($d_size[0]-$wm_size[0]) );
584  $y = round( ($wm->ypos/100)*($d_size[1]-$wm_size[1]) );
585  if ($image->compose($wm_image, $x, $y, $wm->opacity))
586  {
587    $changes++;
588    if ($wm->xrepeat)
589    {
590      // todo
[12865]591      $pad = $wm_size[0] + max(30, round($wm_size[0]/4));
592      for($i=-$wm->xrepeat; $i<=$wm->xrepeat; $i++)
593      {
594        if (!$i) continue;
595        $x2 = $x + $i * $pad;
596        if ($x2>=0 && $x2+$wm_size[0]<$d_size[0])
597          if (!$image->compose($wm_image, $x2, $y, $wm->opacity))
598            break;
599      }
[12851]600    }
601  }
602  $wm_image->destroy();
[12908]603  $timing['watermark'] = time_step($step);
[12851]604}
605
[12796]606// no change required - redirect to source
607if (!$changes)
608{
609  header("X-i: No change");
610  ierror( $page['src_url'], 301);
611}
612
[13426]613if ($d_size[0]*$d_size[1] < 256000)
[12958]614{// strip metadata for small images
615  $image->strip();
616}
[13843]617
[14649]618$image->set_compression_quality( ImageStdParams::$quality );
[12796]619$image->write( $page['derivative_path'] );
620$image->destroy();
[17675]621@chmod($page['derivative_path'], 0644);
[12908]622$timing['save'] = time_step($step);
[12796]623
[12820]624send_derivative($expires);
[12908]625$timing['send'] = time_step($step);
626
627ilog('perf',
628  basename($page['src_path']), $o_size, $o_size[0]*$o_size[1],
629  basename($page['derivative_path']), $d_size, $d_size[0]*$d_size[1],
[12920]630  function_exists('memory_get_peak_usage') ? round( memory_get_peak_usage()/(1024*1024), 1) : '',
[12908]631  time_step($begin),
[12920]632  '|', $timing);
[12796]633?>
Note: See TracBrowser for help on using the repository browser.