source: trunk/i.php @ 20370

Last change on this file since 20370 was 19703, checked in by plg, 11 years ago

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

File size: 16.9 KB
Line 
1<?php
2// +-----------------------------------------------------------------------+
3// | Piwigo - a PHP based photo gallery                                    |
4// +-----------------------------------------------------------------------+
5// | Copyright(C) 2008-2013 Piwigo Team                  http://piwigo.org |
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
22define('PHPWG_ROOT_PATH','./');
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/');
29defined('PWG_DERIVATIVE_DIR') or define('PWG_DERIVATIVE_DIR', $conf['data_location'].'i/');
30
31@include(PHPWG_ROOT_PATH.PWG_LOCAL_DIR .'config/database.inc.php');
32
33
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
69function ilog()
70{
71  global $conf;
72  if (!$conf['enable_i_log']) return;
73
74  $line = date("c");
75  foreach( func_get_args() as $arg)
76  {
77    $line .= ' ';
78    if (is_array($arg))
79    {
80      $line .= implode(' ', $arg);
81    }
82    else
83    {
84      $line .= $arg;
85    }
86  }
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        }
92}
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);
107    ilog('WARN', $code, $url, $_SERVER['REQUEST_URI']);
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;
120  ilog('ERROR', $code, $msg, $_SERVER['REQUEST_URI']);
121  exit;
122}
123
124function time_step( &$step )
125{
126  $tmp = $step;
127  $step = microtime(true);
128  return intval(1000*($step - $tmp));
129}
130
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
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"];
189    if ($pos=strpos($req, '&'))
190    {
191      $req = substr($req, 0, $pos);
192    }
193    $req = rawurldecode($req);
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
204  foreach (preg_split('#/+#', $req) as $token)
205  {
206    preg_match($conf['sync_chars_regex'], $token) or ierror('Invalid chars in request', 400);
207  }
208
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  {
225    if ( derivative_to_url($type) == $deriv[0])
226    {
227      $page['derivative_type'] = $type;
228      $page['derivative_params'] = $params;
229      break;
230    }
231  }
232
233  if (!isset($page['derivative_type']))
234  {
235    if (derivative_to_url(IMG_CUSTOM) == $deriv[0])
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  {
248    $params = $page['derivative_params'] = parse_custom_params($deriv);
249    ImageStdParams::apply_global($params);
250
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    }
259    $greatest = ImageStdParams::get_by_type(IMG_XXLARGE);
260
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    }
268  }
269
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  {
276    $req = '../'.$req;
277  }
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
284function try_switch_source(DerivativeParams $params, $original_mtime)
285{
286  global $page;
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))
303      return false; // cannot really know if a watermark is required
304    $dsize = $params->compute_final_size($original_size);
305    $use_watermark = $params->will_watermark($dsize);
306    ilog($use_watermark, $dsize);
307  }
308
309  $candidates = array();
310  foreach(ImageStdParams::get_defined_type_map() as $candidate)
311  {
312    if ($candidate->type == $params->type)
313      continue;
314    if ($candidate->use_watermark != $use_watermark)
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
327      if (!isset($original_size))
328        continue;
329      $candidate_size = $candidate->compute_final_size($original_size);
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  }
335
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));
349    $page['rotation_angle'] = 0;
350                return true;
351  }
352        return false;
353}
354
355function send_derivative($expires)
356{
357  global $page;
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
364    echo json_encode( array( 'url'=>embellish_url(get_absolute_root_url().$page['derivative_path']) ) );
365    return;
366  }
367  $fp = fopen($page['derivative_path'], 'rb');
368
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
391$page=array();
392$begin = $step = microtime(true);
393$timing=array();
394foreach( explode(',','load,rotate,crop,scale,sharpen,watermark,save,send') as $k )
395{
396  $timing[$k] = '';
397}
398
399include_once(PHPWG_ROOT_PATH .'include/dblayer/functions_'.$conf['dblayer'].'.inc.php');
400include_once( PHPWG_ROOT_PATH .'/include/derivative_params.inc.php');
401include_once( PHPWG_ROOT_PATH .'/include/derivative_std_params.inc.php');
402
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}
412pwg_db_check_charset();
413
414list($conf['derivatives']) = pwg_db_fetch_row(pwg_query('SELECT value FROM '.$prefixeTable.'config WHERE param=\'derivatives\''));
415ImageStdParams::load_from_db();
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
438$expires=false;
439$now = time();
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) )
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
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  }
459  send_derivative($expires);
460  exit;
461}
462
463include_once(PHPWG_ROOT_PATH . 'admin/include/image.class.php');
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  {
471    $query = '
472SELECT *
473  FROM '.$prefixeTable.'images
474  WHERE path=\''.$page['src_location'].'\'
475;';
476
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'];
484
485      if (!isset($row['rotation']))
486      {
487        $page['rotation_angle'] = pwg_image::get_rotation_angle($page['src_path']);
488
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      }
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}
510else
511{
512  $page['rotation_angle'] = 0;
513}
514pwg_db_close($pwg_db_link);
515
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}
525
526if (!mkgetdir(dirname($page['derivative_path'])))
527{
528  ierror("dir create error", 500);
529}
530
531ignore_user_abort(true);
532@set_time_limit(0);
533
534$image = new pwg_image($page['src_path']);
535$timing['load'] = time_step($step);
536
537$changes = 0;
538
539// rotate
540if (0 != $page['rotation_angle'])
541{
542  $image->rotate($page['rotation_angle']);
543  $changes++;
544  $timing['rotate'] = time_step($step);
545}
546
547// Crop & scale
548$o_size = $d_size = array($image->get_width(),$image->get_height());
549$params->sizing->compute($o_size , $page['coi'], $crop_rect, $scaled_size );
550if ($crop_rect)
551{
552  $changes++;
553  $image->crop( $crop_rect->width(), $crop_rect->height(), $crop_rect->l, $crop_rect->t);
554  $timing['crop'] = time_step($step);
555}
556
557if ($scaled_size)
558{
559  $changes++;
560  $image->resize( $scaled_size[0], $scaled_size[1] );
561  $d_size = $scaled_size;
562  $timing['scale'] = time_step($step);
563}
564
565if ($params->sharpen)
566{
567  $changes += $image->sharpen( $params->sharpen );
568  $timing['sharpen'] = time_step($step);
569}
570
571if ($params->will_watermark($d_size))
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
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      }
600    }
601  }
602  $wm_image->destroy();
603  $timing['watermark'] = time_step($step);
604}
605
606// no change required - redirect to source
607if (!$changes)
608{
609  header("X-i: No change");
610  ierror( $page['src_url'], 301);
611}
612
613if ($d_size[0]*$d_size[1] < 256000)
614{// strip metadata for small images
615  $image->strip();
616}
617
618$image->set_compression_quality( ImageStdParams::$quality );
619$image->write( $page['derivative_path'] );
620$image->destroy();
621@chmod($page['derivative_path'], 0644);
622$timing['save'] = time_step($step);
623
624send_derivative($expires);
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],
630  function_exists('memory_get_peak_usage') ? round( memory_get_peak_usage()/(1024*1024), 1) : '',
631  time_step($begin),
632  '|', $timing);
633?>
Note: See TracBrowser for help on using the repository browser.