All posts by Everett - TipsFor.us

Bulk Image Processing: How to shrink a bunch of images using iPhoto on OS X

Here’s a real quick tip. Let’s pretend that you took a bunch of nice big photos with your nice digital camera. But your friend wants copies… and you can’t email them because the files are huge! How do you shrink down hundreds of images quickly? Let iPhoto and Mail help…

See the video for this tip in action, or follow these simple steps:

1. Open iPhoto and highlight the images you want
2. Choose to Mail the photos by clicking the Mail icon at the bottom of iPhoto.
3. Choose the size that you want for your new images.
4. When the new Mail message opens up, you could actually send the message at this point (assuming that Mail is configured correctly to send and receive messages), OR you can select all, then drag the message contents over into a desktop folder.
5. You can zip up the contents of the Desktop folder and be done!

Checking to See if Packages are Installed in PHP or Perl

I have this love/hate thing with open source technology. It’s great that it’s free, bugs are openly discussed instead of hidden away, and ultimately I think the open source technologies are more robust.

But here’s what always gets me: the packages and their dependencies.

Perl

At the bash prompt, you can type:
perl -e 'use Some::Package'

If the package is installed, nothing will happen. The one line script executes without complaint. But if you DON’T have that package installed, you’ll get an error like this:
Can't locate Some/Package.pm in @INC (@INC contains: /usr/lib/perl5/site_perl/5.8.8/i386-linux-thread-multi
[...]
/usr/lib/perl5/5.8.8/i386-linux-thread-multi /usr/lib/perl5/5.8.8 .) at -e line 1.
BEGIN failed--compilation aborted at -e line 1.

If that’s the case, you need to find and install that package. With Perl, the way to go is to get familiar with www.cpan.org — yes, it’s a fugly site, but you can search for packages and read up on them. If you want to download them, I recommend using the CPAN command line tool. You run this utility by simply typing cpan on the bash command line. Take the time to understand this utility! It will save you SO MUCH TIME.

PHP

PHP is nowhere near as verbose as Perl when it comes to error messages and debugging; it’s also a bit more mysterious when it comes to its package structure. You can get a lot of information by examining the output of the phpinfo() function. Simply write following script and hit it with a web browser:


<?php phpinfo(); ?>

On the command line, you can use rpm to see if a specific package is installed… however, good luck guessing what the package name is. For example, here you can check the version of the GD graphics library:
rpm -q php-gd
If the package is installed, the result should be something like:
php-gd-5.1.6-20.el5_2.1

You can also have a look in the php module directory. For Linux systems, it’s often here: /usr/lib/php/modules/

Search the File System

If you’re still frustrated, you can search the file system for the file — modules are FILES… they live SOMEWHERE. In Linux, you can use the find command:
find /path/to/start/search -name 'name_of_file'
E.g. to search for myfile.txt in the current directory or beneath, you’d type this:

find . -name myfile.txt

All the Linux file name metacharacters are valid, e.g. “*.txt” to search for all text files. The path can also be specified as any valid pathname, e.g. “~/” to refer to the current user’s home directory.

It’s a bit messy in there, but hopefully this helps you evaluate a system to see if your scripts or pages will actually work!

Stealth Submit: Using AJAX to Save Form Data Without Submitting the Form

Ah, this is sneaky. You may think that a web site can’t read form fields until you hit the submit button, but this is not the case. Using AJAX, a site can read form data at any time. This could be used for nefarious purposes, but I’m presenting here for two reasons: 1) to let people know that it can be done and 2) to show people how to do it for legitimate purposes. What’s a legitimate use of this? Logging, mostly… a lot of work goes into studying why users may fill out only the first page of a form. If you log the data regardless of whether a user clicked submit, you can study user behavior and possibly improve your form.

This tutorial assumes the following: you have a web server running PHP 5 (or greater) and MySQL 5 (or greater) with the mysqli extension installed. There are several parts to how this works:

1. savedata.php: A processing page that grabs posted variables (e.g. savedata.php?variable=value).
2. db.php: A database interface file (usually contained above the document root, contains passwords and save logic).
3. index.php: The HTML page containing the form.
4. The prototype JavaScript library. This is a very handy library that’s pretty easy to use.

Here’s an image showing how these files are organized in their folder structure. /html is the document root of the site: all browser requests are directed in there. /lib is above the document root… only PHP can make calls inside that folder. That’s were we put our database connection logic, including usernames and passwords.

