<?php
/****************************************************************************
 * DRBPoll
 * http://www.dbscripts.net/poll/
 * 
 * Copyright © 2007-2010 Don B 
 ****************************************************************************/

$PREVENT_DUPLICATE_VOTES = TRUE;
$SHOW_COUNTS = TRUE;
$vote_fp = NULL;

require_once(dirname(__FILE__) . '/config.php');

function show_vote_control($poll_id) {
	global $POLL_URL;
	global $VOTE_STRING;
	global $VOTE_PARAM_NAME;
	global $VOTE_LIST_DEFAULT_LABEL;
	global $VALID_POLLS;
	global $POLL_ID_PARAM_NAME;
	global $SUBMIT_BUTTON_STRING;
	global $VIEW_RESULTS_STRING;
	
	// Validate parameters
	if(!is_valid_poll_id($poll_id)) {
		vote_die('ERROR: An invalid poll ID was submitted.');
	}
	
	$poll = $VALID_POLLS[$poll_id];

	// Output vote control
	echo("<div class=\"voteContainer\">\r\n");
	echo("\t<form class=\"vote\" method=\"post\" action=\"" . $POLL_URL . "vote.php\">\r\n");
	echo("\t<fieldset>\r\n");
	if(!empty($poll->legend) && sizeof($poll->legend) > 0) {
		echo("\t<legend>" . htmlspecialchars($poll->legend) . "</legend>\r\n");
	}
	echo("\t<p class=\"question\">\r\n");
	echo("\t\t" . htmlspecialchars($poll->question) . "\r\n");
	echo("\t</p>\r\n\t<p>\r\n");
	
	global $CONTROL_RADIOBUTTONS;
	global $CONTROL_COMBOBOX;
	if($poll->control_type === $CONTROL_RADIOBUTTONS) {
		
		// Iterate through poll values	
		foreach ($poll->values as $value_id => $description) {
			echo("\t\t<input type=\"radio\" name=\"" . htmlspecialchars($VOTE_PARAM_NAME) .
				"\" value=\"" . htmlspecialchars($value_id) . "\" /> ");
			if( isset($poll->urls[$value_id]) && !empty($poll->urls[$value_id]) ) {
				echo("<a href=\""  . htmlspecialchars($poll->urls[$value_id]) 
				. "\" target=\"_blank\" rel=\"nofollow\">" 
				. htmlspecialchars($description) . "</a>");
			} else {
				echo(htmlspecialchars($description));
			}
			echo("<br />\r\n");
		}
		
		echo("\t</p>\r\n\t<p>\r\n");
		
	} else if($poll->control_type === $CONTROL_COMBOBOX) {
		
		echo("\t\t" . htmlspecialchars($VOTE_STRING) . "\r\n");
	
		echo("\t\t<select name=\"" . htmlspecialchars($VOTE_PARAM_NAME) . "\">\r\n");
		echo("\t\t\t<option value=\"\">" . htmlspecialchars($VOTE_LIST_DEFAULT_LABEL) . "</option>\r\n");
	
		// Iterate through poll values	
		foreach ($poll->values as $value_id => $description) {
			echo("\t\t\t<option value=\"" . htmlspecialchars($value_id) . "\">");
			echo(htmlspecialchars($description));
			echo("</option>\r\n");
		}
		echo("\t\t</select>\r\n");
		
	} else {
		
		vote_die('ERROR: Invalid control type.');
		
	}
	echo("\t\t<input type=\"hidden\" name=\"" . htmlspecialchars($POLL_ID_PARAM_NAME) . "\" value=\"" . htmlspecialchars($poll_id) . "\" />\r\n");
	//echo("\t\t<input type=\"submit\" value=\"" . htmlspecialchars($SUBMIT_BUTTON_STRING) . "\" class=\"submit\" />\r\n");
	echo("\t</p>\r\n\t<p class=\"currentResults\">\r\n");
	
	// Show results link
	echo("\t\t<a href=\"" . $POLL_URL . "results.php?" . htmlspecialchars($POLL_ID_PARAM_NAME) . "=" . htmlspecialchars($poll_id) . "\">" . htmlspecialchars($VIEW_RESULTS_STRING) . "</a>\r\n");
	
	echo("\t</p>\r\n");
	echo("\t</fieldset>\r\n");
	echo("\t</form>\r\n");
	echo("</div>\r\n");
	
}

