Building a Big Project, Part 7

Status
Not open for further replies.

Baljeet

Member
Jan 31, 2011
76
0
Note: this is part of a series.
It starts here:
I'm picking up from here:

One of my friends has been following along with this series, and was getting some odd errors from his (more recent) version of WAMP. In particular, he's using PHP 5.3, while I was on 5.2.6. As a result, I figured I should upgrade. I didn't do the uninstall/reinstall, but things seemed to be fine until I noticed WAMP was still using the 5.2.6 PHP instead of 5.3.0. When I tried to switch, it broke.

Welcome to the realities of working with development tools! WAMP broke so hard that I uninstalled, restarted, and killed it. I installed XAMPP and, after a little poking around, managed to get it running. Of course, now I'm discovering some interesting, variations... things like these two lines at the top of every page:
Code:
Warning: session_start() [function.session-start]: Cannot send session cookie - headers already sent by (output started at C:\xampp\htdocs\MyFiction\index.php:2) in C:\xampp\htdocs\MyFiction\init.php on line 2

Warning: session_start() [function.session-start]: Cannot send session cache limiter - headers already sent (output started at C:\xampp\htdocs\MyFiction\index.php:2) in C:\xampp\htdocs\MyFiction\init.php on line 2
Obviously, between upgrading to PHP 5.3.0 and a slightly different configuration file, things are different! Another difference is that XAMPP provides me with an (invalid) security certificate. After adding an exception, I'll be able to test secure and insecure connections (I'm sure you can see where this will lead).

If any of you think I'm freaking out on you or jerking you around, consider this: with any decent-sized project, you WILL have changes to your development environment, tools, etc. Those changes WILL cause you headaches! If you use a third party component, a component upgrade will mess you up. This is especially true if your buddy down the hall hasn't upgraded yet! Different servers will have different configurations, and those differences can make things interesting, especially if you have limited control over some of the differences.

I also had to recreate my database. I'll include the SQL script as an attachment for those of you who may be curious. phpMyAdmin made entering the script and running it fairly simple, so it wasn't all that bad, really.

Let's deal with the warning messages first. The first two lines of index.php were:
[highlight=php]
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<?php include("init.php");?>
[/highlight]
If I swap them the messages go away:
[highlight=php]
<?php include("init.php");?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
[/highlight]
Lesson learned: you have to set your session information before you send a single byte to the browser!

Now that I can TEST secure connections, it's time to actually do that! So far, we only have to modify a few files. First, init.php. We'll change $sroot to actually be the secure root!
[highlight=php]
<?php
session_start();

//define root URLs
$root = "http://localhost/MyFiction/";
$sroot = "https://localhost/MyFiction/";

//open database.
//This is going to store everything, so we might as well have our connection now!
[/highlight]
I'm only listing the first few lines, since the change is very local. We need to update header.php as well. The goal here is to make sure the login and join screens are over a secure connection at all times!
[highlight=php]
<div class="header">
<div class="upper left">
Welcome to MyFiction.net!
</div>
<div class="upper right">
<?php
if ($_SESSION["user"]<>"") echo "Welcome Back ".$_SESSION["user"];
elseif ($_REQUEST["page"]=="login") echo "Please Enter user name and password";
elseif ($_REQUEST["page"]=="join") echo "Thank you for joining";
else echo "<a class=\"white\" href=\"".$sroot."index.php?page=login\">login</a> or <a class=\"white\" href=\"".$sroot."index.php?page=join\">sign up</a>";
?>
</div>
</div>
[/highlight]
Unfortunately, once the connection is secure, we're slowing down traffic for the rest of the site. We'll update menu.php to avoid that problem:
[highlight=php]
<div class="menu">
<ul>
<li><a href="<?php echo $root; ?>index.php?page=home">Home</a>
<ul>
<li><a href="<?php echo $root; ?>index.php?page=profile">Profile</a></li>
<li><a href="<?php echo $root; ?>index.php?page=beta">Beta</a></li>
</ul>
</li>
<li><a href="<?php echo $root; ?>index.php?page=newfiction">New Fiction</a></li>
<li><a href="<?php echo $root; ?>index.php?page=forums">Forums</a></li>
<li><a href="<?php echo $root; ?>index.php?page=resources">Writer's Resources</a>
<ul>
<li><a href="http://www.merriam-webster.com/">Miriam Webster's</a></li>
<li><a href="http://www.wikipedia.org">Wikipedia</a></li>
<li><a href="http://www.openoffice.org">OpenOffice.org</a></li>
<li><a href="http://www.mla.org">MLA</a></li>
<li><a href="http://www.apa.org">APA</a></li>
<li><a href="http://www.google.com">Google</a></li>
</ul>
</li>
<li><a href="<?php echo $root; ?>index.php?page=search">Search</a></li>
</div>
<!--[if lt IE 7]> <div style='border: 1px solid #F7941D; background: #FEEFDA; text-align: center; clear: both; height: 75px; position: relative;'> <div style='position: absolute; right: 3px; top: 3px; font-family: courier new; font-weight: bold;'><a href='#' onclick='javascript:this.parentNode.parentNode.style.display="none"; return false;'><img src='http://www.ie6nomore.com/files/theme/ie6nomore-cornerx.jpg' style='border: none;' alt='Close this notice'/></a></div> <div style='width: 640px; margin: 0 auto; text-align: left; padding: 0; overflow: hidden; color: black;'> <div style='width: 75px; float: left;'><img src='http://www.ie6nomore.com/files/theme/ie6nomore-warning.jpg' alt='Warning!'/></div> <div style='width: 275px; float: left; font-family: Arial, sans-serif;'> <div style='font-size: 14px; font-weight: bold; margin-top: 12px;'>You are using an outdated browser</div> <div style='font-size: 12px; margin-top: 6px; line-height: 12px;'>For a better experience using this site, please upgrade to a modern web browser.</div> </div> <div style='width: 75px; float: left;'><a href='http://www.firefox.com' target='_blank'><img src='http://www.ie6nomore.com/files/theme/ie6nomore-firefox.jpg' style='border: none;' alt='Get Firefox 3.5'/></a></div> <div style='width: 75px; float: left;'><a href='http://www.browserforthebetter.com/download.html' target='_blank'><img src='http://www.ie6nomore.com/files/theme/ie6nomore-ie8.jpg' style='border: none;' alt='Get Internet Explorer 8'/></a></div> <div style='width: 73px; float: left;'><a href='http://www.apple.com/safari/download/' target='_blank'><img src='http://www.ie6nomore.com/files/theme/ie6nomore-safari.jpg' style='border: none;' alt='Get Safari 4'/></a></div> <div style='float: left;'><a href='http://www.google.com/chrome' target='_blank'><img src='http://www.ie6nomore.com/files/theme/ie6nomore-chrome.jpg' style='border: none;' alt='Get Google Chrome'/></a></div> </div> </div> <![endif]-->
[/highlight]
The result of all this is simple: we now have secure connections to our data, and can verify that the connections work. Unfortunately, join.php isn't reflecting this change. We need to ensure that join.php calls itself securely, and that we go to the secure confirmation screen to complete registration:
[highlight=php]
<?php
if ($_POST["action"]=="confirm")
{
$penname = ValString($_POST["penname"],$vsStrict,20);
$password = $_POST["password"];
$passwordconf = $_POST["passwordconf"];
$email = ValString($_POST["email"],$vsStrict,100);
$emailconf = ValString($_POST["emailconf"],$vsStrict,100);
$valid = true;
if ($penname=="")
{
$valid = false;
$error = "Sorry, your User Name cannot be blank!";
}
else if ($password!=$passwordconf)
{
$valid = false;
$error = "Sorry, your passwords did not match!";
}
else if ($password=='')
{
$valid = false;
$error = "Sorry, your password cannot be blank!";
}
else if ($email!=$emailconf)
{
$valid = false;
$error = "Sorry, your emails did not match!";
}
else if ($email=="")
{
$valid = false;
$error = "Sorry, your email cannot be blank!";
}
else if (ValString($email,$vsEmail|$vsStrict)=="")
{
$valid = false;
$error = "Sorry, you have not entered a valid email address!";
}
else
{
$sql = "SELECT COUNT(*) AS MYCOUNT FROM USERS WHERE PENNAME='".ValString($penname,$vsSQL)."'";
if (!($result = mysql_query($sql, $sourcedb)))
{
echo "DB error, could not query the database";
exit;
}
$row = mysql_fetch_assoc($result);
if ($row["MYCOUNT"] != 0)
{
$valid = false;
$error = "Sorry, that pen name is already in use!";
}
}
}
else
{
$penname="";
$password="";
$passwordconf="";
$email="";
$emailconf="";
$valid = false;
$error = "";
}
if (!$valid)
{
?>
<h3>Join</h3>
<?php echo $error ?>
<form name="application" action="<?php echo $sroot; ?>index.php" method="post">
<input type="hidden" name="page" value="join">
<input type="hidden" name="action" value="confirm">
<table>
<tr>
<td>
User Name:
</td>
<td>
<input name="penname" type="text" maxlength="30" value="<?php echo $penname ?>"></input>
</td>
</tr>
<tr>
<td>
Password:
</td>
<td>
<input name="password" type="text" maxlength="20" value="<?php echo $password ?>"></input>
</td>
</tr>
<tr>
<td>
Confirm Password:
</td>
<td>
<input name="passwordconf" type="text" maxlength="20" value="<?php echo $passwordconf ?>"></input>
</td>
</tr>
<tr>
<td>
Email:
</td>
<td>
<input name="email" type="text" maxlength="100" value="<?php echo $email ?>"></input>
</td>
</tr>
<tr>
<td>
Confirm Email:
</td>
<td>
<input name="emailconf" type="text" maxlength="100" value="<?php echo $emailconf ?>"></input>
</td>
</tr>
</table>
<input type="submit" value="Submit">
</form>
<?php
}
else
{
$encpassword=hash('sha512',$password);
$confirmation = "";
srand(time());
for ($i = 1; $i<=20; $i++)
$confirmation = $confirmation . (rand()%10);
$sql = "INSERT INTO USERS (PENNAME, PASSWORD, CONFIRMATION, EMAIL) VALUES ('".ValString($penname,$vsSQL)."','$encpassword','$confirmation','".ValString($email,$vsSQL)."')";
$result = mysql_query($sql, $sourcedb);
if ($result)
{
$message = 'Thank you for your interest in MyFiction.net.' . "\r\n" .
'Please confirm your registration at '.$sroot.'index.php?page=confirm&confirmation=' . $confirmation;
$headers = 'From: [email protected]' . "\r\n" .
'Reply-To: [email protected]' . "\r\n";
mail($email,'Thanks for joining MyFiction.net',$message,$headers)
?>
<h3>Thanks for joining!</h3>
<p>You should receive an email shortly to confirm your membership</p>
<?php
}
else
{
?>
<h3>Thanks for your interest!</h3>
<p>There was an error processing your request. Please try again later.</p>
<?php
}
}
?>
[/highlight]
As advertised, these changes haven't been too difficult. Unfortunately, waiting could have created a much more difficult situation to correct. The moral of the story is: don't wait! Make sure you have security built in at every layer!