Folder structure used for Stealth Submit Files
Folder structure used for Stealth Submit Files

The files used in this demo are listed below, but because scripts tend to get buggy when they’ve undergone conversion to/from html-entities, I’m zipping up the files and uploading them here.
Stealth Submit Sample Files.zip

You should be able to unload the html and lib directories to your web server. Point your DocumentRoot at the html directory; be sure to run the SQL commands included at the top of the db.php file so your database looks the way that this script expects it to.

For the use of visibility, the contents of these files is included below:

html/savedata.php : the PHP Page that grabs and stores variables


<?php
/*
Test this page by hitting it in a browser with variables:
http://your_domain/savedata.php?first_name=Bart&last_name=Simpson&age=11&home_town=Springfield&job=Punk

*Add &debug=1 to the url to print out verbose messages.
*/

include_once($_SERVER['DOCUMENT_ROOT'] . "/../lib/db.php");
$debug = $_REQUEST['debug'];
$debug_msg ="<p>The following variables were passed:</p><hr/>";

// Harvest all form data: this works for posts and gets.
foreach ($_REQUEST as $var => $value) {
$debug_msg .= "<b>$var</b>: $value<br/>";
$form_data[$var] = $value;
}

if ($debug) {
echo $debug_msg . "<hr/>";
}

$result = save_user_data($form_data);

if ($debug) {
if ($result) {
echo "Data saved successfully. Id: $result";
} else {
echo "Error saving data.";
}
}

?>

lib/db.php : Here’s the Database Interface Page


<?php
/*
Contains database handle (username/password) and saving functions

Here's the table definition for the table used in this demo:

CREATE DATABASE ajax_demo;

CREATE TABLE `user` (
`id` int(11) NOT NULL auto_increment,
`first_name` char(64) default NULL,
`last_name` char(64) default NULL,
`age` tinyint(3) default NULL,
`home_town` char(64) default NULL,
`job` char(64) default NULL,
UNIQUE KEY `id` (`id`)
) ENGINE=MyISAM;

GRANT ALL PRIVILEGES ON ajax_demo.* TO 'peter'@'%'
IDENTIFIED BY 'abc123';
*/

// Test regex's here by uncommenting this line and executing this file.
// echo get_name_regex('Bob');
/*-------------------------------------------------------------------------
DEFINE Database constants here
---------------------------------------------------------------------------*/
define("DATABASE_HOST", 'localhost');
define("DATABASE", 'ajax_demo');
define("DEFAULT_USER", 'peter');
define("DEFAULT_USER_PASSWORD", 'abc123');

function connect_db ($user = DEFAULT_USER) {
/*
INPUT: $user
This function allows for multiple handles to be called, e.g. handles for
read-only, write-only, etc. Each user has its own permissions.
mysqli format is mysqli(DATABASE_HOST, USER, PASSWORD, DATABASE);
*/
switch($user) {
case DEFAULT_USER:
$link = new mysqli(DATABASE_HOST, DEFAULT_USER, DEFAULT_USER_PASSWORD, DATABASE);
break;
}

return $link;

}

/*-------------------------------------------------------------------------*/
function save_user_data ($input) {
/*
INPUT:
Hash with values from form, e.g. $input['first_name'].
OUTPUT:
id from database if successful insert; otherwise null.
*/

$link = connect_db();
/* check connection */
if ( mysqli_connect_errno() ) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}

$sql = "INSERT INTO user
(
first_name,
last_name,
home_town,
job,
age
) VALUES (
?,
?,
?,
?,
?
)";

$statement = $link->prepare($sql);

if (!$statement) {
printf('Error - SQLSTATE %s.\n', mysqli_sqlstate($db_connection));
exit();
};

// Filter data (regex's, validate, etc)
$first_name = get_name_regex($input['first_name']);
$last_name = get_name_regex($input['last_name']);
$home_town = get_name_regex($input['home_town']);
$job = get_name_regex($input['job']);
$age = get_integers_only_regex($input['age']);

// Bind-parameters: s=string, i=integer, d=double, b=blob
$statement->bind_param('ssssi', $first_name, $last_name, $home_town, $job, $age);

$statement->execute();

if ($link->error) {
echo $link->error;
}

if ($link->insert_id) {
$result = $link->insert_id;
}

echo

$link->close();

return $result;
}