function show_poll_results($poll_id) {
	
	// Validate parameters
	if(!is_valid_poll_id($poll_id)) {
		vote_die('ERROR: An invalid poll ID was submitted.');
	}
	
	// Get poll object
	global $VALID_POLLS;
	$poll = $VALID_POLLS[$poll_id];
	
	// Get vote summary
	$summarylist = vote_summary_list($poll_id);
	if($summarylist === FALSE) {
		// If the summary list is missing, attempt to regenerate it
		if(regenerate_vote_summary($poll_id))
			$summarylist = vote_summary_list($poll_id);
	}
	if($summarylist !== FALSE) {
		$totalVotes = $summarylist[0];
		$largest_count = find_largest_vote_value_count($summarylist);
	} else {
		$totalVotes = 0;
		$largest_count = 0;
	}
	
	// Show question
	echo("\t<p class=\"question\">\r\n");
	echo("\t\t" . htmlspecialchars($poll->question) . "\r\n");
	echo("\t</p>\r\n");
	
	// Start table for results
	echo("<table class=\"pollTable\">\r\n");
	
	// Iterate through poll values
	$barNumber = 1;
	global $MAX_POLL_BAR_WIDTH;
	global $SHOW_COUNTS;
	foreach($poll->values as $value_id => $description) {
		
		// Find vote count for this value
		$summary_row = find_vote_value_summary($value_id, $summarylist);
		
		if($summary_row === FALSE) {
			$count = 0;
		} else {
			$count = $summary_row[1];
		}
		
		$percentage = (($totalVotes > 0)?($count / $totalVotes):0);
		$bar_percentage = (($largest_count > 0)?($count / $largest_count):0);
		echo("\t<tr>");
		echo("<td class=\"pollDescriptionCell\">");
		if( isset($poll->urls[$value_id]) && !empty($poll->urls[$value_id]) ) {
			echo("<a href=\""  . htmlspecialchars($poll->urls[$value_id]) 
			. "\" target=\"_blank\" rel=\"nofollow\">" 
			. htmlspecialchars($description) . "</a>");
		} else {
			echo(htmlspecialchars($description));
		}
		echo("</td>");
		echo("<td class=\"pollBarCell\">");
		if($count > 0) {
			echo("<div class=\"pollBar\" id=\"pollBar" . ($barNumber++) . "\" style=\"width:" . round($MAX_POLL_BAR_WIDTH * $bar_percentage, 0) . "px;\"></div>");
		}
		echo("</td>");
		echo("<td class=\"pollCountCell\">" . (($SHOW_COUNTS === TRUE)?$count . " (":"") . (($count > 0)?round($percentage * 100, 2):0) . "%" . (($SHOW_COUNTS === TRUE)?")":"") . "</td>");
		echo("</tr>\r\n");
		
	}
	
	echo("</table>");
	
	if($SHOW_COUNTS === TRUE) {
		global $NUMBER_OF_VOTES_STRING;
		$numberOfVotesString = sprintf($NUMBER_OF_VOTES_STRING, $totalVotes);
		echo("<p>" . htmlspecialchars($numberOfVotesString) . "</p>");
	}

}

function the_current_poll_results() {
	global $requested_poll_id;
	vote_lock($requested_poll_id, LOCK_SH);
	show_poll_results($requested_poll_id);
	vote_unlock();
}

function the_return_to_url() {
	global $requested_poll_id;
	global $VALID_POLLS;
	$poll = $VALID_POLLS[$requested_poll_id];
	
	if(!empty($poll->returnToURL)) {
		echo $poll->returnToURL;
	} else {
		vote_die("ERROR: Return to URL not defined for this poll.");
	}
	
}

function smarter_is_int($val) {
    return (is_numeric($val)?intval($val)==$val:FALSE);
}

function is_valid_poll_id($poll_id) {
	global $VALID_POLLS;
	return (!empty($poll_id) && preg_match('/^[a-zA-Z0-9]+$/D', $poll_id) === 1 && array_key_exists($poll_id, $VALID_POLLS));
}

function is_valid_vote($poll, $vote_value_id) {
	return (!empty($vote_value_id) && preg_match('/^[a-zA-Z0-9]+$/D', $vote_value_id) === 1 && array_key_exists($vote_value_id, $poll->values));
}

function get_vote_count($summarylist) {

	// Get vote count from summary
 	if($summarylist === FALSE || count($summarylist) < 1) {
 		return 0;
 	} else {
 		return $summarylist[0];
 	}
	
}

function add_new_vote($poll_id, $vote_value_id) {
	global $vote_error_message;
	
	// Make sure vote wasn't left blank
	if(empty($vote_value_id)) {
		global $NO_VOTE_SELECTED_ERROR_MSG;
		$vote_error_message = $NO_VOTE_SELECTED_ERROR_MSG;
		return FALSE;
	}
	
	// Validate poll ID
	if(!is_valid_poll_id($poll_id)) {
		vote_die('ERROR: An invalid poll ID was submitted.');
	}
	
	// Get poll object
	global $VALID_POLLS;
	$poll = $VALID_POLLS[$poll_id];
	
	// Validate vote value ID
	if(!is_valid_vote($poll, $vote_value_id) ) {
		vote_die('ERROR: An invalid vote was submitted.');
	}
	
	// Lock
	vote_lock($poll_id, LOCK_EX);
	
	$range_low = ip2long("100.100.100.100");
	$range_high = ip2long("200.200.200.200");
	
	// Check for duplicate vote attempt
	$ipaddress = ip2long($_SERVER['REMOTE_ADDR']);
	global $PREVENT_DUPLICATE_VOTES;
	if($PREVENT_DUPLICATE_VOTES && has_voted($poll_id, $ipaddress)) {
		global $DUPLICATE_VOTE_ERROR_MSG;
		$vote_error_message = $DUPLICATE_VOTE_ERROR_MSG;
		vote_unlock();
		return FALSE;
	}
	
	// Add vote
	vote_history_add($poll_id, $ipaddress, $vote_value_id);
	vote_summary_add($poll_id, $vote_value_id);

	// Unlock
	vote_unlock();
	
	return TRUE;
}

function vote_summary_file_path($id) {
 	return dirname(__FILE__) . '/data/summary_' . $id . '.dat';
}

function vote_history_file_path($id) {
	return dirname(__FILE__) . '/data/history_' . $id . '.dat';
}

function vote_lock_file_path($id) {
	return dirname(__FILE__) . '/data/lock_' . $id . '.dat';
}

function vote_summary_list($poll_id) {

	// Load existing
	$summarylist = @file(vote_summary_file_path($poll_id));
	if($summarylist !== FALSE) {
		$summarylist = array_map("trim", $summarylist);
	}
	return $summarylist;

}

function find_vote_value_summary($vote_value_id, $summarylist) {
	
	if($summarylist === FALSE) return FALSE;
	for($i = 1; $i < sizeof($summarylist); $i++) {
		
		$summary_row = explode_history($summarylist[$i]);
		if($summary_row[0] === ("" . $vote_value_id)) {
			return $summary_row;
		}
		
	}
	return FALSE;
	
}

function find_largest_vote_value_count($summarylist) {
	
	$largest_count = 0;
	for($i = 1; $i < sizeof($summarylist); $i++) {
		
		$summary_row = explode_history($summarylist[$i]);
		if($summary_row[1] > $largest_count) {
			$largest_count = $summary_row[1]; 
		}
		
	}
	return $largest_count;
	
}

function vote_die($msg) {
	vote_unlock();
	die($msg);
}

function vote_lock($poll_id, $operation) {
	global $vote_fp;
	if($vote_fp !== NULL) {
		vote_die("Already locked for vote");
	}
	$vote_fp = @fopen(vote_lock_file_path($poll_id), 'a');
	if($vote_fp === FALSE) die("Unable to open lock file");
	if(@flock($vote_fp, $operation) === FALSE) {
		@fclose($vote_fp);
		die("Unable to lock for vote");
	}
	return;
}

function vote_unlock() {
	global $vote_fp;
	if($vote_fp !== NULL) {
		@flock($vote_fp, LOCK_UN);
		@fclose($vote_fp);
		$vote_fp = NULL;
	}
}


