From 7cc8c369cb6a01cffb58215966844b53794f32f4 Mon Sep 17 00:00:00 2001
From: Fabio Comuni <fabrix.xm@gmail.com>
Date: Thu, 7 Jun 2012 17:42:13 +0200
Subject: [PATCH 1/2] Add support for PNG images with alpha

---
 boot.php              |   7 ++-
 include/Photo.php     | 127 ++++++++++++++++++++++++++++++++++++------
 include/items.php     |  12 ++--
 include/network.php   |   5 +-
 include/user.php      |   8 ++-
 mod/parse_url.php     |   5 +-
 mod/photo.php         |  18 ++++--
 mod/photos.php        |  51 ++++++++++-------
 mod/profile_photo.php |  10 ++--
 mod/wall_upload.php   |  34 +++++------
 update.php            |   8 ++-
 11 files changed, 215 insertions(+), 70 deletions(-)

diff --git a/boot.php b/boot.php
index 7961efc4a8..0e8f017f46 100644
--- a/boot.php
+++ b/boot.php
@@ -11,7 +11,7 @@ require_once('include/cache.php');
 define ( 'FRIENDICA_PLATFORM',     'Friendica');
 define ( 'FRIENDICA_VERSION',      '3.0.1366' );
 define ( 'DFRN_PROTOCOL_VERSION',  '2.23'    );
-define ( 'DB_UPDATE_VERSION',      1148      );
+define ( 'DB_UPDATE_VERSION',      1149      );
 
 define ( 'EOL',                    "<br />\r\n"     );
 define ( 'ATOM_TIME',              'Y-m-d\TH:i:s\Z' );
@@ -28,6 +28,11 @@ define ( 'ATOM_TIME',              'Y-m-d\TH:i:s\Z' );
  */
 
 define ( 'JPEG_QUALITY',            100  );
+/**
+ * $a->config['system']['png_quality'] from 0 (uncompressed) to 9
+ */
+define ( 'PNG_QUALITY',             8  );
+
 
 /**
  * Not yet used
diff --git a/include/Photo.php b/include/Photo.php
index fce5599996..ffefc37eca 100644
--- a/include/Photo.php
+++ b/include/Photo.php
@@ -7,14 +7,34 @@ class Photo {
 	private $width;
 	private $height;
 	private $valid;
+	private $type;
+	private $types;
 
-	public function __construct($data) {
+	/**
+	 * supported mimetypes and corresponding file extensions
+	 */
+	static function supportedTypes() {
+		$t = array();
+		$t['image/jpeg'] ='jpg';
+		if (imagetypes() & IMG_PNG) $t['image/png'] = 'png';
+		return $t;
+	}
+
+	public function __construct($data, $type="image/jpeg") {
+
+		$this->types = $this->supportedTypes();
+		if (!array_key_exists($type,$this->types)){
+			$type='image/jpeg';
+		}
 		$this->valid = false;
+		$this->type = $type;
 		$this->image = @imagecreatefromstring($data);
 		if($this->image !== FALSE) {
 			$this->width  = imagesx($this->image);
 			$this->height = imagesy($this->image);
 			$this->valid  = true;
+			imagealphablending($this->image, false);
+			imagesavealpha($this->image, true);
 		}
 	}
 
@@ -38,6 +58,13 @@ class Photo {
 	public function getImage() {
 		return $this->image;
 	}
+	
+	public function getType() {
+		return $this->type;
+	}
+	public function getExt() {
+		return $this->types[$this->type];
+	}
 
 	public function scaleImage($max) {
 
@@ -78,6 +105,9 @@ class Photo {
 
 
 		$dest = imagecreatetruecolor( $dest_width, $dest_height );
+		imagealphablending($dest->image, false);
+		imagesavealpha($dest, true);
+		if ($this->type=='image/png') imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127));; // fill with alpha
 		imagecopyresampled($dest, $this->image, 0, 0, 0, 0, $dest_width, $dest_height, $width, $height);
 		if($this->image)
 			imagedestroy($this->image);
@@ -134,6 +164,9 @@ class Photo {
 
 
 		$dest = imagecreatetruecolor( $dest_width, $dest_height );
+		imagealphablending($dest->image, false);
+		imagesavealpha($dest, true);
+		if ($this->type=='image/png') imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127));; // fill with alpha
 		imagecopyresampled($dest, $this->image, 0, 0, 0, 0, $dest_width, $dest_height, $width, $height);
 		if($this->image)
 			imagedestroy($this->image);
@@ -148,6 +181,9 @@ class Photo {
 	public function scaleImageSquare($dim) {
 
 		$dest = imagecreatetruecolor( $dim, $dim );
+		imagealphablending($dest->image, false);
+		imagesavealpha($dest, true);
+		if ($this->type=='image/png') imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127));; // fill with alpha
 		imagecopyresampled($dest, $this->image, 0, 0, 0, 0, $dim, $dim, $this->width, $this->height);
 		if($this->image)
 			imagedestroy($this->image);
@@ -159,6 +195,9 @@ class Photo {
 
 	public function cropImage($max,$x,$y,$w,$h) {
 		$dest = imagecreatetruecolor( $max, $max );
+		imagealphablending($dest->image, false);
+		imagesavealpha($dest, true);
+		if ($this->type=='image/png') imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127));; // fill with alpha
 		imagecopyresampled($dest, $this->image, 0, 0, $x, $y, $max, $max, $w, $h);
 		if($this->image)
 			imagedestroy($this->image);
@@ -168,20 +207,38 @@ class Photo {
 	}
 
 	public function saveImage($path) {
-		$quality = get_config('system','jpeg_quality');
-		if((! $quality) || ($quality > 100))
-			$quality = JPEG_QUALITY;
-		imagejpeg($this->image,$path,$quality);
+		switch($this->type){
+			case "image/png":
+				$quality = get_config('system','png_quality');
+				if((! $quality) || ($quality > 9))
+					$quality = PNG_QUALITY;
+				imagepng($this->image, $path, $quality);
+				break;
+			default:
+				$quality = get_config('system','jpeg_quality');
+				if((! $quality) || ($quality > 100))
+					$quality = JPEG_QUALITY;
+				imagejpeg($this->image,$path,$quality);
+		}
+		
 	}
 
 	public function imageString() {
 		ob_start();
+		switch($this->type){
+			case "image/png":
+				$quality = get_config('system','png_quality');
+				if((! $quality) || ($quality > 9))
+					$quality = PNG_QUALITY;
+				imagepng($this->image,NULL, $quality);
+				break;
+			default:
+				$quality = get_config('system','jpeg_quality');
+				if((! $quality) || ($quality > 100))
+					$quality = JPEG_QUALITY;
 
-		$quality = get_config('system','jpeg_quality');
-		if((! $quality) || ($quality > 100))
-			$quality = JPEG_QUALITY;
-
-		imagejpeg($this->image,NULL,$quality);
+				imagejpeg($this->image,NULL,$quality);
+		}
 		$s = ob_get_contents();
 		ob_end_clean();
 		return $s;
@@ -200,8 +257,8 @@ class Photo {
 			$guid = get_guid();
 
 		$r = q("INSERT INTO `photo`
-			( `uid`, `contact-id`, `guid`, `resource-id`, `created`, `edited`, `filename`, `album`, `height`, `width`, `data`, `scale`, `profile`, `allow_cid`, `allow_gid`, `deny_cid`, `deny_gid` )
-			VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', %d, %d, '%s', '%s', '%s', '%s' )",
+			( `uid`, `contact-id`, `guid`, `resource-id`, `created`, `edited`, `filename`, type, `album`, `height`, `width`, `data`, `scale`, `profile`, `allow_cid`, `allow_gid`, `deny_cid`, `deny_gid` )
+			VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', %d, %d, '%s', '%s', '%s', '%s' )",
 			intval($uid),
 			intval($cid),
 			dbesc($guid),
@@ -209,6 +266,7 @@ class Photo {
 			dbesc(datetime_convert()),
 			dbesc(datetime_convert()),
 			dbesc(basename($filename)),
+			dbesc($this->type),
 			dbesc($album),
 			intval($this->height),
 			intval($this->width),
@@ -230,6 +288,38 @@ class Photo {
 }}
 
 
+/**
+ * Guess image mimetype from filename or from Content-Type header
+ * 
+ * @arg $filename string Image filename
+ * @arg $fromcurl boolean Check Content-Type header from curl request
+ */
+function guess_image_type($filename, $fromcurl=false) {
+	$type = null;
+	if ($fromcurl) {
+		$a = get_app(); 
+		$headers=array();
+		$h = explode("\n",$a->get_curl_headers());
+		foreach ($h as $l) {
+			list($k,$v) = array_map("trim", explode(":", trim($l), 2));
+			$headers[$k] = $v;
+		}
+		if (array_key_exists('Content-Type', $headers))
+			$type = $headers['Content-Type'];
+	}
+	if (is_null($type)){
+		$ext = pathinfo($filename, PATHINFO_EXTENSION);
+		$types = Photo::supportedTypes();
+		foreach ($types as $m=>$e){
+			if ($ext==$e) return $m;
+		}
+		$type = "image/jpeg";
+	}
+
+	return $type;
+	
+}
+
 function import_profile_photo($photo,$uid,$cid) {
 
 	$a = get_app();
@@ -238,7 +328,12 @@ function import_profile_photo($photo,$uid,$cid) {
 
 	$filename = basename($photo);
 	$img_str = fetch_url($photo,true);
-	$img = new Photo($img_str);
+	
+	// guess mimetype from headers or filename
+	$type = guess_image_type($photo,true);
+
+	
+	$img = new Photo($img_str, $type);
 	if($img->is_valid()) {
 
 		$img->scaleImageSquare(175);
@@ -266,9 +361,9 @@ function import_profile_photo($photo,$uid,$cid) {
 
 
 
-		$photo = $a->get_baseurl() . '/photo/' . $hash . '-4.jpg';
-		$thumb = $a->get_baseurl() . '/photo/' . $hash . '-5.jpg';
-		$micro = $a->get_baseurl() . '/photo/' . $hash . '-6.jpg';
+		$photo = $a->get_baseurl() . '/photo/' . $hash . '-4.' . $img->getExt();
+		$thumb = $a->get_baseurl() . '/photo/' . $hash . '-5.' . $img->getExt();
+		$micro = $a->get_baseurl() . '/photo/' . $hash . '-6.' . $img->getExt();
 	}
 	else
 		$photo_failure = true;
diff --git a/include/items.php b/include/items.php
index 66e219ddf2..15c043e922 100755
--- a/include/items.php
+++ b/include/items.php
@@ -1346,7 +1346,11 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $pass = 0)
 		}
 			
 		$img_str = fetch_url($photo_url,true);
-		$img = new Photo($img_str);
+		// guess mimetype from headers or filename
+		$type = guess_image_type($photo_url,true);
+		
+		
+		$img = new Photo($img_str, $type);
 		if($img->is_valid()) {
 			if($have_photo) {
 				q("DELETE FROM `photo` WHERE `resource-id` = '%s' AND `contact-id` = %d AND `uid` = %d",
@@ -1372,9 +1376,9 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $pass = 0)
 			q("UPDATE `contact` SET `avatar-date` = '%s', `photo` = '%s', `thumb` = '%s', `micro` = '%s'  
 				WHERE `uid` = %d AND `id` = %d LIMIT 1",
 				dbesc(datetime_convert()),
-				dbesc($a->get_baseurl() . '/photo/' . $hash . '-4.jpg'),
-				dbesc($a->get_baseurl() . '/photo/' . $hash . '-5.jpg'),
-				dbesc($a->get_baseurl() . '/photo/' . $hash . '-6.jpg'),
+				dbesc($a->get_baseurl() . '/photo/' . $hash . '-4.'.$img->getExt()),
+				dbesc($a->get_baseurl() . '/photo/' . $hash . '-5.'.$img->getExt()),
+				dbesc($a->get_baseurl() . '/photo/' . $hash . '-6.'.$img->getExt()),
 				intval($contact['uid']),
 				intval($contact['id'])
 			);
diff --git a/include/network.php b/include/network.php
index eeb2460d14..446413cd83 100644
--- a/include/network.php
+++ b/include/network.php
@@ -807,8 +807,11 @@ function scale_external_images($s,$include_link = true) {
 			if(stristr($mtch[1],$hostname))
 				continue;
 			$i = fetch_url($mtch[1]);
+			// guess mimetype from headers or filename
+			$type = guess_image_type($mtch[1],true);
+			
 			if($i) {
-				$ph = new Photo($i);
+				$ph = new Photo($i, $type);
 				if($ph->is_valid()) {
 					$orig_width = $ph->getWidth();
 					$orig_height = $ph->getHeight();
diff --git a/include/user.php b/include/user.php
index af43a2b52c..2477438bf4 100644
--- a/include/user.php
+++ b/include/user.php
@@ -284,7 +284,11 @@ function create_user($arr) {
 
 		$filename = basename($photo);
 		$img_str = fetch_url($photo,true);
-		$img = new Photo($img_str);
+		// guess mimetype from headers or filename
+		$type = guess_image_type($photo,true);
+
+		
+		$img = new Photo($img_str, $type);
 		if($img->is_valid()) {
 
 			$img->scaleImageSquare(175);
@@ -324,4 +328,4 @@ function create_user($arr) {
 	$result['user'] = $u;
 	return $result;
 
-}
\ No newline at end of file
+}
diff --git a/mod/parse_url.php b/mod/parse_url.php
index 27dac4d5d3..cdf2223a86 100644
--- a/mod/parse_url.php
+++ b/mod/parse_url.php
@@ -215,8 +215,11 @@ function parse_url_content(&$a) {
 					
 					$i = fetch_url($image);
 					if($i) {
+						// guess mimetype from headers or filename
+						$type = guess_image_type($image,true);
+						
 						require_once('include/Photo.php');
-						$ph = new Photo($i);
+						$ph = new Photo($i, $type);
 						if($ph->is_valid()) {
 							if($ph->getWidth() > 300 || $ph->getHeight() > 300) {
 								$ph->scaleImage(300);
diff --git a/mod/photo.php b/mod/photo.php
index 3cd8250a9e..e8e74cfde1 100644
--- a/mod/photo.php
+++ b/mod/photo.php
@@ -1,6 +1,7 @@
 <?php
 
 require_once('include/security.php');
+require_once('include/Photo.php');
 
 function photo_init(&$a) {
 
@@ -83,9 +84,11 @@ function photo_init(&$a) {
 		);
 		if(count($r)) {
 			$data = $r[0]['data'];
+			$mimetype = $r[0]['type'];
 		}
 		if(! isset($data)) {
 			$data = file_get_contents($default);
+			$mimetype = 'image/jpeg';
 		}
 	}
 	else {
@@ -95,7 +98,9 @@ function photo_init(&$a) {
 		 */
 
 		$resolution = 0;
-		$photo = str_replace('.jpg','',$photo);
+		foreach( Photo::supportedTypes() as $m=>$e){
+			$photo = str_replace(".$e",'',$photo);
+		}
 	
 		if(substr($photo,-2,1) == '-') {
 			$resolution = intval(substr($photo,-1,1));
@@ -119,6 +124,7 @@ function photo_init(&$a) {
 
 			if(count($r)) {
 				$data = $r[0]['data'];
+				$mimetype = $r[0]['type'];
 			}
 			else {
 
@@ -136,6 +142,7 @@ function photo_init(&$a) {
 				);
 				if(count($r)) {
 					$data = file_get_contents('images/nosign.jpg');
+					$mimetype = 'image/jpeg';
 					$prvcachecontrol = true;
 				}
 			}
@@ -148,12 +155,15 @@ function photo_init(&$a) {
 
 				case 4:
 					$data = file_get_contents('images/person-175.jpg');
+					$mimetype = 'image/jpeg';
 					break;
 				case 5:
 					$data = file_get_contents('images/person-80.jpg');
+					$mimetype = 'image/jpeg';
 					break;
 				case 6:
 					$data = file_get_contents('images/person-48.jpg');
+					$mimetype = 'image/jpeg';
 					break;
 				default:
 					killme();
@@ -164,11 +174,11 @@ function photo_init(&$a) {
 	}
 
 	if(isset($customres) && $customres > 0 && $customres < 500) {
-		require_once('include/Photo.php');
-		$ph = new Photo($data);
+		$ph = new Photo($data, $mimetype);
 		if($ph->is_valid()) {
 			$ph->scaleImageSquare($customres);
 			$data = $ph->imageString();
+			$mimetype = $ph->getType();
 		}
 	}
 
@@ -181,7 +191,7 @@ function photo_init(&$a) {
 		header_remove('pragma');
 	}
 
-	header("Content-type: image/jpeg");
+	header("Content-type: ".$mimetype);
 
 	if($prvcachecontrol) {
 
diff --git a/mod/photos.php b/mod/photos.php
index 3ce6f9934d..0723535871 100644
--- a/mod/photos.php
+++ b/mod/photos.php
@@ -5,6 +5,7 @@ require_once('include/acl_selectors.php');
 require_once('include/bbcode.php');
 require_once('include/security.php');
 
+
 function photos_init(&$a) {
 
 
@@ -105,6 +106,8 @@ function photos_post(&$a) {
 	logger('mod_photos: REQUEST ' . print_r($_REQUEST,true), LOGGER_DATA);
 	logger('mod_photos: FILES '   . print_r($_FILES,true), LOGGER_DATA);
 
+	$phototypes = Photo::supportedTypes();
+
 	$can_post  = false;
 	$visitor   = 0;
 
@@ -311,7 +314,7 @@ function photos_post(&$a) {
 				intval($page_owner_uid)
 			);
 			if(count($r)) {
-				$ph = new Photo($r[0]['data']);
+				$ph = new Photo($r[0]['data'], $r[0]['type']);
 				if($ph->is_valid()) {
 					$ph->rotate(270);
 
@@ -362,6 +365,7 @@ function photos_post(&$a) {
 			intval($page_owner_uid)
 		);
 		if(count($p)) {
+			$ext = $phototypes[$p[0]['type']];
 			$r = q("UPDATE `photo` SET `desc` = '%s', `album` = '%s', `allow_cid` = '%s', `allow_gid` = '%s', `deny_cid` = '%s', `deny_gid` = '%s' WHERE `resource-id` = '%s' AND `uid` = %d",
 				dbesc($desc),
 				dbesc($albname),
@@ -386,7 +390,7 @@ function photos_post(&$a) {
 
 			$title = '';
 			$uri = item_new_uri($a->get_hostname(),$page_owner_uid);
-
+			
 			$arr = array();
 
 			$arr['uid']           = $page_owner_uid;
@@ -412,7 +416,7 @@ function photos_post(&$a) {
 			$arr['origin']        = 1;
 			
 			$arr['body']          = '[url=' . $a->get_baseurl() . '/photos/' . $a->data['user']['nickname'] . '/image/' . $p[0]['resource-id'] . ']' 
-						. '[img]' . $a->get_baseurl() . '/photo/' . $p[0]['resource-id'] . '-' . $p[0]['scale'] . '.jpg' . '[/img]' 
+						. '[img]' . $a->get_baseurl() . '/photo/' . $p[0]['resource-id'] . '-' . $p[0]['scale'] . '.'. $ext . '[/img]' 
 						. '[/url]';
 		
 			$item_id = item_store($arr);
@@ -581,17 +585,17 @@ function photos_post(&$a) {
 					$arr['inform']        = $tagged[2];
 					$arr['origin']        = 1;
 					$arr['body']          = '[url=' . $tagged[1] . ']' . $tagged[0] . '[/url]' . ' ' . t('was tagged in a') . ' ' . '[url=' . $a->get_baseurl() . '/photos/' . $owner_record['nickname'] . '/image/' . $p[0]['resource-id'] . ']' . t('photo') . '[/url]' . ' ' . t('by') . ' ' . '[url=' . $owner_record['url'] . ']' . $owner_record['name'] . '[/url]' ;
-					$arr['body'] .= "\n\n" . '[url=' . $a->get_baseurl() . '/photos/' . $owner_record['nickname'] . '/image/' . $p[0]['resource-id'] . ']' . '[img]' . $a->get_baseurl() . "/photo/" . $p[0]['resource-id'] . '-' . $best . '.jpg' . '[/img][/url]' . "\n" ;
+					$arr['body'] .= "\n\n" . '[url=' . $a->get_baseurl() . '/photos/' . $owner_record['nickname'] . '/image/' . $p[0]['resource-id'] . ']' . '[img]' . $a->get_baseurl() . "/photo/" . $p[0]['resource-id'] . '-' . $best . '.' . $ext . '[/img][/url]' . "\n" ;
 
 					$arr['object'] = '<object><type>' . ACTIVITY_OBJ_PERSON . '</type><title>' . $tagged[0] . '</title><id>' . $tagged[1] . '/' . $tagged[0] . '</id>';
 					$arr['object'] .= '<link>' . xmlify('<link rel="alternate" type="text/html" href="' . $tagged[1] . '" />' . "\n");
 					if($tagged[3])
-						$arr['object'] .= xmlify('<link rel="photo" type="image/jpeg" href="' . $tagged[3]['photo'] . '" />' . "\n");
+						$arr['object'] .= xmlify('<link rel="photo" type="'.$p[0]['type'].'" href="' . $tagged[3]['photo'] . '" />' . "\n");
 					$arr['object'] .= '</link></object>' . "\n";
 
 					$arr['target'] = '<target><type>' . ACTIVITY_OBJ_PHOTO . '</type><title>' . $p[0]['desc'] . '</title><id>'
 						. $a->get_baseurl() . '/photos/' . $owner_record['nickname'] . '/image/' . $p[0]['resource-id'] . '</id>';
-					$arr['target'] .= '<link>' . xmlify('<link rel="alternate" type="text/html" href="' . $a->get_baseurl() . '/photos/' . $owner_record['nickname'] . '/image/' . $p[0]['resource-id'] . '" />' . "\n" . '<link rel="preview" type="image/jpeg" href="' . $a->get_baseurl() . "/photo/" . $p[0]['resource-id'] . '-' . $best . '.jpg' . '" />') . '</link></target>';
+					$arr['target'] .= '<link>' . xmlify('<link rel="alternate" type="text/html" href="' . $a->get_baseurl() . '/photos/' . $owner_record['nickname'] . '/image/' . $p[0]['resource-id'] . '" />' . "\n" . '<link rel="preview" type="'.$p[0]['type'].'" href="' . $a->get_baseurl() . "/photo/" . $p[0]['resource-id'] . '-' . $best . '.' . $ext . '" />') . '</link></target>';
 
 					$item_id = item_store($arr);
 					if($item_id) {
@@ -662,7 +666,7 @@ function photos_post(&$a) {
 	$str_group_deny    = perms2str(((is_array($_REQUEST['group_deny']))    ? $_REQUEST['group_deny']    : explode(',',$_REQUEST['group_deny'])));
 	$str_contact_deny  = perms2str(((is_array($_REQUEST['contact_deny']))  ? $_REQUEST['contact_deny']  : explode(',',$_REQUEST['contact_deny'])));
 
-	$ret = array('src' => '', 'filename' => '', 'filesize' => 0);
+	$ret = array('src' => '', 'filename' => '', 'filesize' => 0, 'type' => '');
 
 	call_hooks('photo_post_file',$ret);
 
@@ -670,15 +674,17 @@ function photos_post(&$a) {
 		$src      = $ret['src'];
 		$filename = $ret['filename'];
 		$filesize = $ret['filesize'];
+		$type     = $ret['type'];
 	}
 	else {
 		$src        = $_FILES['userfile']['tmp_name'];
 		$filename   = basename($_FILES['userfile']['name']);
 		$filesize   = intval($_FILES['userfile']['size']);
+		$type       = $_FILES['userfile']['type'];
 	}
+	
 
-
-	logger('photos: upload: received file: ' . $filename . ' as ' . $src . ' ' . $filesize . ' bytes', LOGGER_DEBUG);
+	logger('photos: upload: received file: ' . $filename . ' as ' . $src . ' '. $type . ' ' . $filesize . ' bytes', LOGGER_DEBUG);
 
 	$maximagesize = get_config('system','maximagesize');
 
@@ -701,7 +707,7 @@ function photos_post(&$a) {
 	logger('mod/photos.php: photos_post(): loading the contents of ' . $src , LOGGER_DEBUG);
 
 	$imagedata = @file_get_contents($src);
-	$ph = new Photo($imagedata);
+	$ph = new Photo($imagedata, $type);
 
 	if(! $ph->is_valid()) {
 		logger('mod/photos.php: photos_post(): unable to process image' , LOGGER_DEBUG);
@@ -771,7 +777,7 @@ function photos_post(&$a) {
 	$arr['origin']        = 1;
 
 	$arr['body']          = '[url=' . $a->get_baseurl() . '/photos/' . $owner_record['nickname'] . '/image/' . $photo_hash . ']' 
-				. '[img]' . $a->get_baseurl() . "/photo/{$photo_hash}-{$smallest}.jpg" . '[/img]' 
+				. '[img]' . $a->get_baseurl() . "/photo/{$photo_hash}-{$smallest}.".$ph->getExt() . '[/img]' 
 				. '[/url]';
 
 	$item_id = item_store($arr);
@@ -814,8 +820,8 @@ function photos_content(&$a) {
 		notice( t('Public access denied.') . EOL);
 		return;
 	}
-
-
+	
+	
 	require_once('include/bbcode.php');
 	require_once('include/security.php');
 	require_once('include/conversation.php');
@@ -825,6 +831,8 @@ function photos_content(&$a) {
 		return;
 	}
 
+	$phototypes = Photo::supportedTypes();
+
 	$_SESSION['photo_return'] = $a->cmd;
 
 	//
@@ -991,7 +999,7 @@ function photos_content(&$a) {
 			$a->set_pager_itemspage(20);
 		}
 
-		$r = q("SELECT `resource-id`, `id`, `filename`, max(`scale`) AS `scale`, `desc` FROM `photo` WHERE `uid` = %d AND `album` = '%s' 
+		$r = q("SELECT `resource-id`, `id`, `filename`, type, max(`scale`) AS `scale`, `desc` FROM `photo` WHERE `uid` = %d AND `album` = '%s' 
 			AND `scale` <= 4 $sql_extra GROUP BY `resource-id` ORDER BY `created` DESC LIMIT %d , %d",
 			intval($owner_uid),
 			dbesc($album),
@@ -1038,13 +1046,15 @@ function photos_content(&$a) {
 					$twist = 'rotleft';
 				else
 					$twist = 'rotright';
+				
+				$ext = $phototypes[$rr['type']];
 
 				$o .= replace_macros($tpl,array(
 					'$id' => $rr['id'],
 					'$twist' => ' ' . $twist . rand(2,4),
 					'$photolink' => $a->get_baseurl() . '/photos/' . $a->data['user']['nickname'] . '/image/' . $rr['resource-id'],
 					'$phototitle' => t('View Photo'),
-					'$imgsrc' => $a->get_baseurl() . '/photo/' . $rr['resource-id'] . '-' . $rr['scale'] . '.jpg',
+					'$imgsrc' => $a->get_baseurl() . '/photo/' . $rr['resource-id'] . '-' . $rr['scale'] . '.' .$ext,
 					'$imgalt' => template_escape($rr['filename']),
 					'$desc'=> template_escape($rr['desc'])
 				));
@@ -1158,9 +1168,9 @@ function photos_content(&$a) {
 			$prevlink = array($prevlink, '<div class="icon prev"></div>') ;
 
 		$photo = array(
-			'href' => $a->get_baseurl() . '/photo/' . $hires['resource-id'] . '-' . $hires['scale'] . '.jpg',
+			'href' => $a->get_baseurl() . '/photo/' . $hires['resource-id'] . '-' . $hires['scale'] . '.' . $phototypes[$hires['type']],
 			'title'=> t('View Full Size'),
-			'src'  => $a->get_baseurl() . '/photo/' . $lores['resource-id'] . '-' . $lores['scale'] . '.jpg' . '?f=&_u=' . datetime_convert('','','','ymdhis')
+			'src'  => $a->get_baseurl() . '/photo/' . $lores['resource-id'] . '-' . $lores['scale'] . '.' . $phototypes[$lores['type']] . '?f=&_u=' . datetime_convert('','','','ymdhis')
 		);
 
 		if($nextlink)
@@ -1449,7 +1459,7 @@ function photos_content(&$a) {
 		$a->set_pager_itemspage(20);
 	}
 
-	$r = q("SELECT `resource-id`, `id`, `filename`, `album`, max(`scale`) AS `scale` FROM `photo`
+	$r = q("SELECT `resource-id`, `id`, `filename`, type, `album`, max(`scale`) AS `scale` FROM `photo`
 		WHERE `uid` = %d AND `album` != '%s' AND `album` != '%s'  
 		$sql_extra GROUP BY `resource-id` ORDER BY `created` DESC LIMIT %d , %d",
 		intval($a->data['user']['uid']),
@@ -1469,13 +1479,14 @@ function photos_content(&$a) {
 				$twist = 'rotleft';
 			else
 				$twist = 'rotright';
-
+			$ext = $phototypes[$rr['type']];
+			
 			$photos[] = array(
 				'id'       => $rr['id'],
 				'twist'    => ' ' . $twist . rand(2,4),
 				'link'  	=> $a->get_baseurl() . '/photos/' . $a->data['user']['nickname'] . '/image/' . $rr['resource-id'],
 				'title' 	=> t('View Photo'),
-				'src'     	=> $a->get_baseurl() . '/photo/' . $rr['resource-id'] . '-' . ((($rr['scale']) == 6) ? 4 : $rr['scale']) . '.jpg',
+				'src'     	=> $a->get_baseurl() . '/photo/' . $rr['resource-id'] . '-' . ((($rr['scale']) == 6) ? 4 : $rr['scale']) . '.' . $ext,
 				'alt'     	=> template_escape($rr['filename']),
 				'album'	=> array(
 					'link'  => $a->get_baseurl() . '/photos/' . $a->data['user']['nickname'] . '/album/' . bin2hex($rr['album']),
diff --git a/mod/profile_photo.php b/mod/profile_photo.php
index ace8dadd47..2817f39423 100644
--- a/mod/profile_photo.php
+++ b/mod/profile_photo.php
@@ -53,7 +53,7 @@ function profile_photo_post(&$a) {
 
 			$base_image = $r[0];
 
-			$im = new Photo($base_image['data']);
+			$im = new Photo($base_image['data'], $base_image['type']);
 			if($im->is_valid()) {
 				$im->cropImage(175,$srcX,$srcY,$srcW,$srcH);
 
@@ -108,6 +108,7 @@ function profile_photo_post(&$a) {
 	$src      = $_FILES['userfile']['tmp_name'];
 	$filename = basename($_FILES['userfile']['name']);
 	$filesize = intval($_FILES['userfile']['size']);
+	$filetype = $_FILES['userfile']['type'];
 
 	$maximagesize = get_config('system','maximagesize');
 
@@ -118,7 +119,7 @@ function profile_photo_post(&$a) {
 	}
 
 	$imagedata = @file_get_contents($src);
-	$ph = new Photo($imagedata);
+	$ph = new Photo($imagedata, $filetype);
 
 	if(! $ph->is_valid()) {
 		notice( t('Unable to process image.') . EOL );
@@ -193,7 +194,7 @@ function profile_photo_content(&$a) {
 			goaway($a->get_baseurl() . '/profiles');
 			return; // NOTREACHED
 		}
-		$ph = new Photo($r[0]['data']);
+		$ph = new Photo($r[0]['data'], $r[0]['type']);
 		profile_photo_crop_ui_head($a, $ph);
 		// go ahead as we have jus uploaded a new photo to crop
 	}
@@ -214,7 +215,7 @@ function profile_photo_content(&$a) {
 		return $o;
 	}
 	else {
-		$filename = $a->config['imagecrop'] . '-' . $a->config['imagecrop_resolution'] . '.jpg';
+		$filename = $a->config['imagecrop'] . '-' . $a->config['imagecrop_resolution'] . '.'.$a->config['imagecrop_ext'];
 		$resolution = $a->config['imagecrop_resolution'];
 		$tpl = get_markup_template("cropbody.tpl");
 		$o .= replace_macros($tpl,array(
@@ -268,6 +269,7 @@ function profile_photo_crop_ui_head(&$a, $ph){
 
 	$a->config['imagecrop'] = $hash;
 	$a->config['imagecrop_resolution'] = $smallest;
+	$a->config['imagecrop_ext'] = $ph->getExt();
 	$a->page['htmlhead'] .= get_markup_template("crophead.tpl");
 	return;
 }}
diff --git a/mod/wall_upload.php b/mod/wall_upload.php
index fa66561e8e..20666e0ef4 100644
--- a/mod/wall_upload.php
+++ b/mod/wall_upload.php
@@ -57,16 +57,18 @@ function wall_upload_post(&$a) {
 	if(! x($_FILES,'userfile') && ! x($_FILES,'media'))
 		killme();
 
-        if(x($_FILES,'userfile')) {
-	        $src      = $_FILES['userfile']['tmp_name'];
-	        $filename = basename($_FILES['userfile']['name']);
-	        $filesize = intval($_FILES['userfile']['size']);
-        }
-        elseif(x($_FILES,'media')) {
-	        $src = $_FILES['media']['tmp_name'];
-                $filename = basename($_FILES['media']['name']);
-	        $filesize = intval($_FILES['media']['size']);
-        }
+	if(x($_FILES,'userfile')) {
+		$src      = $_FILES['userfile']['tmp_name'];
+		$filename = basename($_FILES['userfile']['name']);
+		$filesize = intval($_FILES['userfile']['size']);
+		$filetype = $_FILES['userfile']['type'];
+	}
+	elseif(x($_FILES,'media')) {
+		$src = $_FILES['media']['tmp_name'];
+		$filename = basename($_FILES['media']['name']);
+		$filesize = intval($_FILES['media']['size']);
+		$filetype = $_FILES['media']['type'];
+	}
 
 	$maximagesize = get_config('system','maximagesize');
 
@@ -77,7 +79,7 @@ function wall_upload_post(&$a) {
 	}
 
 	$imagedata = @file_get_contents($src);
-	$ph = new Photo($imagedata);
+	$ph = new Photo($imagedata, $filetype);
 
 	if(! $ph->is_valid()) {
 		echo ( t('Unable to process image.') . EOL);
@@ -123,19 +125,19 @@ function wall_upload_post(&$a) {
 /* mod Waitman Gobble NO WARRANTY */
 
 //if we get the signal then return the image url info in BBCODE, otherwise this outputs the info and bails (for the ajax image uploader on wall post)
-        if ($_REQUEST['hush']!='yeah') {
+	if ($_REQUEST['hush']!='yeah') {
 
 		/*existing code*/
 		if(local_user() && intval(get_pconfig(local_user(),'system','plaintext')))
-			echo  "\n\n" . '[url=' . $a->get_baseurl() . '/photos/' . $page_owner_nick . '/image/' . $hash . '][img]' . $a->get_baseurl() . "/photo/{$hash}-{$smallest}.jpg[/img][/url]\n\n";
+			echo  "\n\n" . '[url=' . $a->get_baseurl() . '/photos/' . $page_owner_nick . '/image/' . $hash . '][img]' . $a->get_baseurl() . "/photo/{$hash}-{$smallest}.".$ph->getExt()."[/img][/url]\n\n";
 		else
-			echo  '<br /><br /><a href="' . $a->get_baseurl() . '/photos/' . $page_owner_nick . '/image/' . $hash . '" ><img src="' . $a->get_baseurl() . "/photo/{$hash}-{$smallest}.jpg\" alt=\"$basename\" /></a><br /><br />";
+			echo  '<br /><br /><a href="' . $a->get_baseurl() . '/photos/' . $page_owner_nick . '/image/' . $hash . '" ><img src="' . $a->get_baseurl() . "/photo/{$hash}-{$smallest}.".$ph->getExt()."\" alt=\"$basename\" /></a><br /><br />";
 		/*existing code*/
 		
 	} else {
-                $m = '[url=' . $a->get_baseurl() . '/photos/' . $page_owner_nick . '/image/' . $hash . '][img]' . $a->get_baseurl() . "/photo/{$hash}-{$smallest}.jpg[/img][/url]";
+		$m = '[url=' . $a->get_baseurl() . '/photos/' . $page_owner_nick . '/image/' . $hash . '][img]' . $a->get_baseurl() . "/photo/{$hash}-{$smallest}.".$ph->getExt()."[/img][/url]";
 		return($m);
-        }
+	}
 /* mod Waitman Gobble NO WARRANTY */
 
 	killme();
diff --git a/update.php b/update.php
index 35a6771b00..0f7bf664d9 100644
--- a/update.php
+++ b/update.php
@@ -1,6 +1,6 @@
 <?php
 
-define( 'UPDATE_VERSION' , 1148 );
+define( 'UPDATE_VERSION' , 1149 );
 
 /**
  *
@@ -1283,3 +1283,9 @@ function update_1147() {
 	return UPDATE_SUCCESS ;
 }
 
+function update_1148() {
+	$r = q("ALTER TABLE photo ADD type CHAR(128) NOT NULL DEFAULT 'image/jpeg' AFTER filename");
+	if (!$r)
+		return UPDATE_FAILED;
+	return UPDATE_SUCCESS;
+}

From 2438c6e45d47ca194b51f99ddf113112fbb6ccb9 Mon Sep 17 00:00:00 2001
From: Fabio Comuni <fabrix.xm@gmail.com>
Date: Thu, 7 Jun 2012 20:17:31 +0200
Subject: [PATCH 2/2] png support: update database.sql, fix some typos, fix
 tinymce image browser

---
 database.sql          |  1 +
 include/Photo.php     | 26 ++++++++++++++------------
 mod/fbrowser.php      | 18 ++++++++++++++----
 mod/photos.php        |  4 ++--
 mod/profile_photo.php |  3 ++-
 mod/wall_upload.php   |  3 ++-
 6 files changed, 35 insertions(+), 20 deletions(-)

diff --git a/database.sql b/database.sql
index c1d1b27e4e..53dc0c5b21 100644
--- a/database.sql
+++ b/database.sql
@@ -745,6 +745,7 @@ CREATE TABLE IF NOT EXISTS `photo` (
   `desc` text NOT NULL,
   `album` char(255) NOT NULL,
   `filename` char(255) NOT NULL,
+  `type` CHAR(128) NOT NULL DEFAULT 'image/jpeg',
   `height` smallint(6) NOT NULL,
   `width` smallint(6) NOT NULL,
   `data` mediumblob NOT NULL,
diff --git a/include/Photo.php b/include/Photo.php
index ffefc37eca..f769a70a6e 100644
--- a/include/Photo.php
+++ b/include/Photo.php
@@ -105,9 +105,9 @@ class Photo {
 
 
 		$dest = imagecreatetruecolor( $dest_width, $dest_height );
-		imagealphablending($dest->image, false);
+		imagealphablending($dest, false);
 		imagesavealpha($dest, true);
-		if ($this->type=='image/png') imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127));; // fill with alpha
+		if ($this->type=='image/png') imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
 		imagecopyresampled($dest, $this->image, 0, 0, 0, 0, $dest_width, $dest_height, $width, $height);
 		if($this->image)
 			imagedestroy($this->image);
@@ -164,9 +164,9 @@ class Photo {
 
 
 		$dest = imagecreatetruecolor( $dest_width, $dest_height );
-		imagealphablending($dest->image, false);
+		imagealphablending($dest, false);
 		imagesavealpha($dest, true);
-		if ($this->type=='image/png') imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127));; // fill with alpha
+		if ($this->type=='image/png') imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
 		imagecopyresampled($dest, $this->image, 0, 0, 0, 0, $dest_width, $dest_height, $width, $height);
 		if($this->image)
 			imagedestroy($this->image);
@@ -181,9 +181,9 @@ class Photo {
 	public function scaleImageSquare($dim) {
 
 		$dest = imagecreatetruecolor( $dim, $dim );
-		imagealphablending($dest->image, false);
+		imagealphablending($dest, false);
 		imagesavealpha($dest, true);
-		if ($this->type=='image/png') imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127));; // fill with alpha
+		if ($this->type=='image/png') imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
 		imagecopyresampled($dest, $this->image, 0, 0, 0, 0, $dim, $dim, $this->width, $this->height);
 		if($this->image)
 			imagedestroy($this->image);
@@ -195,9 +195,9 @@ class Photo {
 
 	public function cropImage($max,$x,$y,$w,$h) {
 		$dest = imagecreatetruecolor( $max, $max );
-		imagealphablending($dest->image, false);
+		imagealphablending($dest, false);
 		imagesavealpha($dest, true);
-		if ($this->type=='image/png') imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127));; // fill with alpha
+		if ($this->type=='image/png') imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
 		imagecopyresampled($dest, $this->image, 0, 0, $x, $y, $max, $max, $w, $h);
 		if($this->image)
 			imagedestroy($this->image);
@@ -295,6 +295,7 @@ class Photo {
  * @arg $fromcurl boolean Check Content-Type header from curl request
  */
 function guess_image_type($filename, $fromcurl=false) {
+    logger('Photo: guess_image_type: '.$filename . ($fromcurl?' from curl headers':''), LOGGER_DEBUG);
 	$type = null;
 	if ($fromcurl) {
 		$a = get_app(); 
@@ -310,12 +311,13 @@ function guess_image_type($filename, $fromcurl=false) {
 	if (is_null($type)){
 		$ext = pathinfo($filename, PATHINFO_EXTENSION);
 		$types = Photo::supportedTypes();
-		foreach ($types as $m=>$e){
-			if ($ext==$e) return $m;
-		}
 		$type = "image/jpeg";
-	}
+		foreach ($types as $m=>$e){
+			if ($ext==$e) $type = $m;
+		}
 
+	}
+    logger('Photo: guess_image_type: type='.$type, LOGGER_DEBUG);
 	return $type;
 	
 }
diff --git a/mod/fbrowser.php b/mod/fbrowser.php
index 66ff9252eb..3f034226e1 100644
--- a/mod/fbrowser.php
+++ b/mod/fbrowser.php
@@ -4,7 +4,9 @@
  * @subpackage	FileBrowser
  * @author		Fabio Comuni <fabrixxm@kirgroup.com>
  */
- 
+
+require_once('include/Photo.php');
+
 /**
  * @param App $a
  */
@@ -43,14 +45,22 @@ function fbrowser_content($a){
 				$path[]=array($a->get_baseurl()."/fbrowser/image/".$a->argv[2]."/", $album);
 			}
 				
-			$r = q("SELECT `resource-id`, `id`, `filename`, min(`scale`) AS `hiq`,max(`scale`) AS `loq`, `desc`  
+			$r = q("SELECT `resource-id`, `id`, `filename`, type, min(`scale`) AS `hiq`,max(`scale`) AS `loq`, `desc`  
 					FROM `photo` WHERE `uid` = %d $sql_extra
 					GROUP BY `resource-id` $sql_extra2",
 				intval(local_user())					
 			);
 			
-			
-			function files1($rr){ global $a; return array( $a->get_baseurl() . '/photo/' . $rr['resource-id'] . '-' . $rr['hiq'] . '.jpg', template_escape($rr['filename']), $a->get_baseurl() . '/photo/' . $rr['resource-id'] . '-' . $rr['loq'] . '.jpg');  }
+			function files1($rr){ 
+				global $a;
+				$types = Photo::supportedTypes();
+				$ext = $types[$rr['type']];
+				return array( 
+					$a->get_baseurl() . '/photo/' . $rr['resource-id'] . '-' . $rr['hiq'] . '.' .$ext, 
+					template_escape($rr['filename']), 
+					$a->get_baseurl() . '/photo/' . $rr['resource-id'] . '-' . $rr['loq'] . '.'. $ext
+				);
+			}
 			$files = array_map("files1", $r);
 			
 			$tpl = get_markup_template("filebrowser.tpl");
diff --git a/mod/photos.php b/mod/photos.php
index 0723535871..efeab8fa3c 100644
--- a/mod/photos.php
+++ b/mod/photos.php
@@ -682,9 +682,9 @@ function photos_post(&$a) {
 		$filesize   = intval($_FILES['userfile']['size']);
 		$type       = $_FILES['userfile']['type'];
 	}
-	
+	if ($type=="") $type=guess_image_type($filename);
 
-	logger('photos: upload: received file: ' . $filename . ' as ' . $src . ' '. $type . ' ' . $filesize . ' bytes', LOGGER_DEBUG);
+	logger('photos: upload: received file: ' . $filename . ' as ' . $src . ' ('. $type . ') ' . $filesize . ' bytes', LOGGER_DEBUG);
 
 	$maximagesize = get_config('system','maximagesize');
 
diff --git a/mod/profile_photo.php b/mod/profile_photo.php
index 2817f39423..e44707f9bb 100644
--- a/mod/profile_photo.php
+++ b/mod/profile_photo.php
@@ -109,7 +109,8 @@ function profile_photo_post(&$a) {
 	$filename = basename($_FILES['userfile']['name']);
 	$filesize = intval($_FILES['userfile']['size']);
 	$filetype = $_FILES['userfile']['type'];
-
+    if ($filetype=="") $filetype=guess_image_type($filename);
+    
 	$maximagesize = get_config('system','maximagesize');
 
 	if(($maximagesize) && ($filesize > $maximagesize)) {
diff --git a/mod/wall_upload.php b/mod/wall_upload.php
index 20666e0ef4..4b81f8d1c2 100644
--- a/mod/wall_upload.php
+++ b/mod/wall_upload.php
@@ -69,7 +69,8 @@ function wall_upload_post(&$a) {
 		$filesize = intval($_FILES['media']['size']);
 		$filetype = $_FILES['media']['type'];
 	}
-
+	
+    if ($filetype=="") $filetype=guess_image_type($filename);
 	$maximagesize = get_config('system','maximagesize');
 
 	if(($maximagesize) && ($filesize > $maximagesize)) {