/*====== DATA FILTERING and VALIDATION =============================*/
function get_name_regex ($input) {
$pattern = '/(;|\||`|=|--|\/|\.|>|<|&|^|"|'."\n|\r".'|{|}|[|]|\)|\(|[0-9])/i';
$input = preg_replace($pattern, ' ', $input);
return trim(ucfirst($input));
}

// Given any input, we only want a valid integer here, e.g. 2.24 --> 2
// Specify the length desired.
function get_integers_only_regex ($input, $len = 100) {
// $input = (int)$input; // typecast will fail if you have a zip w/ a leading 0, e.g. 09921
if ($len > 0) {
$pattern = '/\D/';
$input = preg_replace($pattern, '', $input);
$input = substr($input, 0, $len);
}
return $input;
}
?>

html/index.php : the HTML Page Containing the Form

<html>
<head><title>Stealth Submit</title>

<script src="/js/prototype-1.6.0.3.js"></script>
<script language = "Javascript">
function saveData() {
new Ajax.Request('savedata.php', {
method: 'post',
parameters: {
// $('form_id').serialize(true)
first_name: document.getElementById('first_name').value,
last_name: document.getElementById('last_name').value,
home_town: document.getElementById('home_town').value,
age: document.getElementById('age').value,
job: document.getElementById('job').value
}
});
}

</script>

</head>
<body onunload="saveData()">
<form id="form_id" method="post" action="action.php">
First Name: <input type="text" id="first_name" name="first_name"><br/>
Last Name: <input type="text" id="last_name" name="last_name"><br/>
Age: <input type="text" id="age" name="age"><br/>
Hometown: <input type="text" id="home_town" name="home_town"><br/>
Job: <input type="text" id="job" name="job"><br/>
<input type="submit" value="Submit" />
</form>

<a href="http://www.google.com/">Arbitrary Other Page... FORM NOT SUBMITTED</a>

</body>
</html>

Installing Packages in Linux

I always forget the exact commands required to extracting my little tarballs onto my Linux system after downloading them. But wait! Before you go downloading a tarball, try to download a pre-compiled package using a package management tool like Yum, APT, Aptitude, Pacman, Portage, Yast, etc.. You will save LOTS of time by doing this. However, for the sake of this post, let’s pretend you’ve entered the grim and dark world of un-compiled packages, and now you must must make the sacred journey into adulthood by downloading and compiling your own packages. (There’s still time to back out… are you sure there isn’t a pre-compiled package out there?)

Unpacking a .gz File

For this example, let’s say that you’ve downloaded the file ImageMagick-6.4.4-10.tar.gz (e.g. by using the wget utility or by uploading the package to your Linux server).

tar -zxvf ImageMagick-6.4.4-10.tar.gz

This will extract the file to the current directory.

* You may need preface the commands using the sudo command… as in “sudo make me a sandwich“. You may also want to read the man page: man tar

After Unpacking…

cd into the new directory and read the documentation! It’s a bummer, but when you roll your own stuff here, you gotta RTFM. That’s just part of the sacred ritual. Be glad it’s not adult circumcision we’re talking about.

In a nutshell, the compilation process usually goes a little something like this:

  1. ./configure [with options listed]
  2. make
  3. make install

Each of these steps may take a while… compiling and testing and such. Go get some coffee.

What do I do if my iPhone is Lost or Stolen?

Holy flaming monkey balls! I lost my iPhone! What do I do?

  1. Call AT&T Wireless at 800-331-0500 and use their menu to report a lost or stolen phone.
    This does not protect your data in any way, it only shuts down the ability of your phone to make or receive phone calls. (Your monthly bills will CONTINUE… haha.)
  2. File a police report.
  3. Try not to kick yourself for not following the rest of the instructions in this article…

That SUCKS. What Else Can I Do?

To bring back that lovely word we learned in 8th grade sex-ed: PREVENTION!

Even if you don’t like it…. it’s time to change the security settings on your phone.

  1. Enable Auto-Lock : Settings -> General –> Auto-Lock (must be ON, e.g. 3 minutes)
  2. Enable the Passcode Lock : Settings -> General –> Passcode Lock
    Turn Passcode ON (enter a 4 digit passcode twice)

  3. Require Passcode : set a time interval, e.g. “Immediately”, or “After 15 min.”
  4. Enable the Self Destruct : Erase Data –> ON (Erase all data on this iPhone after 10 failed passcode attempts)
    * Gulp… this one isn’t for the weak of heart. Ideally, all the data on your iPhone is only a copy, right? You’ve synced recently, RIGHT!? Well… there is the possibility that you’ll lose unique data if these feature ever kicks in. There’s also the possibility that some a-hole trickster buddy of yours will finger-mash your phone repeatedly in order to delete everything. No matter if your data is in the hands of nefarious users or deleted by rowdy friends, either way, this is a serious feature.
Passcode Lock Screen on the iPhone
Passcode Lock Screen on the iPhone

And for more privacy….

Turn OFF SMS Preview : Settings -> General –> Passcode Lock –> Turn OFF SMS Preview

* The preview is what displays on your phone when a text message comes in… by default the ENTIRE text and the sender’s name shows up on your screen without you having to unlock the phone. This could be embarrassing if the text is personal (e.g. if you are sleeping with your boss’s wife, and you get a bunch of XXO’s coming in during an important meeting). If you have the SMS Preview disabled, you’ll only see “New Text Message”.

And for the Good Samaritans…

There is a web site dedicated to lost and found iPhones:
www.ifoundyouriphone.com

You can register your serial number on your phone and put a message on your wallpaper where the person who finds your phone can go and contact you to get it properly returned. I encourage everyone to do this. There are a lot of good people out there, and hey, doing this simple step allows those people to be good. If all they got is a random phone that self-destructed its data, then they might just decide to keep it, but if they can look up your name and address, they might try to give it back to you. Think positively.

Taking Screen shots on an iPhone 3G

You can take screenshots of your iPhone by pressing and holding both buttons for about a second.
You can take screenshots of your iPhone by pressing and holding both buttons for about a second.

This is an (un)documented built-in feature on the iPhone 3G (i.e. phones with OS 2.0 or greater). Just prep your phone screen however you want it, then press the round application button and the power/sleep button at the top simultaneously (read: your phone’s only got 2 buttons, press them both). Hold the buttons for about a second, and if your sound is on, then you should get the obligatory shutter sound.

It’s a handy way to take screenshots!

Then the next question… how do get those pictures OFF my phone? Two easy ways: sync your phone (all the screenshots show up in your Photos application, just like anything you take using your camera). Or, view the photo in the Photos app, then email the photo to yourself. But this is a bummer because sometimes I’ve been unable to email the photo (Yahoo and a personal account).

You CAN sync up to any computer… if you plug your phone into any Mac computer, for example, iPhoto should be able to grab photos off your iPhone camera, but you can’t preview the PNG screenshots.

Don't be afraid! iPhoto can't preview PNG files, but you can use Preview to convert them to JPGs
Don't be afraid! iPhoto can't preview PNG files, but you can use Preview to convert them to JPGs

Don’t be afraid if you can’t see your screenshot! iPhoto can’t preview the PNG files, but you can drag them to the desktop and open them using Preview (or similar) to convert the file format.

Tech Tips for Databases, Techies, and Pumpkins

Trick or Treat! Since it’s Halloween, I thought it’d be funny to see what other tech tips are out there. So, here is the tale of my obligatory Google search for our site’s prized search term: “Tech Tips for Us”

Tech Tips for Catholic Teens — Ah, yes, FINALLY someone did what we’ve been meaning to do as a sub-domain for years! Why visit “catholic_teen.tipsfor.us” when you have “http://catholictechtips.stblogs.com/”? Other than the obviously strange preclusion about the target audience, the perfunctory cross fav.icon, and the notable omission about the hottest techie action for altar boys, the blog looks suspiciously similar to our own…

http://www.unixwiz.net/techtips/sql-injection.html This is a good site for people wanting to understand how to protect from MySQL database vulnerabilities… the Wiki article is also pretty good. I wrote a little article about MySQL Security that seemed a bit too geeky for this blog (I know, I know… I’m disappointing the fans). I think my favorite story about a site getting owned was when the Dremel site (famous for their moto-tool) was hacked via SQL-injection and the homepage ended up being replaced with an image of a Goatse-pumpkin. (Appropriately, the pumpkin was carved with a Dremel Moto-tool). Watch out boys and girls! SQL injection is not pretty!

This is not the pumpkin image that was placed on the Dremel site, but it's close enough.
This is not the pumpkin image that was placed on the Dremel site, but it's close enough.

LifeHacker.com — hey, this site looks legit. They even have a seasonal article about Do-It-Yourself Facial Prosthetics! They also have lots of old-school artwork with airships — the kind with little propellers that you see in Miyazaki films or in Smashing Pumpkins videos. I LOVE THAT!