aboutsummaryrefslogblamecommitdiffstats
path: root/Zotlabs/Module/Photo.php
blob: 30670c3291f376529c44069e0608d20553527c26 (plain) (tree)
1
2
3
4
5
6
7
8
     

 


                                     
                                   
                                               



                                             
 

                                  
                            
                               
 













                                                  

                                    
                                      


                                       


                                                           
                                                      
                                   
 





                                                                                                                                                         


                                                               

                                                 

                                                                
                                                                                         


                                                                
                                                                                         








                                                                

                                   
 





                                                                                                                   

                                                                         
                                                           
                                                                                        

                                                                     
                         
                                
                                     







                                                                                                                                             
                                            
                         
 
                                     



                                                                                                           
                         
 
                                                            






                                       
                                                                                                                   




                                                                                                            
 
                                                                                                        





                                                                               
                                                           












                                                                                                               
                                                    

                         
                                                                                                                                     



                                                   
                                                
 

                                                                  
                                                     
                                                               

                                                                                      
                                                                

                                                                                   
                                                                
                                                                 




                                                                                  
                                                                                             







                                                                                                                                                

                                                 
                                 
 
                                                     
                                                                                                        
 

                                                                       
                                                                           
                                                                                                                           


                                                           
                                
                                                                
                        
                                                         
                                                                                     
                                                                             
                                                                      
                                                                      
                                                                                     

                                                                         
                                                                   
                                         
                                                                                                                                                      
                                                                                                         

                                      
                                                        



                                                                                  
                                         
 
                                 

                            
                                                                  
                 


                                 




                                               
 
                                        
 
                                                                                                                                                     


                                                       
                                                             
                 
 







                                                               
 
                                             




                                                                                      
                                                                     











                                                                                              
                                                     
 





                                                                                                                    
                                                                                    

                 
 
                                                     
                                                                                         
                                         
                                                                                            
 
                                                       















                                                                                                                 
                         


         
<?php


namespace Zotlabs\Module;

require_once('include/security.php');
require_once('include/attach.php');
require_once('include/photo/photo_driver.php');

class Photo extends \Zotlabs\Web\Controller {

