This page intentionally left ugly
A programmer and technology enthusiast destroys programming and technology. Welcome to the dichotomy of my existence...
Feel free to browse the experiments and pick up anything you may find useful. Or head over to the obligatory introduction.
Few other works in the artistic world exemplifies the statement that “art flourishes under censorship”. Sergei Parajanov’s work created behind the Iron Curtain is no less than an audio-visual onslaught of poetic metaphor and vision.
Created in 1968, the film is an exploration of Parajanov’s life without the use of dialogue. The entire story is narrated in imagery and symbolism alone. It would probably take a decade or more to unravel all of the meaning. The film was suppressed in the former Soviet Union and was not widely available outside film festivals in the West.
It stars Sofiko Chiaureli in no less than six roles including that of the male poet and main character Sayat Nova, the King of Song.
Sadly, Parajanov didn’t live long enough to see his work flourish. He passed away in 1990 after a battle with lung cancer. Arguably caused by his years of incarceration in a labor camp as punishment for expressing his views.
The film had such a strong impact on the musical group Juno Ractor (known for their contributions to the Matrix trilogy soundtrack), that they fashioned a music video composed entirely of imagery from the movie.
Not quite the original film, but you get the idea of what you would be missing if you don’t watch it.
Everything esoteric to intriguing to convoluted and awe inspiring that you ever wanted to know about the former soviet bloc.
“Interesting news from Russia in English language.”
One article about the Lost City of Chernobyl is particularly stunning in regard to the images. While not nuclear related, a similarly abandoned Kadykchan, The City of Broken Dreams is equally heartfelt. A testament to a drive and ideology that simply evaporated from the lives of the people who once lived there…
Just to show that I haven’t been twiddling my thumbs all this time, I thought I’d post what I have so far on the forum script I’ve been working on. Last Monday, I posted the updated database SQL.
Now there are 12 tables in all. I forgot to include the “usersgroups” table and now there’s a “floodcheck” table as well. Originally, I planned to use a purely cookie or session based check for flooding, but these can be easily spoofed. Flood checking will now take place in the database class.
I’ve written up the basics on Database.class.php (the parent class for all database abstraction), MySQL.class.php, config.php (I’ve finalised on the presets) and index.php, but this should give you an idea of where I’m going with this.
Note: The license is MIT, but this is only because Google Code doesn’t allow the ISC license or Public Domain dedications at this time. I plan to release a separate version of the code (functionally identical to this one) without any license and with my usual disclaimer.
WARNING: This code is highly experimental and will contain omissions, exceptions and egregious coding errors. This is just a progress update, so please feel free to treat it as such.
For some reason the sourcecode formatting puts the empty() function twice as, “emptyempty”. This is a WordPress formatting issue. The plain version doesn’t have the double entry.
/**
* Core class.
* Used for core forum functions, formatting and security.
*
* @author Jayanath Rodrigo
* @package Core
* @access public
* @version 0.1
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
final class Core
{
static private $instance; // Singleton instance for this class
private $cookies; // Boolean to hold cookie capability (true/false)
private $props; // Presets array passed during object construction
private $db; // Database object passed during object construction
/**
* Core constructor.
* This is a private function. Use "getInstance" below to get a fresh object.
*
* @param array $p the presets in the core passed by reference.
* @param object $d the database object (MySQL, SQLite et al) passed by reference.
*/
private function __construct(&$p, &$d)
{
$props = $p;
$db = $d;
$cookies = $this->cookiesEnabled();
}
/**
* Singleton instance.
*
* @param array $p The presets in the core passed by reference.
* @param object $d The database object (MySQL, SQLite et al) passed by reference.
* @return object Core Singleton instance
*/
static function getInstance(&$p, &$d)
{
if(!isset(self::$instance))
self::$instance = new Core($p, $d);
return self::$instance;
}
/******************************************************************************
Posting status and user input
******************************************************************************/
/**
* Checks if user is currently browsing the index page.
*
* @return bool true If on the index page. Defaults to false.
*/
public function browsingIndex()
{
if(!isset($_GET['forum']) && !isset($_GET['topic']) && !isset($_GET['page']) && !isset($_GET['section']))
return true;
return false;
}
/**
* Checks if user is currently browsing a specific forum.
*
* @return bool true If on a forum page. Defaults to false.
*/
public function browsingForum()
{
if(isset($_GET['forum']) && !isset($_GET['topic']) && !isset($_GET['section']))
return true;
return false;
}
/**
* Checks if user is currently browsing a specific topic.
*
* @return bool true If on a topic page. Defaults to false.
*/
public function browsingTopic()
{
if(isset($_GET['forum']) && isset($_GET['topic']) && !isset($_GET['section']))
return true;
return false;
}
/**
* Checks if user is currently browsing a specific section. I.E. Plugin module.
*
* @return bool true If on a section page. Defaults to false.
*/
public function browsingSection()
{
if(!isset($_GET['forum']) && !isset($_GET['topic']) && isset($_GET['section']))
return true;
return false;
}
/**
* Gets the current page index.
*
* @return int $p Filtered output defaults to 1 (first page).
*/
public function currentPage()
{
$p = 1;
if(isset($_GET['page']))
$p = $this->getDefaultInt($_GET['page'], 1);
return $p;
}
/**
* Status: Posting a new forum topic.
*
* @return bool true If $_POST array key "forum" is present and "topic" is absent. Defaults to false.
*/
public function postingNewTopic()
{
if(isset($_POST['forum']) && !isset($_POST['topic']))
return true;
return false;
}
/**
* Status: Posting a new topic reply.
*
* @return bool true If $_POST array keys "forum" and "topic" are present. Defaults to false.
*/
public function postingNewReply()
{
if(isset($_POST['forum']) && isset($_POST['topic']))
return true;
return false;
}
/**
* Status: User is attempting to login.
*
* @return bool true If $_POST array keys "username" and "password" are present. Defaults to false.
*/
public function checkLogin()
{
if(isset($_POST['username']) && isset($_POST['password']))
return true;
return false;
}
/**
* Status: User is attempting to login.
*
* @return bool true If $_GET array keys "logout" is set and the "user" session is present. Defaults to false.
*/
public function checkLogout()
{
if(isset($_GET['logout']) && !isset($_GET['login']) && isset($_SESSION['user']))
return true;
return false;
}
/**
* Gets the currently requested forum ID.
* Priority is given to the $_POST "forum" key.
*
* @return int $f_id Filtered output defaults to 0 (no forum requested).
*/
public function postForumID()
{
$f_id = 0;
if(isset($_POST['forum']))
$f_id = $this->getDefaultInt($_POST['forum'], 0);
elseif(isset($_GET['forum']))
$f_id = $this->getDefaultInt($_GET['forum'], 0);
return $f_id;
}
/**
* Gets the currently requested topic ID.
* Priority is given to the $_POST "topic" key.
*
* @return int $t_id Filtered output defaults to 0 (no topic requested).
*/
public function postTopicID()
{
$t_id = 0;
if(isset($_POST['topic']))
$t_id = $this->getDefaultInt($_POST['topic'], 0);
elseif(isset($_GET['topic']))
$t_id = $this->getDefaultInt($_GET['topic'], 0);
return $t_id;
}
/**
* Gets the currently requested parent (topic or forum) ID.
* Priority is given to the $_POST "parent" key.
*
* @return int $p_id Filtered output defaults to 0 (no parent item requested).
*/
public function postParentID()
{
$p_id = 0;
if(isset($_GET['parent']))
$p_id = $this->getDefaultInt($_GET['parent'], 0);
elseif(isset($_GET['parent']))
$p_id = $this->getDefaultInt($_GET['parent'], 0);
return $p_id;
}
/**
* Gets the posted subject title form field data.
*
* @return string $_t Filtered output defaults to '' (blank string).
*/
public function postTitle()
{
$_t = '';
if(isset($_POST['title']))
$_t = $this->getDefaultString($_POST['title'], '');
return $_t;
}
/**
* Gets the posted content data (message body) form field data.
*
* @return string $_c Filtered output defaults to '' (blank string).
*/
public function postContent()
{
$_c = '';
if(isset($_POST['content']))
$_c = $this->getDefaultHtml($_POST['content'], '');
return $_c;
}
/**
* Gets the posted author name form field data
*
* @return string $_a Filtered output defaults to '' (blank string).
*/
public function postAuthor()
{
$_a = '';
if(isset($_POST['author']))
$_a = $this->getDefaultString($_POST['author'], '');
return $_a;
}
/**
* Gets the posted author email form field data.
*
* @return string $_e Filtered output defaults to '' (blank string).
*/
public function postEmail()
{
$_e = '';
if(isset($_POST['email']))
$_e = $this->getDefaultString($_POST['email'], '');
return $_e;
}
/**
* Gets a database compatible datetime stamp.
*
* @return date Current date time in "Year-Month-Date Hour:Minute:Second" format.
*/
public function postDate() {
return date('Y-m-d H:i:s');
}
/******************************************************************************
User status and identity
******************************************************************************/
/**
* Gets user data into variables passed by reference.
*
* @deprecated This function is deprecated in favor of the one with fewer parameters below it
* @see function getUserDataArray.
*
* @param int $userID user ID key. Defaults to 0 on error
* @param string $user username. Defaults to '' on error
* @param array $priv user privileges. Defaults to empty array on error
* @param int $msgID message ID key. Defaults to 0 on error
* @param string $exc error message text.
*/
public function getUserData(&$userID, &$user, &$priv, &$msgID, &$exc)
{
try
{
$raw = explode('::', $this->loggedInUser());
list($userID, $user, $msgID) = explode(',' $raw[0]);
$priv = $this->getPrivileges($raw[1]);
// Filter everything
$userID = $this->getDefaultInt($userID, 0);
$user = $this->getDefaultString($user, '');
$msgID = $this->getDefaultInt($msgID, 0);
}
catch (Exception $exc)
{
$userID = 0;
$user = '';
$priv = array();
$msgID = 0;
}
}
/**
* Gets user data as an array.
*
* @param array $data user data (id, name, privileges, messageID). Defaults to error message on exception.
* @return array|string Array with user data if successful or exception message as string on error.
*/
public function getUserDataArray()
{
$data = null;
try
{
$raw = explode('::', $this->loggedInUser());
// First part of "raw" (user info)
list($userID, $user, $msgID) = explode(',' $raw[0]);
$data = array(
"id" => $userID,
"username" => $this->getDefaultString($user, ''),
"messageID" = $this->getDefaultInt($msgID, 0),
"privileges" => $this->getPrivileges($raw[1]) // Second part of "raw" (privileges)
);
}
catch (Exception $exc)
{
$data = $exc;
}
return $data;
}
/**
* Saves user data into session and cookies.
*
* @deprecated This function is deprecated in favor of the one with fewer parameters below it.
* @see function setUserDataArray
*
* @param int $userID Unique user key
* @param string $name Username
* @param array $priv Designated user parameters
* @param int msgID Display message number
*/
public function setUserData(&$userID, &$user, &$msgID, &$priv)
{
$data = $this->encrypt($userID . ',' . $user . ',' . $msgID . '::' . implode('', $priv));
// Destroy and restart the current session
session_regenerate_id(true);
$_SESSION['user'] = $data;
if($cookies)
setcookie("user", $data);
}
/**
* Saves user data into session and cookie
*
* @param array $info Raw user data including id, name, message ID and privileges array
*/
public function setUserDataArray(&$info)
{
list($userID, $user, $msgID, $priv) = $info;
$data = $this->encrypt($userID . ',' . $user . ',' . $msgID . '::' . implode('', $priv));
// Destroy and restart the current session
session_regenerate_id(true);
$_SESSION['user'] = $data;
if($cookies)
setcookie("user", $data);
}
/**
* Check if user can support cookies
*
* @return bool $cookies True if cookies are enabled by the browser, false if else
*/
public function cookiesEnabled()
{
if(empty($cookies))
{
setcookie("cookies", time());
if(isset($_COOKIE["cookies"]))
{
$cookies = true;
unset($_COOKIE["cookies"]);
}
else
{
$cookies = false;
}
}
return $cookies;
}
/**
* Check if user has a valid login session or saved cookie
*
* @return string $ret Formatted string pattern containing user ID, name, display name message ID and privilege flags
*/
public function loggedInUser()
{
// Prevent hijack
if(!isset($_SESSION['user']))
session_regenerate_id();
$ret = null;
if(isset($_SESSION['user']))
$ret = $this->decrypt($_SESSION['user']);
elseif(isset($_COOKIE["user"]))
$ret = $this->decrypt($_COOKIE['user']);
else
$ret = '0,0,0::000000000000000000000';
return $ret;
}
/**
* Authenticates user based on posted username and password form fields
*
* @return bool true If authentication went without problems. Defaults to false.
*/
public function login()
{
// Make sure the current user is logged out first
if(isset($_SESSION['user']) || isset($_COOKIE['user']))
$this->logout();
if(isset($_POST['username']) && isset($_POST['password']))
{
$username = $this->getDefaultString($_POST['username'], '');
$password = $this->getDefaultString($_POST['password'], '');
// Invalid or empty form data
if(empty($username) || empty($password))
return false;
// Connect to database and get user by name
$data = $db->getLoginData($username);
// User doesn't exist
if(empty($data))
return false;
// Verify returned authentication data
$encPass = $this->decrypt($data['pass']);
$encSalt = $this->decrypt($data['salt']);
// Password matches, login the user and return true
if(str_replace($encSalt, '', $encPass) == $password)
{
$udata = array($data['id'], $username, 0, implode('', $data['priv']));
$this->setUserDataArray($udata);
return true;
}
}
return false;
}
/**
* Logout by removing 'user' session and cookie data.
*/
public function logout()
{
if(isset($_SESSION['user']))
$_SESSION['user'] = null;
if(isset($_COOKIE['user']))
unset($_COOKIE['user']);
// Just to be sure
session_regenerate_id();
}
/**
* Puts user privileges into an array
*
* @param string $priv User privilegs flags 21 characters in length (0/1)
* @return array $privileges User privileges sorted into readable array
*/
public function getPrivileges(&$priv)
{
// Initial array with no privileges
$privileges = array(
"CanReply" => 0,
"CanCreateTopics" => 0,
"CanUsePM" => 0,
"CanReplyReadOnlyForums" => 0,
"CanCreateTopicsReadOnlyForums" => 0,
"CanEditOwnPosts" => 0,
"CanDeleteOwnPosts" => 0,
"CanEditOthersPosts" => 0,
"CanDeleteOthersPosts" => 0,
"CanMoveTopics" => 0,
"CanLockTopics" => 0,
"CanCreateForums"= 0,
"CanEditForums" => 0,
"CanDeleteForums" => 0,
"CanCreateUsers"= 0,
"CanBanUsers"= 0,
"CanEditUsers"= 0,
"CanDeleteUsers"= 0,
"CanCreateGroups"= 0,
"CanEditGroups"= 0,
"CanDeleteGroups"= 0
);
// Get array from privileges string
$pr = explode('', $priv);
// If the initial array has appropriate number of flags
if(count($pr) == 21) {
// Get secured values back to privileges from source array
$this->filterPushToArray("int", $pr, $privileges);
}
return $privileges;
}
/**
* Utility function to reset default values in an array with filtered data.
* Arrays are passed by reference.
*
* @param string $type Content type to return. Values "html", "string", "int".
* @param int $count The number of items to iterate through.
* @param array $source The raw array containing unfiltered data.
* @param array $source Sorted destination array with filtered data.
*/
public function filterPushToArray($type, $count, &$source, &$dest)
{
$type = strtolower($type);
$i = 0;
// Iterate through each key and insert corresponding source array value
foreach($dest as $key => $value)
{
// Get filtered content based on type
switch($type)
{
case "html":
$dest[$key] = $this->getDefaultHtml($source[i], $value);
break;
case "string":
$dest[$key] = $this->getDefaultString($source[i], $value);
break;
case "int":
$dest[$key] = $this->getDefaultInt($source[i], $value);
break;
}
$i++;
}
}
/******************************************************************************
Formatting and presentation functions
******************************************************************************/
/**
* Converts topic titles into usable URLs.
* Will be used along with IDs.
*
* @param string $str The raw title
* @return string $str The cleaned up title with special characters removed
*/
public function titleToSlug($str)
{
$str = preg_replace('/[~`!\@#\$\%\^\&\*\(\)\-\_\+={}\[\]\|:;\"\'\< \>\?,.\\\/\s+]/imu', '', $str);
return strtolower($str);
}
/**
* Check if CAPS are below a percent threshold.
* This function is for improving readability by enforcing caps limits.
*
* @param string $str The posted content
* @param int $limit The threshold limit in percent
* @return bool true If the amout of CAPS matches are below the limit, else returns false.
*/
public function capsCheck($str, $limit)
{
$percent = round(($limit / 100) * strlen($str));
preg_match_all('/[A-Z]/', $str, $matches);
return ((count($matches[0]) >= $percent) ? true : false);
}
/**
* Truncates strings to specified limit and return with ellipse
* This function is for improving readability by limiting titles or summary lengths.
*
* @param string $str The posted content
* @param int $limit The threshold limit in number of maximum characters. Default is 100.
* @return string $str Formatted text limited to specified length and followed by '...' .
*/
public function stringTrunc($str, $limit = 100)
{
if (strlen($str) < = $limit)
return $str;
return substr_replace($string, '...', ($limit - strlen($str)));
}
/******************************************************************************
Url authentication and flood limit
******************************************************************************/
/**
* Verify a certain amount of time has passed between requests
* Prevents abuse by checking for flood/DoS or forced entry attempts.
*
* @deprecated This function is deprecated in favor of a purely database based approach
*
* @param int $limit The threshold limit in time format.
* @return bool true If sufficient time has passed between requests or false if it has not.
*/
public function floodLimit($limit)
{
$req = $this->postDate();
if(isset($_SESSION['req']) || isset($_COOKIE['req']))
{
$sreq = (isset($_SESSION['req']))? strtotime($_SESSION['req']) : $req;
$creq = (isset($_COOKIE['req']))? strtotime($_COOKIE['req']) : $req;
// Compare and get the most recent time
if($sreq < $creq)
$sreq = $creq;
return ($req < ($sreq + strtotime($limit)));
}
$_SESSION['req'] = $req;
setcookie('req', $req);
return false;
}
/**
* Authenticate the requested URL as originating from the site homepage
* Prevents outside redirect attempts by comparing the source and destination URLs (site_url)
*
* @param string $req The raw requested URL
* @return bool true If the source and destination URLs match or false if they dont
*/
public function verifyURL($req)
{
if(strtolower($req) == substr(0, (strlen(strtolower($props['site_url'])) - 1)))
return true;
return false;
}
/*****************************************************************************************************
Sanitized input
*****************************************************************************************************/
/**
* Allow only text. HTML escaped if necessary, output unicode.
* Prevents malicious or accidental HTML tags from being posted. Formatted strings only.
*
* @param string $v The content to be checked.
* @return string $v|$d Verified and formatted content as $v or default, $d, if $v is invalid or empty.
*/
public function getDefaultString($v, $d)
{
$v = (empty($v) && (strtolower($v) != 'false') && ($v != '0'))? $d : $v;
return htmlentities(iconv('UTF-8', 'UTF-8', $v), ENT_QUOTES, 'UTF-8');
}
/**
* Allow only numbers, specifically, integers. No strings and/or HTML allowed.
* Prevents any value other than an integer from being sent.
*
* @param string $v The content to be checked.
* @return int $v|$d Verified $v if it is an integer or default, $d, if $v is invalid.
*/
public function getDefaultInt($v, $d)
{
return (!ctype_digit($v))? $d : $v;
}
/**
* Allow only safe HTML (tags, attributes and attribute values).
* Prevents any value other than those in the presets whitelists from being sent back.
*
* This function needs more work!
*
* @param string $v The content to be checked.
* @return string $v|$d The filtered and formatted allowed HTML as $v or default fallback value, $d, if $v is empty or cannot be parsed.
*/
public function getDefaultHtml($v, $d)
{
// Check if the content isn't empty or we skip all the filtering
$v = (empty($v) && (strtolower($v) != 'false') && ($v != '0'))? $d : $v;
// Content passed empty check
if($v != $d)
{
// Get disallowed attributes from presets
$badattr = implode('|', $props['ui_format_invalid_attributes']);
// Get formatting whitelist
$allowed = implode('|', $props['ui_format_tags']);
// Remove all tags not in the formatting whitelist first, but leave the content inside
$v = preg_replace("!<\s*?(" . $allowed . ").*?>((.*?))?!ismu", "\3", $v);
// Iterate through each allowed tag
foreach($props['ui_format_tags'] as $tag)
{
// Get matched tags in the content
preg_match_all("< \s*?" . $tag[0] . "([^>]+)>!ismu", $v, $matches);
// Allowed tags are present
if(count($matches[0] > 0))
{
// Iterate through each match and filter attributes.
for($i=0; $i < count($matches[0]); $i++)
{
// Get the specified attributes for this tag (The "([^>]+)" part in the above regular expression)
$attr = explode(' ', $matches[1][$i]);
// Filtered replacement
$repl = "";
// Each allowed attribute for this specific tag
for($j=0; $j < count($attr); $j++)
{
$at = strtolower($attr[$j]);
$atm = explode(',', $tag[1]);
// Iterate through each found attribute to see if it's in the allowed list for this tag
// Ignore bad attribute values (I.E. onclick inside "style" attribute)
for($k=0; $k < count(atm); $k++)
{
if(preg_match("!^" . atm[k] . "=\"?[^(" . $badattr . ")]+\"?!ismu", $at))
$repl .= " " . $at;
}
}
// Replace the old tag and attributes with clean ones
$v = str_replace($matches[0][$i], "<". $tag[0] . $repl . ">", $v);
}
}
}
}
return $v;
}
/*****************************************************************************************************
Security and encryption
*****************************************************************************************************/
/**
* Generate a random string with a specific range (for salts etc...)
*
* @param int $min The minimum generated string length
* @param int $max The maximum generated string length
* @param bool $upper Uppercase (capital) letters are required. Defaults to true.
* @param bool $special Special characters (punctuation etc...) are required. Defaults to true.
* @return string $random Generated random string based on specifications.
*/
public function genRandom($min, $max, $upper = true, $spec = true)
{
$charset = "0123456789abcdefghijklmnopqrstuvwxyz";
if($upper)
$charset .= "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
if($spec)
$charset .= "~!@#$%^&*()_+`-={}|\\]?[\":;'><,./";
$l = mt_rand($min, $max);
for($i = 0; $i<$l; $i++) {
$random .= $charset[(mt_rand(0, (strlen($charset)-1)))];
}
return $random;
}
/**
* Encrypt in Rijndael 256 with optional Base64 encoding.
* If Base64 is used during encryption, it must be used again for decryption.
*
* @param string $text The content to be encrypted.
* @param bool $base64 Optional flag for base64 encoding the the final output. Defaults to true.
* @return string Encrypted and optionally encoded string
*/
public function encrypt($text, $base64=true)
{
if($base64)
return trim(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $salt, $text, MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND))));
return trim(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $salt, $text, MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND)));
}
/**
* Decrypt as encrypted above in Rijndael 256 with optional Base64 decoding (Default).
* If Base64 is used to encrypt, it must be used again for decryption.
*
* @param string $text The content to be decrypted.
* @param bool $base64 Optional flag for base64 decoding the input first. Defaults to true.
* @return string Decrypted content
*/
public function decrypt($text, $base64=true)
{
if($base64)
return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $salt, base64_decode($text), MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND)));
return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $salt, $text, MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND)));
}
/*****************************************************************************************************
Software downloading and updating
*****************************************************************************************************/
/**
* Download a specified file to specified destination.
* Used for updating the software or getting special content from a remote server.
* If PHP has been compiled with cURL, it will use that. If not, it will try fopen.
*
* @param string $url Download source location
* @param string $dest Download destination (must be a writable folder)
* @return bool $ret True If everything went OK or false if something went wrong.
*/
public function getFile($url, $dest)
{
$ret = false;
$out = '';
try
{
if(function_exists('curl_init'))
{
// If cURL exists, we can use it
$file = curl_init($url);
curl_setopt($file, CURLOPT_CONNECTTIMEOUT, 3);
curl_setopt($file, CURLOPT_RETURNTRANSFER, true);
curl_setopt($file, CURLOPT_BINARYTRANSFER, true);
$out = curl_exec($file);
// After everything is read, close cURL
curl_close($file);
// Empty if something went wrong
if(empty($out))
$ret = false;
$fo = fopen($dest, 'w');
fwrite($fo, $file);
fclose($fo);
}
else
{
// If cURL doesn't exist, we have to stick to fopen
$file = fopen($url, 'rb');
// Buffer the read file
while(!feof($file))
{
$out .= fread($file, 8192);
}
// Empty if something went wrong
if(empty($out))
$ret = false;
fclose($file);
$fo = fopen($dest, 'w');
fwrite($fo, $out);
fclose($fo);
}
// If the newly created file exists, return true
if(file_exists($dest))
$ret = true;
}
catch
{
// If something went wrong, return false
$ret = false;
}
return $ret;
}
/**
* Extract downloaded files to specified directories.
* Obviously incomplete...
*/
public function pkgExtract($file, $dest)
{
if(function_exists('zip_open'))
{
// Set working path
$path = $props['site_upload_directory'] . 'tmp';
// Extract the file
$pkg = zip_open($path, $file);
// Opening successful
if(is_resource($pkg))
{
while($item = zip_read($pkg))
{
$cpath = $path . dirname(zip_entry_name($item));
$cname = $path . zip_entry_name($item);
}
}
}
}
}
But what the hey! We’re only part of this after party until the cows come home… and then we drop dead of some incurable circumstance. I.E. Plane crash, bed sores or boredom.
I’ll be mourning National Blog Posting Month for all time; or the lifespan of me or blogging, whichever comes first. Incidentally, this is the exact length of time this particular event will be celebrated by everyone else. I’ve never heard of it before either, but Heather, helpfully clarifies in the comments, it isn’t a scheduled event or even an event, really.
For some reason it reminds me of Valentine’s Day… Marketese for Buy Jewels, Chocolates, Candy and Cards or You’re a Bad Boyfriend Day. After all, nothing exemplifies the immaterial love between two souls like material tokens.
V-Day: It’s all substance! And that substance weighs as much as nothing.
Just when you thought the collective spew, the spiritual diarrhea of the masses, holds no match in terms of odor nor texture of me, me, me, me, me all day every day by everyone. And not a particularly interesting me, me, me, me, me either.
So I mourn the start this of this new holiday with a moment of silence…
Update July 3rd
Well, it looks like Heather won’t approve my last comment. Other comments posted hours later are already visible.
Can’t say I blame her completely since it is a bit of a buzz kill. But I think it’s legitimate criticism of a non event when the majority of blogs exibit the same degree of shallowness reflected in the comment.
Oh well… Here it is for anyone else…
Update July 6rd
Surprise! My comment went up after all…
Good too see it will be received with tounge firmly in cheek ;)
I.E. What Wikipedia is not…
Why do it?
Because if scientists are looking for a unified theory of everything, why don’t we create a compendium of all knowledge?
Besides this, Wikipedia has seen as string of deleted articles (of disputable noteworthiness) which are summarily overriden if restored by admins or other members with greater standing. Anonymous contributions, while still significant, aren’t valued as much. This wasn’t the case just a few years ago, where a significant portion of all edits came from anonymous contributors. Better yet, those were the days where individuals were willing to contribute without personal recognition, save for an IP address in the article history.
Ironically, Wikipedia’s own popularity is making anonymous contributions, it’s original backbone, cumbersome. This is tragic, since I’ve only bothered to login to Wikipedia just a few times. And if I’m not doing it, I know legions of others must be the same.
By the same token of what Wikipedia is, if it’s not a “Community”, Democracy, bureaucracy, battleground, anarchy nor my web host, then what it is, isn’t nearly good enough.
Wikipedia is not “free”
The collection commands a high price as the entire project is not knowledge for the sake of knowledge. It’s knowledge for the sake of recognition and self praise, often, at the cost of anonymous contributors.
While I can’t fault Wikipedia alone for this, by contributing and encouraging contribution to this project, we have, perhaps irrevocably, bound many aspects of knowledge to an intellectual prison.
Knowledge, Information, Data or what ever else you want to call it, isn’t “free” until you have unbound the hands of the user, contributor and distributor. Your opinion of what entails “freedom” does not diminish the true definition of the word.
Wikipedia is a religion
People obsess over its nuances as to its place in references. It contains innumerable contradictions (a byproduct of many cooks spoiling the broth etc…), theories as to its worth in a free (as in to criticism as well as praise) society. While these by themselves wouldn’t purely constitute religion, the arrogance of certain admins as to what knowledge is significant (especially when it comes to pet subjects) is almost insufferable.
And it has become ridiculous.
There are many such instances where the notability of the subject has not changed at all since its inception, only the number of instances it is used which shouldn’t have been a measure of notability as, by Wikipedia’s own admission, notability isn’t a function of popularity.
The article on bbPress, for example, was deleted as not being notable. Yet here it is now, created and contributed to. And I wouldn’t be surprised if it’s deleted and recreated several times more.
Behold the non-Anarchy, non-Bureaucratic and non-Democratic process at work.
Further proof that it’s a religion: Wikipedia allows non-sourced content all the time. Yet only deletes those articles which draw attention. Like the priest who swats away legitimate criticism for fear of debasing the whole premise, admins and plenty of users are more than willing to stick a banner questioning notability and walk away.
“Being bold” may result in countless reverts. Since “consensus through discussion” is double speak for a plutocracy that ridicules dissent by removing knowledge wholesale.
Looks like religion to me.
What Wikipedia could have been…
An archive of knowledge that should belong to none; that should simply exist as knowledge. The accumulated net worth of a species.
If each person contributed everything they know about any chosen topic to the universal collection of knowledge, the benefit for humanity would be almost incalculable. But let’s not lament on that now.
Let’s begin now– it’s not too late — to pick up the mantle to a truly free fountain of knowledge. An archive of everything.
There is an origin to a project that could be very promising in this vein.