function vote_summary_add($poll_id, $vote_value_id) {

	// Get existing poll summary
	$summarylist = vote_summary_list($poll_id);
	$vote_summary_file_path = vote_summary_file_path($poll_id);
	
	// Create summary file if it doesn't exist
	if(!file_exists($vote_summary_file_path)) {
		if(@touch($vote_summary_file_path) === FALSE) {
			vote_die("Unable to create summary file");
		}
	}
	
	// Open summary file
 	$summary_fp = @fopen($vote_summary_file_path, "r+");
 	if($summary_fp === FALSE) {
 		vote_die("Unable to open summary file for writing");
 	}
 	if(@ftruncate($summary_fp, 0) === FALSE) {
 		@fclose($summary_fp);
 		vote_die("Unable to truncate summary file");
 	}
 	
 	// Update total vote count
 	if($summarylist === FALSE || count($summarylist) < 1) {
 		$count = 1;
 	} else {
 		$count = $summarylist[0] + 1;
 	}
 	fputs($summary_fp, $count . "\n");

	// Add vote to value total
	if($summarylist === FALSE) {
		
		// First vote
		$out = $vote_value_id . "|1";
		fputs($summary_fp, $out . "\n");

	} else {
 	
 		// Iterate through existing vote values
 		$vote_counted = FALSE;
 		for($i = 1; $i < sizeof($summarylist); $i++) {
 			
 			$summary_row = explode_history($summarylist[$i]);
 			if($summary_row[0] === $vote_value_id) {
 				
 				// Increase vote count for this value
 				$summary_row[1] += 1;
 				$vote_counted = TRUE;
 				
 			}
 			
 			// Write out new vote count for this id
 			$out = implode("|", $summary_row);
 			fputs($summary_fp, $out . "\n");
 			
 		}

 		if($vote_counted === FALSE) {
 			
 			// This is the first vote for this value
 			$out = $vote_value_id . "|1";
 			fputs($summary_fp, $out . "\n");
 			
 		}
 		
 		
 	}
 	
 	fclose($summary_fp);
	
}

function vote_history_add($poll_id, $ipaddress, $vote_value_id) {
	
	// Open/create history file
 	$history_fp = @fopen(vote_history_file_path($poll_id), "a");
 	if($history_fp === FALSE) {
 		vote_die("Unable to open history file for writing");
 	}
 	
 	// Add IP address and vote to history
 	fputs($history_fp, $ipaddress . "|" . $vote_value_id . "\n");
 	fclose($history_fp);
 	
}

function explode_history($line) {
	return array_map("trim", explode("|", $line));
}

function has_voted($poll_id, $ipaddress) {
	
	$summary_fp = @fopen(vote_history_file_path($poll_id), "r");
	if($summary_fp === FALSE) return FALSE;

	$ipaddress = trim($ipaddress);
	while(!@feof($summary_fp)) {
		$summary_line = @fgets($summary_fp);
		if(empty($summary_line)) continue;
		$summary = explode_history($summary_line);
		if($summary[0] == $ipaddress) {
			@fclose($summary_fp);
			return TRUE;
		}
	
	}
	@fclose($summary_fp);
	
	return FALSE;

}

function regenerate_vote_summary($poll_id) {

	// Find vote history file
	$history_fp = @fopen(vote_history_file_path($poll_id), "r");
	if($history_fp === FALSE) return FALSE;

	// Read votes into summary array
	$count = 0;
	$votes = array();
	while(!@feof($history_fp)) {
		$history_line = @fgets($history_fp);
		if(empty($history_line)) continue;
		$history = explode_history($history_line);
		
		// Add to summary array
		$count++;
		$vote_value_id = $history[1];
		if(isset($votes[$vote_value_id])) $votes[$vote_value_id]++;
		else $votes[$vote_value_id] = 1;
		
	}
	@fclose($history_fp);
	
	// Open summary file
 	$summary_fp = @fopen(vote_summary_file_path($poll_id), "a");
 	if($summary_fp === FALSE) {
 		vote_die("Unable to open summary file for writing");
 	}
 	if(@ftruncate($summary_fp, 0) === FALSE) {
 		@fclose($summary_fp);
 		vote_die("Unable to truncate summary file");
 	}
 	
 	// Regenerate summary based on counts from history in summary array
 	fputs($summary_fp, $count . "\n"); // Total count
 	foreach($votes as $vote_id => $value) {
 		fputs($summary_fp, $vote_id . "|" . $value . "\n");
 	}
 	@fclose($summary_fp);

	return TRUE;
}

/* DO NOT REMOVE OR HIDE THE CREDIT BELOW, PER LICENSE! */
function the_credits() {
	$line = "";
	echo(base64_decode($line) . "\n");
}
/* END CREDIT */

?>