	function init() {

		$streaming = null;
		$channel = null;
		$person = 0;
		$renew = false;

		switch(argc()) {
			case 4:
				$person = argv(3);
				$res    = argv(2);
				$type   = argv(1);
				break;
			case 2:
				$photo = argv(1);
				break;
			case 1:
			default:
				killme();
				// NOTREACHED
		}

		$cache_mode = array(
			'on' => false,
			'age' => 86400,
			'exp' => true,
			'leak' => false
		);
		call_hooks('cache_mode_hook', $cache_mode);
		
		$observer_xchan = get_observer_hash();
		$cachecontrol = '';

		if(isset($type)) {
	
			/**
			 * Profile photos - Access controls on default profile photos are not honoured since they need to be exchanged with remote sites.
			 * 
			 */
			 
			$default = get_default_profile_photo();
			 
			if($type === 'profile') {
				switch($res) {
					case 'm':
						$resolution = 5;
						$default = get_default_profile_photo(80);
						break;
					case 's':
						$resolution = 6;
						$default = get_default_profile_photo(48);
						break;
					case 'l':
					default:
						$resolution = 4;
						break;
				}
			}
	
			$uid = $person;
			
			$data = '';

			$r = q("SELECT * FROM photo WHERE imgscale = %d AND uid = %d AND photo_usage = %d LIMIT 1",
				intval($resolution),
				intval($uid),
				intval(PHOTO_PROFILE)
			);
			if($r) {
			    $modified = strtotime($r[0]['edited'] . "Z");
			    $mimetype = $r[0]['mimetype'];
			    if(intval($r[0]['os_storage']))
			        $data = file_get_contents(dbunescbin($r[0]['content']));
			    else
			        $data = dbunescbin($r[0]['content']);
			}
				
			if(! $data) {
			    $d = [ 'imgscale' => $resolution, 'channel_id' => $uid, 'default' => $default, 'data'  => '', 'mimetype' => '' ];
			    call_hooks('get_profile_photo',$d);
			    
			    $resolution = $d['imgscale'];
			    $uid        = $d['channel_id']; 	
			    $default    = $d['default'];
			    $data       = $d['data'];
			    $mimetype   = $d['mimetype'];
			    $modified   = 0;
			}

			if(! $data) {
			    $x = z_fetch_url(z_root() . '/' . $default, true, 0, [ 'novalidate' => true ]);
			    $data = ($x['success'] ? $x['body'] : EMPTY_STR);
			    $mimetype = 'image/png';
			    $modified = filemtime($default);
			}

			$cachecontrol = ', must-revalidate';
		}
		else {
	
			/**
			 * Other photos
			 */
	
			/* Check for a cookie to indicate display pixel density, in order to detect high-resolution
			   displays. This procedure was derived from the "Retina Images" by Jeremey Worboys,
			   used in accordance with the Creative Commons Attribution 3.0 Unported License.
			   Project link: https://github.com/Retina-Images/Retina-Images
			   License link: http://creativecommons.org/licenses/by/3.0/
			*/

			// @FIXME It seems this part doesn't work because we are not setting such cookie
			$cookie_value = false;
			if (isset($_COOKIE['devicePixelRatio'])) {
			  $cookie_value = intval($_COOKIE['devicePixelRatio']);
			}
			else {
			  // Force revalidation of cache on next request
			  // $prvcachecontrol = 'no-cache';
			  $status = 'no cookie';
			}
	
			$resolution = 0;
	
			if(strpos($photo,'.') !== false)
				$photo = substr($photo,0,strpos($photo,'.'));
		
			if(substr($photo,-2,1) == '-') {
				$resolution = intval(substr($photo,-1,1));
				$photo = substr($photo,0,-2);
				// If viewing on a high-res screen, attempt to serve a higher resolution image:
				if ($resolution == 2 && ($cookie_value > 1))
				    $resolution = 1;
			}
			
			$r = q("SELECT uid, photo_usage, display_path FROM photo WHERE resource_id = '%s' AND imgscale = %d LIMIT 1",
				dbesc($photo),
				intval($resolution)
			);
			if($r) {
				$allowed = (-1);

				$u = intval($r[0]['photo_usage']);
				if($u) {
					$allowed = 1;
					if($u === PHOTO_COVER) 
						if($resolution < PHOTO_RES_COVER_1200)
							$allowed = (-1);
					if($u === PHOTO_PROFILE)
						if(! in_array($resolution,[4,5,6]))
							$allowed = (-1);
					if($u === PHOTO_CACHE) {
						// Validate cache
						if($cache_mode['on']) {
							$cache = array(
								'resid' => $photo,
								'status' => false
							);
							call_hooks('cache_url_hook', $cache);
							if(! $cache['status']) {
								$url = htmlspecialchars_decode($r[0]['display_path']);
								// SSLify if needed
								if(strpos(z_root(),'https:') !== false && strpos($url,'https:') === false)
									$url = z_root() . '/sslify/' . $filename . '?f=&url=' . urlencode($url);
								header("Location: " . $url);
								killme();
							}
						}
					}
				}

				if($allowed === (-1))
					$allowed = attach_can_view($r[0]['uid'],$observer_xchan,$photo);

				$channel = channelx_by_n($r[0]['uid']);

				// Now we'll see if we can access the photo
				$e = q("SELECT * FROM photo WHERE resource_id = '%s' AND imgscale = %d $sql_extra LIMIT 1",
					dbesc($photo),
					intval($resolution)
				);
				
				$exists = (($e) ? true : false);
			
				if($exists && $allowed) {
					$expires = strtotime($e[0]['expires'] . 'Z');
					$data = dbunescbin($e[0]['content']);
					$filesize = $e[0]['filesize'];
					$mimetype = $e[0]['mimetype'];
					$modified = strtotime($e[0]['edited'] . 'Z');

					if(intval($e[0]['os_storage'])) {
						$streaming = $data;
					}
					if($e[0]['allow_cid'] != '' || $e[0]['allow_gid'] != '' || $e[0]['deny_gid'] != '' || $e[0]['deny_gid'] != '')
						$prvcachecontrol = 'no-store, no-cache, must-revalidate';
				}
				else {
					if(! $allowed) {
						http_status_exit(403,'forbidden');
					}
					if(! $exists) {
						http_status_exit(404,'not found');
					}

				}
			} 
			else
				http_status_exit(404,'not found');
		}

 		if(! $data)
 			killme();
 			
 		$etag = md5($data . $modified);
 		
 		if($modified == 0)
 		    $modified = time();

		header_remove('Pragma');

		if($_SERVER['HTTP_IF_NONE_MATCH'] === $etag || $_SERVER['HTTP_IF_MODIFIED_SINCE'] === gmdate("D, d M Y H:i:s", $modified) . " GMT") {
			header_remove('Expires');
			header_remove('Cache-Control');
			header_remove('Set-Cookie');
			http_status_exit(304,'not modified');
		}

		if(isset($res) && intval($res) && $res < 500) {
			$ph = photo_factory($data, $mimetype);
			if($ph->is_valid()) {
				$ph->scaleImageSquare($res);
				$data = $ph->imageString();
				$mimetype = $ph->getType();
			}
		}

		if(isset($prvcachecontrol)) {
	
			// it is a private photo that they have no permission to view.
			// tell the browser not to cache it, in case they authenticate
			// and subsequently have permission to see it
	
			header("Cache-Control: " . $prvcachecontrol);
	
		}
		else {
			// The photo cache default is 1 day to provide a privacy trade-off,
			// as somebody reducing photo permissions on a photo that is already 
			// "in the wild" won't be able to stop the photo from being viewed
			// for this amount amount of time once it is in the browser cache.
			// The privacy expectations of your site members and their perception 
			// of privacy where it affects the entire project may be affected.
			// This has performance considerations but we highly recommend you 
			// leave it alone. 
	
			$maxage = $cache_mode['age'];

			if($cache_mode['exp'] || (! isset($expires)) || (isset($expires) && $expires - 60 < time()))
				$expires = time() + $maxage;
			else
				$maxage = $expires - time();
			
		 	header("Expires: " . gmdate("D, d M Y H:i:s", $expires) . " GMT");
			header("Cache-Control: max-age=" . $maxage . $cachecontrol);
	
		}

		header("Content-type: " . $mimetype);
		header("Last-Modified: " . gmdate("D, d M Y H:i:s", $modified) . " GMT");
		header("ETag: " . $etag);
		header("Content-Length: " . (isset($filesize) ? $filesize : strlen($data)));

		// If it's a file resource, stream it. 
		if($streaming && $channel) {
			if(strpos($streaming,'store') !== false)
				$istream = fopen($streaming,'rb');
			else
				$istream = fopen('store/' . $channel['channel_address'] . '/' . $streaming,'rb');
			$ostream = fopen('php://output','wb');
			if($istream && $ostream) {
				pipe_streams($istream,$ostream);
				fclose($istream);
				fclose($ostream);
			}
		}
		else {
			echo $data;
		}

		killme();
	}
	
}