php wishlist script/app

Because I can’t help being self-absorbed and excited by all the presents I’m going to get for my birthday, and because I wanted to practice a bit with php, I made a PHP-based wishlist.

Basically it allows your friends to see your wishlist and get dibs on stuff you want so that multiple people don’t end up getting you the same thing.

It’s flat-file based because I don’t fully understand MySQL yet and I like not having to deal with a completely separate entity.

Files involved:

  • wishlist.csv – this contains all the stuff you want, with each line containing “item,description,link” with description and link being optional. Each time a person selects an item they’re going to get you, a wishlist2.csv is created with your original wishlist sans the item he/she picked, and then renamed to wishlist.csv.
  • index.php – reads wishlist.csv and creates a form with a bunch of radio buttons for each item you have
  • wishlist-form.php – processes the item selection and recreates wishlist.csv without the item picked, and shows a thank you message

Example of wishlist.csv:

prodeco bike,+ pegs + rack + fenders? (I LIKE EXPENSIVE THINGS SUE ME),http://www.bikemania.biz/Prodeco_G_Plus_Mariner_Sport_Electric_Bike_p/prodecotech_gplusmar_s.htm
running pants (small/sz 30),cause winter's too cold for shorts
raspberry drops,yummy yummy raspberry drops
origami,bonus points if made out of cash

Code for reading wishlist.csv and the form which displays it:

What are you going to get for Roman's BIRTHDAY??

Since bet everyone's just dying to get me gifts I made this little webpage to make sure no one gets me the same present. Cause who needs 4 electric bikes, amiright?

Whichever gift you pick will disappear from this page.

<?php $row = 1; if (($handle = fopen("wishlist.csv", "r")) !== FALSE) { while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) { $num = count($data); switch ($num) { case 0: break; case 1: echo " $data[0]
"; break; case 2: echo " $data[0] - $data[1]
"; break; case 3: echo " $data[0] - $data[1]
"; break; } $row++; } fclose($handle); } ?>

Lastly, wishlist-form.php:

<?php
$aGift = $_POST['gift'];
if(empty($aGift)) 
	echo "

You didn't select any presents =(.

"; else { $fp = fopen('wishlist2.csv', 'w'); if (($handle = fopen("wishlist.csv", "r")) !== FALSE) { $row = 1; while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) { if ($row!=$aGift[0]) { fputcsv($fp,$data); $row++; } else { $present = $data[0]; $row++; } } fclose($handle); fclose($fp); unlink('wishlist.csv'); rename('wishlist2.csv','wishlist.csv'); } echo "

Thanks for getting $present for me!

"; } ?>

You are the pride of [subject town].

IE9’s toolbar size versus other browsers

It seems with every new version of a web browser, the toolbar gets smaller and smaller. This makes a lot of sense since, as a user, you want as much space as possible for the websites you’re visiting.

In the latest version of Internet Explorer 9, the toolbar panel takes the most minimalist approach ever taken with IE. It actually beats Google Chrome in terms of least amount of space used:

This is partially achieved by combining the search and the address bar as Chrome has done when it was first released, and by leaving only the most essential navigation buttons.

The size difference becomes even more drastic when compared to Firefox 3, which, even when you remove the default bookmarks toolbar, is at least twice the size of IE9’s toolbar.

You can definitely tell that Microsoft is making every effort in making sure users don’t have any need for alternative browsers, and has learned a few lessons from its competitors while implementing its own innovations.

AT&T’s Tethering Costs Infinity Per Megabyte

I was recently sent this article:

http://www.crunchgear.com/2008/07/01/atts-text-messages-cost-1310-per-megabyte/

It basically talks about how text messaging fees have you paying $1,310 per megabyte of data. Which is true – texting fees were always ridiculous and somehow wireless carriers were able to capitalize on that.

Tethering is a little different. When you use tethering (a feature that was built into the iphone in 3.0 over a year ago: http://gizmodo.com/5171796/iphone-30-os-guide-everything-you-need-to-know) your phone acts as an internet connection relay/access point for your laptop. You’re using your data plan on your laptop instead of your phone.

Naturally a person would use more data on their laptop than on their phone as it’s more convenient to watch online videos, write emails, etc. on a laptop. In the past a carrier would charge extra for tethering since data plans were unlimited but more data usage is a higher load on a network that a carrier has to maintain and pay for. However, with data caps on all of AT&T’s plans, using more data than your plan covers already results in heavy fees ($10 for every 1GB overage in the 2GB plan).

So charging an extra $20 a month just to be able to tether is just AT&T saying “we own your asses.”

An excerpt from an interview with Mark Collins, senior VP of data and voice products at AT&T:

GigaOM: What about the $20 tethering fee? It looks like a convenience charge.

Collins: That capability is enabling something you can’t do today. You can use one device and get multiple connections so it’s more useful to you. You’re going to use more data so the price is based on the value that will be delivered.

Enabling something AT&T disabled last year. Using data which already increases in cost depending on how much you use.

Simple analogy: Imagine Apple introduced a feature in their older iPhones (original, 3g) that allowed them to record videos using the camera and email them to your friends. AT&T disabled that feature on the account that you will probably end up using more data with it since you will be emailing videos. However, they finally decided they are going to charge $20 per month for “enabling” it and because “you’re going to use more data.”

Firewall (iptables) rules for Zimbra in CentOS

In my previous posts I have documented how to set up a Raid-5 CentOS system, test that RAID’s reliability, and install Zimbra on said system.

In this post I will go over strengthening the security for your system by editing the default port for SSH access and configuring iptables to only accept traffic on ports required by Zimbra, only from certain IP addresses.

1) To change your SSH server port, edit the following line in /etc/ssh/sshd_config

Port 22

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }You can change the number to whichever you prefer (I changed mine to 130). In many cases, this will cut down on outside brute force attacks by as much as 99%.

However, one of Zimbra’s services uses the SSH port for access, so if you do change it, you have to also follow the procedure in step 2 to prevent Zimbra from giving you errors when you try to access some details in Administration Console.

2) From: http://www.zimbra.com/forums/administrators/11796-change-port-22-a.html