If you're interested in downloading XAMPP or a portable Launcher, you can find it here: It's not as user friendly as WAMP, but it packs more punch!

For those who were eagerly awaiting the confirmation screen and login screen... wait 'til next time. I felt that talking about upgrades (and recovering from them) was worth the time.

And the bonus, myfiction.sql
[highlight=sql]
create database myfiction;
use myfiction;

create table USERS
(
PENNAME VARCHAR(30) NOT NULL,
EMAIL VARCHAR(100),
BIRTHDATE DATE,
PASSWORD VARCHAR(50),
CONFIRMATION VARCHAR(20),
WEBSITE VARCHAR(100),
SHOWEMAIL BOOL,
COUNTRY INT,
SHOWCOUNTRY BOOL,
ALLOWPM BOOL,
PROFILE TEXT,
AVATARNAME VARCHAR(20),
AVATARBLOB BLOB,
BETASTATUS BOOL,
BETAPROFILE TEXT,
BETARATING TINYINT,
ISMODERATOR BOOL,
ISADMIN BOOL,
LASTIP VARCHAR(20),
PRIMARY KEY (PENNAME)
);

create table STORIES
(
STORY_GUID INT,
TITLE VARCHAR(100) NOT NULL,
DESCRIPTION TEXT,
USE_CHAPTERS BOOL,
COMPLETE BOOL,
STORY_RATING INT,
STAR_GIVERS INT,
TOTAL_STARS INT,
ENABLED BOOL,
PRIMARY KEY (STORY_GUID)
);

create table CHAPTERS
(
STORY_GUID INT,
CHAPTER INTEGER,
CHAPTER_TITLE VARCHAR(100),
BODY TEXT,
WORD_COUNT INT,
PRIMARY KEY (STORY_GUID,CHAPTER)
);

create table USERINFRACTIONS
(
INFRACT_GUID INT,
PENNAME VARCHAR(30) NOT NULL,
DATE_GIVEN DATE,
GIVEN_BY VARCHAR(30) NOT NULL,
POINTS INT,
EXPIRES DATE,
REASON TEXT,
ISWARNING BOOL,
PRIMARY KEY (INFRACT_GUID)
);

create table STORYREPORT
(
REPORT_GUID INT,
STORY_GUID INT,
CHAPTER INT,
REASON TEXT,
SUBMITTED_BY VARCHAR(30),
PRIMARY KEY (REPORT_GUID)
);

create table PMREPORT
(
REPORT_GUID INT,
PM_GUID INT,
REASON TEXT,
SUBMITTED_BY VARCHAR(30),
PRIMARY KEY (REPORT_GUID)
);

create table PM
(
PM_GUID INT,
PM_CONTENT TEXT,
FROMUSER VARCHAR(30),
TOUSER VARCHAR(30),
TIME DATETIME,
ISREAD BOOL,
DELETEDBYTO BOOL,
DELETEDBYFROM BOOL,
PRIMARY KEY (PM_GUID)
);

create table BANNEDIPS
(
IPADDRESS VARCHAR(20),
PRIMARY KEY (IPADDRESS)
);

create table BANNEDEMAILS
(
EMAILADDRESS VARCHAR(100),
PRIMARY KEY (EMAILADDRESS)
);

create table CATEGORIES
(
CATEGORY VARCHAR(30) NOT NULL,
PRIMARY KEY (CATEGORY)
);

create table GENRES
(
GENRE VARCHAR(30) NOT NULL,
PRIMARY KEY (GENRE)
);

create table LANGUAGES
(
LANGUAGE VARCHAR(30) NOT NULL,
PRIMARY KEY (LANGUAGE)
);

create table FANDOMS
(
FANDOM VARCHAR(100) NOT NULL,
PRIMARY KEY (FANDOM)
);

create table CHARACTERS
(
FANDOM VARCHAR(100) NOT NULL,
FANCHAR VARCHAR(100) NOT NULL,
PRIMARY KEY (FANDOM, FANCHAR)
);

create table RATINGS
(
RATING_GUID INT,
RATING_TITLE VARCHAR(30) NOT NULL,
PRIMARY KEY (RATING_GUID)
);

create table BETACATS
(
PENNAME VARCHAR(30) NOT NULL,
CATEGORY VARCHAR(30) NOT NULL,
PRIMARY KEY (PENNAME, CATEGORY)
);

create table BETAGENRES
(
PENNAME VARCHAR(30) NOT NULL,
GENRE VARCHAR(30) NOT NULL,
PRIMARY KEY (PENNAME, GENRE)
);

create table BETAFANDOMS
(
PENNAME VARCHAR(30) NOT NULL,
FANDOM VARCHAR(100) NOT NULL,
PRIMARY KEY (PENNAME, FANDOM)
);

create table FANDOMCATS
(
FANDOM VARCHAR(100) NOT NULL,
CATEGORY VARCHAR(30) NOT NULL,
PRIMARY KEY (FANDOM, CATEGORY)
);

create table STORYAUTHORS
(
STORY_GUID INT,
PENNAME VARCHAR(30) NOT NULL,
CONFIRMED BOOL,
SUBMITTER BOOL,
PRIMARY KEY (STORY_GUID, PENNAME)
);

create table STORYBETAS
(
STORY_GUID INT,
PENNAME VARCHAR(30) NOT NULL,
CONFIRMED BOOL,
PRIMARY KEY (STORY_GUID, PENNAME)
);

create table STORYCATS
(
STORY_GUID INT,
CATEGORY VARCHAR(30) NOT NULL,
PRIMARY KEY (STORY_GUID, CATEGORY)
);

create table STORYGENRES
(
STORY_GUID INT,
GENRE VARCHAR(30) NOT NULL,
PRIMARY KEY (STORY_GUID, GENRE)
);

create table STORYFANDOMS
(
STORY_GUID INT,
FANDOM VARCHAR(100) NOT NULL,
PRIMARY KEY (STORY_GUID, FANDOM)
);

create table STORYCHARSMAJOR
(
STORY_GUID INT,
FANDOM VARCHAR(100) NOT NULL,
FANCHAR VARCHAR(100) NOT NULL,
PRIMARY KEY (STORY_GUID, FANDOM, FANCHAR)
);

create table STORYCHARSMINOR
(
STORY_GUID INT,
FANDOM VARCHAR(100) NOT NULL,
FANCHAR VARCHAR(100) NOT NULL,
PRIMARY KEY (STORY_GUID, FANDOM,FANCHAR)
);

ALTER TABLE USERS MODIFY COLUMN PASSWORD VARCHAR(128);

[/highlight]

All Credits goes to one who really made this...
 
Status
Not open for further replies.

Users who are viewing this thread

Top