1. Check /etc/ssh/sshd_config and be sure it’s set to 130 (or the port you’re using)

2. stop/start/restart sshd

Code:

service sshd restart

3. su zimbra

4. Be sure zimbra’s ssh port is set to 130 (or the port you’re using), and change “server.domain.com” in the following code to your full hostname.

Code:

zmprov ms server.domain.com zimbraRemoteManagementPort 130

5. Generate new ssh keys

Code:

cd /opt/zimbra/bin/
./zmsshkeygen

6. Deploy the keys

Code:

./zmupdateauthkeys

To test this, you can check the admin console mail queues area/servers/certificates. If you don’t get any errors, then the port has been changed successfully.

3) Unfortunately, I was too lazy to figure out how iptables works, but you can read about it here:

http://wiki.centos.org/HowTos/Network/IPTables

So basically what I did was take an existing line for allowing access to a port in my iptables file (/etc/sysconfig/iptables):

-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Then, if you want to limit access to that port (the web server port in this case, which lets you access Zimbra’s web mail client) to a specific network, like my school network, you append –s and the network range like so:

-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 80 -s 130.245.0.0/16 -j ACCEPT

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

and copy/paste it a whole bunch of times for each port you want to have open for incoming traffic. In nano you can do this by pressing ctrl+k and then ctrl+u a bunch of times, on any line you want to paste.

You can see which ports Zimbra uses here:

http://wiki.zimbra.com/wiki/Ports

It seems to work correctly with just the external access ports enabled in iptables. I’m guessing that’s because all the internal ports are open due to the following line in iptables:

-A RH-Firewall-1-INPUT -i lo -j ACCEPT

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }I believe it means that all incoming traffic from the system itself (localhost) is accepted.

Don’t forget to add a rule for the reconfigured SSH port as well, if you happened to change it.

Also, if you want to be able to receive email from anywhere, make sure you don’t add the –s parameter to port 25.

Here’s how my iptables looks after I edited it for Zimbra and SSH access:

# Firewall configuration written by system-config-securitylevel
# Manual customization of this file is not recommended.
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:RH-Firewall-1-INPUT - [0:0]
-A INPUT -j RH-Firewall-1-INPUT
-A FORWARD -j RH-Firewall-1-INPUT
-A RH-Firewall-1-INPUT -i lo -j ACCEPT
-A RH-Firewall-1-INPUT -p icmp --icmp-type any -j ACCEPT
-A RH-Firewall-1-INPUT -p 50 -j ACCEPT
-A RH-Firewall-1-INPUT -p 51 -j ACCEPT
-A RH-Firewall-1-INPUT -p udp --dport 5353 -d 224.0.0.251 -j ACCEPT
-A RH-Firewall-1-INPUT -p udp -m udp --dport 631 -j ACCEPT
-A RH-Firewall-1-INPUT -p tcp -m tcp --dport 631 -j ACCEPT
-A RH-Firewall-1-INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 25 -j ACCEPT
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 80 -s 130.245.0.0/16 -j ACCEPT
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 110 -s 130.245.0.0/16 -j ACCEPT
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 130 -s 130.245.0.0/16 -j ACCEPT
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 143 -s 130.245.0.0/16 -j ACCEPT
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 443 -s 130.245.0.0/16 -j ACCEPT
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 993 -s 130.245.0.0/16 -j ACCEPT
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 995 -s 130.245.0.0/16 -j ACCEPT
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 7071 -s 130.245.0.0/16 -j ACCEPT
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 80 -s 129.49.0.0/16 -j ACCEPT
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 110 -s 129.49.0.0/16 -j ACCEPT
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 130 -s 129.49.0.0/16 -j ACCEPT
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 143 -s 129.49.0.0/16 -j ACCEPT
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 443 -s 129.49.0.0/16 -j ACCEPT
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 993 -s 129.49.0.0/16 -j ACCEPT
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 995 -s 129.49.0.0/16 -j ACCEPT
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 7071 -s 129.49.0.0/16 -j ACCEPT
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 130 -s 71.247.43.111 -j ACCEPT
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 80 -s 71.247.43.111 -j ACCEPT
-A RH-Firewall-1-INPUT -j REJECT --reject-with icmp-host-prohibited
COMMIT

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }