summaryrefslogtreecommitdiffstats
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/captcha/COLLEGE.ttfbin0 -> 5896 bytes
-rw-r--r--modules/captcha/captcha.php303
-rw-r--r--modules/id3/getid3/getid3.php140
-rw-r--r--modules/id3/getid3/module.audio-video.asf.php9
-rw-r--r--modules/id3/getid3/module.audio-video.flv.php210
-rw-r--r--modules/id3/getid3/module.audio-video.riff.php4
-rw-r--r--modules/id3/getid3/module.audio.mp3.php33
-rw-r--r--modules/id3/getid3/module.tag.id3v2.php88
-rw-r--r--modules/id3/getid3/write.id3v2.php1
-rw-r--r--modules/id3/getid3/write.php8
-rw-r--r--modules/id3/getid3/write.real.php251
-rw-r--r--modules/init.php4
-rw-r--r--modules/validatemail/validateEmail.php638
-rw-r--r--modules/validatemail/validateEmailFormat.php219
14 files changed, 1730 insertions, 178 deletions
diff --git a/modules/captcha/COLLEGE.ttf b/modules/captcha/COLLEGE.ttf
new file mode 100644
index 00000000..400fe5ce
--- /dev/null
+++ b/modules/captcha/COLLEGE.ttf
Binary files differ
diff --git a/modules/captcha/captcha.php b/modules/captcha/captcha.php
new file mode 100644
index 00000000..039d1907
--- /dev/null
+++ b/modules/captcha/captcha.php
@@ -0,0 +1,303 @@
+<?php
+/*
+ Does emit a CAPTCHA graphic and form fields, which allows to tell real
+ people from bots.
+ Though a textual description is generated as well, this sort of access
+ restriction will knock out visually impaired users, and frustrate all
+ others anyhow. Therefore this should only be used as last resort for
+ defending against spambots. Because of the readable text and the used
+ colorspaces this is a weak implementation, not completely OCR-secure.
+
+ captcha::form() will return a html string to be inserted into textarea/
+ [save] <forms> and alike. User input is veryfied with captcha::check().
+ You should leave the sample COLLEGE.ttf next to this script, else you
+ have to define the _FONT_DIR constant correctly. Use only one type.
+
+ Includes a sluggish workaround for Internet Explorer; but this script
+ must reside in a www-accessible directory then.
+ Public Domain, available via http://freshmeat.net/p/captchaphp
+*/
+
+
+#-- config
+define("EWIKI_FONT_DIR", dirname(__FILE__)); // which fonts to use
+define("CAPTCHA_INVERSE", 0); // white or black(=1)
+define("CAPTCHA_TIMEOUT", 5000); // in seconds (=max 4 hours)
+
+
+/* static - (you could instantiate it, but...) */
+class captcha {
+
+
+ /* gets parameter from $_REQUEST[] array (POST vars) and so can
+ verify input, @returns boolean
+ */
+ function check() {
+ if (($hash = $_REQUEST["captcha_hash"])
+ and ($pw = trim($_REQUEST["captcha_input"]))) {
+ return((captcha::hash($pw)==$hash) || (captcha::hash($pw,-1)==$hash));
+ }
+ }
+
+
+ /* yields <input> fields html string (no complete form), with captcha
+ image already embedded as data:-URI
+ */
+ function form($title="&rarr; retype that here", $more="<small><br>Enter the correct letters and numbers from the image into the text box. <br>This small test serves as access restriction against malicious bots. <br>Simply reload the page if this graphic is too hard to read.</small>") {
+ $pw = captcha::mkpass();
+ $hash = captcha::hash($pw);
+// $maxsize = (strpos("MSIE", $_SERVER["HTTP_USER_AGENT"]) ? 1000 : 6000);
+ $maxsize = 100;
+ @header("Vary: User-Agent");
+ $img = "data:image/jpeg;base64,"
+ . base64_encode(captcha::image($pw, 200, 60, CAPTCHA_INVERSE, $maxsize));
+ $alt = htmlentities(captcha::textual_riddle($pw));
+ $test = substr($img,22);
+ $html =
+ '<table border="0" summary="captcha input"><tr>'
+ . '<td><img name="captcha_image" id="captcha_image" src="'.$img. '" height="60" width="200" alt="'.$alt. '" /></td>'
+ . '<td>'.$title. '<br/><input name="captcha_hash" type="hidden" value="'.$hash. '" />'
+ . '<font color="red">*</font><input name="captcha_input" type="text" size="7" maxlength="16" style="height:23px; font-size:16px; font-weight:450;" />'
+ . '</td><td>'.$more.'</td>'
+ . '</tr></table>';
+
+// '<table border="0" summary="captcha input"><tr>'
+// '<img name="captcha_image" id="captcha_image" src="'.$img. '" height="60" width="200" alt="'.$alt. '" /></td>'
+// . '<td><img name="captcha_image" id="captcha_image" src="'.$img.'" height="60" width="200" alt="'.$alt. '" /></td>'
+// . '<td>'.$title. '<br/><input name="captcha_hash" type="hidden" value="'.$hash. '" />'
+// ''.$title. '<br/><input name="captcha_hash" type="hidden" value="'.$hash. '" />'
+// . '<input name="captcha_input" type="text" size="7" maxlength="16" style="height:23px; font-size:16px; font-weight:450;" />'
+// . '<td width="80%">'.$more.'</td>'
+// . '</tr></table>';
+
+ #-- js/html fix if ("MSIE")
+ {
+ $base = "http://$_SERVER[SERVER_NAME]:$_SERVER[SERVER_PORT]/ampache/captcha.php";
+// $base = "http://10.60.60.16/ampache/captcha.php";
+// . substr(realpath(__FILE__), strlen($_SERVER["DOCUMENT_ROOT"]));
+ $html .= <<<END
+<script language="Javascript"><!--
+if (/Microsoft/.test(navigator.appName)) {
+// var msg= "You are using IE Please download firefox in order to register. http://www.mozilla.org/products/firefox";
+// alert(msg);
+ var img = document.captcha_image;
+ img.src = "$base?_ddu=$test";
+ //alert (img.src);
+ }
+--></script>
+END;
+ }
+ $html = "<div class=\"captcha\">$html</div>";
+ return($html);
+ }
+/*<script language="Javascript"><!--
+if (/Microsoft/.test(navigator.appName)) {
+ var img = document.captcha_image;
+ img.src = "$base?_ddu=" + img.src.substr(23);
+ }
+--></script>/*
+
+
+ /* generates alternative (non-graphic), human-understandable
+ representation of the passphrase
+ */
+ function textual_riddle($phrase) {
+ $symbols0 = '"\'-/_:';
+ $symbols1 = array("\n,", "\n;", ";", "\n&", "\n-", ",", ",", "\nand then", "\nfollowed by", "\nand", "\nand not a\n\"".chr(65+rand(0,26))."\",\nbut");
+ $s = "Guess the letters and numbers\n(passphrase riddle)\n--\n";
+ for ($p=0; $p<strlen($phrase); $p++) {
+ $c = $phrase[$p];
+ $add = "";
+ #-- asis
+ if (!rand(0,3)) {
+ $i = $symbols0[rand(0,strlen($symbols0)-1)];
+ $add = "$i$c$i";
+ }
+ #-- letter
+ elseif ($c >= 'A') {
+ $type = ($c >= 'a' ? "small " : "");
+ do {
+ $n = rand(-3,3);
+ $c2 = chr((ord($c) & 0x5F) + $n);
+ }
+ while (($c2 < 'A') || ($c2 > 'Z'));
+ if ($n < 0) {
+ $n = -$n;
+ $add .= "$type'$c2' +$n letters";
+ }
+ else {
+ $add .= "$n chars before $type$c2";
+ }
+ }
+ #-- number
+ else {
+ $add = "???";
+ $n = (int) $c;
+ do {
+ do { $x = rand(1, 10); } while (!$x);
+ $op = rand(0,11);
+ if ($op <= 2) {
+ $add = "($add * $x)"; $n *= $x;
+ }
+ elseif ($op == 3) {
+ $x = 2 * rand(1,2);
+ $add = "($add / $x)"; $n /= $x;
+ }
+ elseif ($sel % 2) {
+ $add = "($add + $x)"; $n += $x;
+ }
+ else {
+ $add = "($add - $x)"; $n -= $x;
+ }
+ }
+ while (rand(0,1));
+ $add .= " = $n";
+ }
+ $s .= "$add";
+ $s .= $symbols1[rand(0,count($symbols1)-1)] . "\n";
+ }
+ return($s);
+ }
+
+
+ /* returns jpeg file stream with unscannable letters encoded
+ in front of colorful disturbing background
+ */
+ function image($phrase, $width=200, $height=60, $inverse=0, $maxsize=0xFFFFF) {
+
+ #-- initialize in-memory image with gd library
+ srand(microtime()*21017);
+ $img = imagecreatetruecolor($width, $height);
+ $R = $inverse ? 0xFF : 0x00;
+ imagefilledrectangle($img, 0,0, $width,$height, captcha::random_color($img, 222^$R, 255^$R));
+ $c1 = rand(150^$R, 185^$R);
+ $c2 = rand(195^$R, 230^$R);
+
+ #-- configuration
+ $fonts = array(
+ // "COLLEGE.ttf",
+ );
+ $fonts += glob(EWIKI_FONT_DIR."/*.ttf");
+
+ #-- encolour bg
+ $wd = 20;
+ $x = 0;
+ while ($x < $width) {
+ imagefilledrectangle($img, $x, 0, $x+=$wd, $height, captcha::random_color($img, 222^$R, 255^$R));
+ $wd += max(10, rand(0, 20) - 10);
+ }
+
+ #-- make interesting background I, lines
+ $wd = 4;
+ $w1 = 0;
+ $w2 = 0;
+ for ($x=0; $x<$width; $x+=(int)$wd) {
+ if ($x < $width) { // verical
+ imageline($img, $x+$w1, 0, $x+$w2, $height-1, captcha::random_color($img,$c1,$c2));
+ }
+ if ($x < $height) { // horizontally ("y")
+ imageline($img, 0, $x-$w2, $width-1, $x-$w1, captcha::random_color($img,$c1,$c2));
+ }
+ $wd += rand(0,8) - 4;
+ if ($wd < 1) { $wd = 2; }
+ $w1 += rand(0,8) - 4;
+ $w2 += rand(0,8) - 4;
+ if (($x > $height) && ($y > $height)) {
+ break;
+ }
+ }
+
+ #-- more disturbing II, random letters
+ $limit = rand(30,90);
+ for ($n=0; $n<$limit; $n++) {
+ $letter = "";
+ do {
+ $letter .= chr(rand(31,125)); // random symbol
+ } while (rand(0,1));
+ $size = rand(5, $height/2);
+ $half = (int) ($size / 2);
+ $x = rand(-$half, $width+$half);
+ $y = rand(+$half, $height);
+ $rotation = rand(60, 300);
+ $c1 = captcha::random_color($img, 130^$R, 240^$R);
+ $font = $fonts[rand(0, count($fonts)-1)];
+ imagettftext($img, $size, $rotation, $x, $y, $c1, $font, $letter);
+ }
+
+ #-- add the real text to it
+ $len = strlen($phrase);
+ $w1 = 10;
+ $w2 = $width / ($len+1);
+ for ($p=0; $p<$len; $p++) {
+ $letter = $phrase[$p];
+ $size = rand(18, $height/2.2);
+ $half = (int) $size / 2;
+ $rotation = rand(-33, 33);
+ $y = rand($size+3, $height-3);
+ $x = $w1 + $w2*$p;
+ $w1 += rand(-$width/90, $width/40); // @BUG: last char could be +30 pixel outside of image
+ $font = $fonts[rand(0, count($fonts)-1)];
+ $r=rand(30,99); $g=rand(30,99); $b=rand(30,99); // two colors for shadow
+ $c1 = imagecolorallocate($img, $r*1^$R, $g*1^$R, $b*1^$R);
+ $c2 = imagecolorallocate($img, $r*2^$R, $g*2^$R, $b*2^$R);
+ imagettftext($img, $size, $rotation, $x+1, $y, $c2, $font, $letter);
+ imagettftext($img, $size, $rotation, $x, $y-1, $c1, $font, $letter);
+ }
+
+ #-- let JFIF stream be generated
+// $quality = 67;
+ $quality = 8;
+ $s = array();
+ do {
+ ob_start(); ob_implicit_flush(0);
+ imagejpeg($img, "", (int)$quality);
+ $jpeg = ob_get_contents(); ob_end_clean();
+ $size = strlen($jpeg);
+ $s_debug[] = ((int)($quality*10)/10) . "%=$size";
+ $quality = $quality * ($maxsize/$size) * 0.93 - 1.7; // -($quality/7.222)*
+ }
+ while (($size > $maxsize) && ($quality >= 16));
+ imagedestroy($img);
+#print_r($s_debug);
+ return($jpeg);
+ }
+
+
+ /* helper code */
+ function random_color($img, $a,$b) {
+ return imagecolorallocate($img, rand($a,$b), rand($a,$b), rand($a,$b));
+ }
+
+
+ /* unreversable hash from passphrase, with time() slice encoded */
+ function hash($text, $dtime=0) {
+ $text = strtolower($text);
+ $pfix = (int) (time() / CAPTCHA_TIMEOUT) + $dtime;
+ return md5("captcha::$pfix:$text::".__FILE__.":$_SERVER[SERVER_NAME]:80");
+ }
+
+
+ /* makes string of random letters for embedding into image and for
+ encoding as hash, later verification
+ */
+ function mkpass() {
+ $s = "";
+ for ($n=0; $n<10; $n++) {
+ $s .= chr(rand(0, 255));
+ }
+ $s = base64_encode($s); // base64-set, but filter out unwanted chars
+ $s = preg_replace("/[+\/=IG0ODQR]/i", "", $s); // (depends on YOUR font)
+ $s = substr($s, 0, rand(5,7));
+ return($s);
+ }
+}
+
+
+#-- IE workaround
+if (isset($_REQUEST["_ddu"])) {
+ header("Content-Type: image/jpeg");
+ die(base64_decode(substr($_REQUEST["_ddu"], 0)));
+}
+
+
+?>
diff --git a/modules/id3/getid3/getid3.php b/modules/id3/getid3/getid3.php
index e87fcfb6..d0a8d479 100644
--- a/modules/id3/getid3/getid3.php
+++ b/modules/id3/getid3/getid3.php
@@ -10,7 +10,7 @@
/////////////////////////////////////////////////////////////////
// Defines
-define('GETID3_VERSION', '1.7.4');
+define('GETID3_VERSION', '1.7.5-200512251515');
define('GETID3_FREAD_BUFFER_SIZE', 16384); // read buffer size in bytes
@@ -111,26 +111,40 @@ class getID3
// ie for "C:/Program Files/Apache/" put "C:/PROGRA~1/APACHE/"
// IMPORTANT: This path must include the trailing slash
if (GETID3_OS_ISWINDOWS && !defined('GETID3_HELPERAPPSDIR')) {
-
$helperappsdir = GETID3_INCLUDEPATH.'..'.GETID3_OS_DIRSLASH.'helperapps'; // must not have any space in this path
if (!is_dir($helperappsdir)) {
+
$this->startup_error .= '"'.$helperappsdir.'" cannot be defined as GETID3_HELPERAPPSDIR because it does not exist';
+
} elseif (strpos(realpath($helperappsdir), ' ') !== false) {
+
$DirPieces = explode(GETID3_OS_DIRSLASH, realpath($helperappsdir));
- foreach ($DirPieces as $key => $value) {
- if ((strpos($value, '.') !== false) && (strpos($value, ' ') === false)) {
- if (strpos($value, '.') > 8) {
- $value = substr($value, 0, 6).'~1';
- }
- } elseif ((strpos($value, ' ') !== false) || strlen($value) > 8) {
- $value = substr($value, 0, 6).'~1';
- }
- $DirPieces[$key] = strtoupper($value);
- }
- $this->startup_error .= 'GETID3_HELPERAPPSDIR must not have any spaces in it - use 8dot3 naming convention if neccesary (on this server that would be something like "'.implode(GETID3_OS_DIRSLASH, $DirPieces).'" - NOTE: this may or may not be the actual 8.3 equivalent of "'.$helperappsdir.'", please double-check). You can run "dir /x" from the commandline to see the correct 8.3-style names. You need to edit the file "'.GETID3_INCLUDEPATH.'/getid3.php" around line '.(__LINE__ - 16);
+ $DirPieces8 = $DirPieces;
+
+ $CLIdir = $DirPieces[0].' && cd \\';
+ for ($i = 1; $i < count($DirPieces); $i++) {
+ if (strpos($DirPieces[$i], ' ') === false) {
+ $CLIdir .= ' && cd '.$DirPieces[$i];
+ } else {
+ ob_start();
+ system($CLIdir.' && dir /ad /x');
+ $subdirsraw = explode("\n", ob_get_contents());
+ ob_end_clean();
+ foreach ($subdirsraw as $line) {
+ if (eregi('^[0-9]{4}/[0-9]{2}/[0-9]{2} [0-9]{2}:[0-9]{2} [AP]M <DIR> ([^ ]{8}) '.preg_quote($DirPieces[$i]).'$', trim($line), $matches)) {
+ $CLIdir .= ' && cd '.$matches[1];
+ break;
+ }
+ }
+ $DirPieces8[$i] = $matches[1];
+ }
+ }
+ $helperappsdir = implode(GETID3_OS_DIRSLASH, $DirPieces8);
+
}
define('GETID3_HELPERAPPSDIR', realpath($helperappsdir).GETID3_OS_DIRSLASH);
+
}
}
@@ -436,7 +450,7 @@ class getID3
'group' => 'audio',
'module' => 'ac3',
'mime_type' => 'audio/ac3',
- ),
+ ),
// AAC - audio - Advanced Audio Coding (AAC) - ADIF format
'adif' => array(
@@ -446,7 +460,7 @@ class getID3
'option' => 'adif',
'mime_type' => 'application/octet-stream',
'fail_ape' => 'WARNING',
- ),
+ ),
// AAC - audio - Advanced Audio Coding (AAC) - ADTS format (very similar to MP3)
@@ -457,7 +471,7 @@ class getID3
'option' => 'adts',
'mime_type' => 'application/octet-stream',
'fail_ape' => 'WARNING',
- ),
+ ),
// AU - audio - NeXT/Sun AUdio (AU)
@@ -466,7 +480,7 @@ class getID3
'group' => 'audio',
'module' => 'au',
'mime_type' => 'audio/basic',
- ),
+ ),
// AVR - audio - Audio Visual Research
'avr' => array(
@@ -474,7 +488,7 @@ class getID3
'group' => 'audio',
'module' => 'avr',
'mime_type' => 'application/octet-stream',
- ),
+ ),
// BONK - audio - Bonk v0.9+
'bonk' => array(
@@ -482,7 +496,7 @@ class getID3
'group' => 'audio',
'module' => 'bonk',
'mime_type' => 'audio/xmms-bonk',
- ),
+ ),
// FLAC - audio - Free Lossless Audio Codec
'flac' => array(
@@ -490,7 +504,7 @@ class getID3
'group' => 'audio',
'module' => 'flac',
'mime_type' => 'audio/x-flac',
- ),
+ ),
// LA - audio - Lossless Audio (LA)
'la' => array(
@@ -498,7 +512,7 @@ class getID3
'group' => 'audio',
'module' => 'la',
'mime_type' => 'application/octet-stream',
- ),
+ ),
// LPAC - audio - Lossless Predictive Audio Compression (LPAC)
'lpac' => array(
@@ -506,7 +520,7 @@ class getID3
'group' => 'audio',
'module' => 'lpac',
'mime_type' => 'application/octet-stream',
- ),
+ ),
// MIDI - audio - MIDI (Musical Instrument Digital Interface)
'midi' => array(
@@ -514,7 +528,7 @@ class getID3
'group' => 'audio',
'module' => 'midi',
'mime_type' => 'audio/midi',
- ),
+ ),
// MAC - audio - Monkey's Audio Compressor
'mac' => array(
@@ -522,7 +536,7 @@ class getID3
'group' => 'audio',
'module' => 'monkey',
'mime_type' => 'application/octet-stream',
- ),
+ ),
// MOD - audio - MODule (assorted sub-formats)
'mod' => array(
@@ -531,7 +545,7 @@ class getID3
'module' => 'mod',
'option' => 'mod',
'mime_type' => 'audio/mod',
- ),
+ ),
// MOD - audio - MODule (Impulse Tracker)
'it' => array(
@@ -540,7 +554,7 @@ class getID3
'module' => 'mod',
'option' => 'it',
'mime_type' => 'audio/it',
- ),
+ ),
// MOD - audio - MODule (eXtended Module, various sub-formats)
'xm' => array(
@@ -549,7 +563,7 @@ class getID3
'module' => 'mod',
'option' => 'xm',
'mime_type' => 'audio/xm',
- ),
+ ),
// MOD - audio - MODule (ScreamTracker)
's3m' => array(
@@ -558,7 +572,7 @@ class getID3
'module' => 'mod',
'option' => 's3m',
'mime_type' => 'audio/s3m',
- ),
+ ),
// MPC - audio - Musepack / MPEGplus
'mpc' => array(
@@ -566,7 +580,7 @@ class getID3
'group' => 'audio',
'module' => 'mpc',
'mime_type' => 'application/octet-stream',
- ),
+ ),
// MP3 - audio - MPEG-audio Layer 3 (very similar to AAC-ADTS)
'mp3' => array(
@@ -574,7 +588,7 @@ class getID3
'group' => 'audio',
'module' => 'mp3',
'mime_type' => 'audio/mpeg',
- ),
+ ),
// OFR - audio - OptimFROG
'ofr' => array(
@@ -582,7 +596,7 @@ class getID3
'group' => 'audio',
'module' => 'optimfrog',
'mime_type' => 'application/octet-stream',
- ),
+ ),
// RKAU - audio - RKive AUdio compressor
'rkau' => array(
@@ -590,7 +604,7 @@ class getID3
'group' => 'audio',
'module' => 'rkau',
'mime_type' => 'application/octet-stream',
- ),
+ ),
// SHN - audio - Shorten
'shn' => array(
@@ -600,7 +614,7 @@ class getID3
'mime_type' => 'audio/xmms-shn',
'fail_id3' => 'ERROR',
'fail_ape' => 'ERROR',
- ),
+ ),
// TTA - audio - TTA Lossless Audio Compressor (http://tta.corecodec.org)
'tta' => array(
@@ -608,7 +622,7 @@ class getID3
'group' => 'audio',
'module' => 'tta',
'mime_type' => 'application/octet-stream',
- ),
+ ),
// VOC - audio - Creative Voice (VOC)
'voc' => array(
@@ -616,7 +630,7 @@ class getID3
'group' => 'audio',
'module' => 'voc',
'mime_type' => 'audio/voc',
- ),
+ ),
// VQF - audio - transform-domain weighted interleave Vector Quantization Format (VQF)
'vqf' => array(
@@ -624,7 +638,7 @@ class getID3
'group' => 'audio',
'module' => 'vqf',
'mime_type' => 'application/octet-stream',
- ),
+ ),
// WV - audio - WavPack (v4.0+)
'wv' => array(
@@ -632,7 +646,7 @@ class getID3
'group' => 'audio',
'module' => 'wavpack',
'mime_type' => 'application/octet-stream',
- ),
+ ),
// Audio-Video formats
@@ -644,15 +658,23 @@ class getID3
'module' => 'asf',
'mime_type' => 'video/x-ms-asf',
'iconv_req' => false,
- ),
+ ),
- // BINK - audio/video - Bink / Smacker
+ // BINK - audio/video - Bink / Smacker
'bink' => array(
'pattern' => '^(BIK|SMK)',
'group' => 'audio-video',
'module' => 'bink',
'mime_type' => 'application/octet-stream',
- ),
+ ),
+
+ // FLV - audio/video - FLash Video
+ 'flv' => array(
+ 'pattern' => '^FLV\x01',
+ 'group' => 'audio-video',
+ 'module' => 'flv',
+ 'mime_type' => 'video/x-flv',
+ ),
// MKAV - audio/video - Mastroka
'matroska' => array(
@@ -660,7 +682,7 @@ class getID3
'group' => 'audio-video',
'module' => 'matroska',
'mime_type' => 'application/octet-stream',
- ),
+ ),
// MPEG - audio/video - MPEG (Moving Pictures Experts Group)
'mpeg' => array(
@@ -668,7 +690,7 @@ class getID3
'group' => 'audio-video',
'module' => 'mpeg',
'mime_type' => 'video/mpeg',
- ),
+ ),
// NSV - audio/video - Nullsoft Streaming Video (NSV)
'nsv' => array(
@@ -676,7 +698,7 @@ class getID3
'group' => 'audio-video',
'module' => 'nsv',
'mime_type' => 'application/octet-stream',
- ),
+ ),
// Ogg - audio/video - Ogg (Ogg-Vorbis, Ogg-FLAC, Speex, Ogg-Theora(*), Ogg-Tarkin(*))
'ogg' => array(
@@ -686,7 +708,7 @@ class getID3
'mime_type' => 'application/ogg',
'fail_id3' => 'WARNING',
'fail_ape' => 'WARNING',
- ),
+ ),
// QT - audio/video - Quicktime
'quicktime' => array(
@@ -694,7 +716,7 @@ class getID3
'group' => 'audio-video',
'module' => 'quicktime',
'mime_type' => 'video/quicktime',
- ),
+ ),
// RIFF - audio/video - Resource Interchange File Format (RIFF) / WAV / AVI / CD-audio / SDSS = renamed variant used by SmartSound QuickTracks (www.smartsound.com) / FORM = Audio Interchange File Format (AIFF)
'riff' => array(
@@ -703,7 +725,7 @@ class getID3
'module' => 'riff',
'mime_type' => 'audio/x-wave',
'fail_ape' => 'WARNING',
- ),
+ ),
// Real - audio/video - RealAudio, RealVideo
'real' => array(
@@ -711,7 +733,7 @@ class getID3
'group' => 'audio-video',
'module' => 'real',
'mime_type' => 'audio/x-realaudio',
- ),
+ ),
// SWF - audio/video - ShockWave Flash
'swf' => array(
@@ -719,7 +741,7 @@ class getID3
'group' => 'audio-video',
'module' => 'swf',
'mime_type' => 'application/x-shockwave-flash',
- ),
+ ),
// Still-Image formats
@@ -732,7 +754,7 @@ class getID3
'mime_type' => 'image/bmp',
'fail_id3' => 'ERROR',
'fail_ape' => 'ERROR',
- ),
+ ),
// GIF - still image - Graphics Interchange Format
'gif' => array(
@@ -742,7 +764,7 @@ class getID3
'mime_type' => 'image/gif',
'fail_id3' => 'ERROR',
'fail_ape' => 'ERROR',
- ),
+ ),
// JPEG - still image - Joint Photographic Experts Group (JPEG)
'jpg' => array(
@@ -752,7 +774,7 @@ class getID3
'mime_type' => 'image/jpeg',
'fail_id3' => 'ERROR',
'fail_ape' => 'ERROR',
- ),
+ ),
// PCD - still image - Kodak Photo CD
'pcd' => array(
@@ -762,7 +784,7 @@ class getID3
'mime_type' => 'image/x-photo-cd',
'fail_id3' => 'ERROR',
'fail_ape' => 'ERROR',
- ),
+ ),
// PNG - still image - Portable Network Graphics (PNG)
@@ -773,7 +795,7 @@ class getID3
'mime_type' => 'image/png',
'fail_id3' => 'ERROR',
'fail_ape' => 'ERROR',
- ),
+ ),
// TIFF - still image - Tagged Information File Format (TIFF)
@@ -784,7 +806,7 @@ class getID3
'mime_type' => 'image/tiff',
'fail_id3' => 'ERROR',
'fail_ape' => 'ERROR',
- ),
+ ),
// Data formats
@@ -798,7 +820,7 @@ class getID3
'fail_id3' => 'ERROR',
'fail_ape' => 'ERROR',
'iconv_req' => false,
- ),
+ ),
// RAR - data - RAR compressed data
'rar' => array(
@@ -808,7 +830,7 @@ class getID3
'mime_type' => 'application/octet-stream',
'fail_id3' => 'ERROR',
'fail_ape' => 'ERROR',
- ),
+ ),
// SZIP - audio - SZIP compressed data
'szip' => array(
@@ -818,7 +840,7 @@ class getID3
'mime_type' => 'application/octet-stream',
'fail_id3' => 'ERROR',
'fail_ape' => 'ERROR',
- ),
+ ),
// TAR - data - TAR compressed data
'tar' => array(
@@ -828,7 +850,7 @@ class getID3
'mime_type' => 'application/x-tar',
'fail_id3' => 'ERROR',
'fail_ape' => 'ERROR',
- ),
+ ),
// GZIP - data - GZIP compressed data
'gz' => array(
@@ -838,7 +860,7 @@ class getID3
'mime_type' => 'application/x-gzip',
'fail_id3' => 'ERROR',
'fail_ape' => 'ERROR',
- ),
+ ),
// ZIP - data - ZIP compressed data
'zip' => array(
diff --git a/modules/id3/getid3/module.audio-video.asf.php b/modules/id3/getid3/module.audio-video.asf.php
index c11a04b2..c7e5aa1b 100644
--- a/modules/id3/getid3/module.audio-video.asf.php
+++ b/modules/id3/getid3/module.audio-video.asf.php
@@ -754,6 +754,11 @@ class getid3_asf
$thisfile_asf_comments['year'] = array( $this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']));
break;
+ case 'wm/lyrics':
+ case 'lyrics':
+ $thisfile_asf_comments['lyrics'] = array($this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']));
+ break;
+
case 'isvbr':
if ($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']) {
$thisfile_audio['bitrate_mode'] = 'vbr';
@@ -769,12 +774,12 @@ class getid3_asf
$tempThisfileInfo = array('encoding'=>$ThisFileInfo['encoding']);
fwrite($tempfilehandle, $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']);
fclose($tempfilehandle);
-
+
$tempfilehandle = fopen($tempfile, "rb");
$id3 = new getid3_id3v2($tempfilehandle, $tempThisfileInfo);
fclose($tempfilehandle);
unlink($tempfile);
-
+
$ThisFileInfo['id3v2'] = $tempThisfileInfo['id3v2'];
}
break;
diff --git a/modules/id3/getid3/module.audio-video.flv.php b/modules/id3/getid3/module.audio-video.flv.php
new file mode 100644
index 00000000..54666bd2
--- /dev/null
+++ b/modules/id3/getid3/module.audio-video.flv.php
@@ -0,0 +1,210 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org> //
+// available at http://getid3.sourceforge.net //
+// or http://www.getid3.org //
+// //
+// FLV module by Seth Kaufman <seth@whirl-i-gig.com> //
+// * version 0.1 (26 June 2005) //
+// minor modifications by James Heinrich <info@getid3.org> //
+// * version 0.1.1 (15 July 2005) //
+/////////////////////////////////////////////////////////////////
+// //
+// module.audio-video.flv.php //
+// module for analyzing Shockwave Flash Video files //
+// dependencies: NONE //
+// ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_flv
+{
+
+ function getid3_flv(&$fd, &$ThisFileInfo, $ReturnAllTagData=false) {
+ fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+
+ $FLVfileData = fread($fd, $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']);
+
+ $FLVmagic = substr($FLVfileData, 0, 3);
+ if ($FLVmagic != 'FLV') {
+ $ThisFileInfo['error'][] = 'Expecting "FLV" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$ThisFileInfo['flv']['header']['signature'].'"';
+ unset($ThisFileInfo['flv']);
+ unset($ThisFileInfo['fileformat']);
+ return false;
+ }
+ $ThisFileInfo['flv']['header']['signature'] = $FLVmagic;
+ $ThisFileInfo['flv']['header']['version'] = ord($FLVfileData{3});
+ $ThisFileInfo['fileformat'] = 'flv';
+
+ $TypeFlags = ord($FLVfileData{4});
+ $ThisFileInfo['flv']['header']['hasAudio'] = (bool) ($TypeFlags & 4);
+ $ThisFileInfo['flv']['header']['hasVideo'] = (bool) ($TypeFlags & 1);
+
+ $FrameSizeDataLength = getid3_lib::BigEndian2Int(substr($FLVfileData, 5, 4));
+
+ // FLV tags
+ $CurrentOffset = $FrameSizeDataLength;
+ $FLVdataLength = strlen($FLVfileData);
+
+ $Duration = 0;
+
+ $SoundFormat = null;
+ $VideoFormat = null;
+ while ($CurrentOffset < $FLVdataLength) {
+ // previous tag size
+ $PreviousTagLength = getid3_lib::BigEndian2Int(substr($FLVfileData, $CurrentOffset, 4));
+ $CurrentOffset += 4;
+
+ $TagType = ord(substr($FLVfileData, $CurrentOffset, 1));
+ $DataLength = getid3_lib::BigEndian2Int(substr($FLVfileData, $CurrentOffset + 1, 3));
+ $Timestamp = getid3_lib::BigEndian2Int(substr($FLVfileData, $CurrentOffset + 4, 3));
+
+ switch ($TagType) {
+ case 8:
+ if (is_null($SoundFormat)) {
+ $SoundInfo = ord(substr($FLVfileData, $CurrentOffset + 11, 1));
+ $SoundFormat = $SoundInfo & 0x07;
+ $ThisFileInfo['flv']['audio']['audioFormat'] = $SoundFormat;
+ $ThisFileInfo['flv']['audio']['audioRate'] = ($SoundInfo & 0x30) / 0x10;
+ $ThisFileInfo['flv']['audio']['audioSampleSize'] = ($SoundInfo & 0x40) / 0x40;
+ $ThisFileInfo['flv']['audio']['audioType'] = ($SoundInfo & 0x80) / 0x80;
+ }
+ break;
+
+ case 9:
+ if (is_null($VideoFormat)) {
+ $VideoInfo = ord(substr($FLVfileData, $CurrentOffset + 11, 1));
+ $VideoFormat = $VideoInfo & 0x07;
+ $ThisFileInfo['flv']['video']['videoCodec'] = $VideoFormat;
+
+ $PictureSizeType = (getid3_lib::BigEndian2Int(substr($FLVfileData, $CurrentOffset + 15, 2))) >> 7;
+ $PictureSizeType = $PictureSizeType & 0x0007;
+ $ThisFileInfo['flv']['header']['videoSizeType'] = $PictureSizeType;
+ switch ($PictureSizeType) {
+ case 0:
+ $PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVfileData, $CurrentOffset + 16, 2));
+ $PictureSizeEnc <<= 1;
+ $ThisFileInfo['video']['resolution_x'] = ($PictureSizeEnc & 0xFF00) >> 8;
+ $PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVfileData, $CurrentOffset + 17, 2));
+ $PictureSizeEnc <<= 1;
+ $ThisFileInfo['video']['resolution_y'] = ($PictureSizeEnc & 0xFF00) >> 8;
+ break;
+
+ case 1:
+ $PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVfileData, $CurrentOffset + 16, 4));
+ $PictureSizeEnc <<= 1;
+ $ThisFileInfo['video']['resolution_x'] = ($PictureSizeEnc & 0xFFFF0000) >> 16;
+
+ $PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVfileData, $CurrentOffset + 18, 4));
+ $PictureSizeEnc <<= 1;
+ $ThisFileInfo['video']['resolution_y'] = ($PictureSizeEnc & 0xFFFF0000) >> 16;
+ break;
+
+ case 2:
+ $ThisFileInfo['video']['resolution_x'] = 352;
+ $ThisFileInfo['video']['resolution_y'] = 288;
+ break;
+
+ case 3:
+ $ThisFileInfo['video']['resolution_x'] = 176;
+ $ThisFileInfo['video']['resolution_y'] = 144;
+ break;
+
+ case 4:
+ $ThisFileInfo['video']['resolution_x'] = 128;
+ $ThisFileInfo['video']['resolution_y'] = 96;
+ break;
+
+ case 5:
+ $ThisFileInfo['video']['resolution_x'] = 320;
+ $ThisFileInfo['video']['resolution_y'] = 240;
+ break;
+
+ case 6:
+ $ThisFileInfo['video']['resolution_x'] = 160;
+ $ThisFileInfo['video']['resolution_y'] = 120;
+ break;
+
+ default:
+ $ThisFileInfo['video']['resolution_x'] = 0;
+ $ThisFileInfo['video']['resolution_y'] = 0;
+ break;
+
+ }
+ }
+ break;
+
+ default:
+ // noop
+ break;
+ }
+
+ if ($Timestamp > $Duration) {
+ $Duration = $Timestamp;
+ }
+
+ $CurrentOffset += ($DataLength + 11);
+ }
+
+ $ThisFileInfo['playtime_seconds'] = $Duration / 1000;
+ $ThisFileInfo['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $ThisFileInfo['playtime_seconds'];
+
+ if ($ThisFileInfo['flv']['header']['hasAudio']) {
+ $ThisFileInfo['audio']['codec'] = $this->FLVaudioFormat($ThisFileInfo['flv']['audio']['audioFormat']);
+ $ThisFileInfo['audio']['sample_rate'] = $this->FLVaudioRate($ThisFileInfo['flv']['audio']['audioRate']);
+ $ThisFileInfo['audio']['bits_per_sample'] = $this->FLVaudioBitDepth($ThisFileInfo['flv']['audio']['audioSampleSize']);
+
+ $ThisFileInfo['audio']['channels'] = $ThisFileInfo['flv']['audio']['audioType'] + 1; // 0=mono,1=stereo
+ $ThisFileInfo['audio']['lossless'] = ($ThisFileInfo['flv']['audio']['audioFormat'] ? false : true); // 0=uncompressed
+ $ThisFileInfo['audio']['dataformat'] = 'flv';
+ }
+ if (@$ThisFileInfo['flv']['header']['hasVideo']) {
+ $ThisFileInfo['video']['codec'] = $this->FLVvideoCodec($ThisFileInfo['flv']['video']['videoCodec']);
+ $ThisFileInfo['video']['dataformat'] = 'flv';
+ $ThisFileInfo['video']['lossless'] = false;
+ }
+
+ return true;
+ }
+
+
+ function FLVaudioFormat($id) {
+ $FLVaudioFormat = array(
+ 0 => 'uncompressed',
+ 1 => 'ADPCM',
+ 2 => 'mp3',
+ 5 => 'Nellymoser 8kHz mono',
+ 6 => 'Nellymoser',
+ );
+ return (@$FLVaudioFormat[$id] ? @$FLVaudioFormat[$id] : false);
+ }
+
+ function FLVaudioRate($id) {
+ $FLVaudioRate = array(
+ 0 => 5500,
+ 1 => 11025,
+ 2 => 22050,
+ 3 => 44100,
+ );
+ return (@$FLVaudioRate[$id] ? @$FLVaudioRate[$id] : false);
+ }
+
+ function FLVaudioBitDepth($id) {
+ $FLVaudioBitDepth = array(
+ 0 => 8,
+ 1 => 16,
+ );
+ return (@$FLVaudioBitDepth[$id] ? @$FLVaudioBitDepth[$id] : false);
+ }
+
+ function FLVvideoCodec($id) {
+ $FLVaudioBitDepth = array(
+ 2 => 'Sorenson H.263',
+ 3 => 'Screen video',
+ );
+ return (@$FLVaudioBitDepth[$id] ? @$FLVaudioBitDepth[$id] : false);
+ }
+
+}
+
+?> \ No newline at end of file
diff --git a/modules/id3/getid3/module.audio-video.riff.php b/modules/id3/getid3/module.audio-video.riff.php
index ebe3ee77..b2225a28 100644
--- a/modules/id3/getid3/module.audio-video.riff.php
+++ b/modules/id3/getid3/module.audio-video.riff.php
@@ -1953,8 +1953,6 @@ class getid3_riff
XMPG Xing MPEG (I-Frame only)
XVID XviD MPEG-4 (www.xvid.org)
XXAN ?XXAN?
- Y422 ADS Technologies Copy of UYVY used in Pyro WebCam firewire camera
- Y800 Simple, single Y plane for monochrome images
YU92 Intel YUV (YU92)
YUNV Nvidia Uncompressed YUV 4:2:2
YUVP Extended PAL format YUV palette (www.riff.org)
@@ -1965,6 +1963,8 @@ class getid3_riff
Y41T Brooktree PC1 YUV 4:1:1 with transparency
Y42B Weitek YUV 4:2:2 Planar
Y42T Brooktree UYUV 4:2:2 with transparency
+ Y422 ADS Technologies Copy of UYVY used in Pyro WebCam firewire camera
+ Y800 Simple, single Y plane for monochrome images
Y8 Grayscale video
YC12 Intel YUV 12 codec
YUV8 Winnov Caviar YUV8
diff --git a/modules/id3/getid3/module.audio.mp3.php b/modules/id3/getid3/module.audio.mp3.php
index d7b0b77a..5fb6110c 100644
--- a/modules/id3/getid3/module.audio.mp3.php
+++ b/modules/id3/getid3/module.audio.mp3.php
@@ -320,8 +320,9 @@ class getid3_mp3
'fast standard|19000' => 19000,
'r3mix|19500' => 19500, // 3.90, 3.90.1, 3.92
'r3mix|19600' => 19600, // 3.90.2, 3.90.3, 3.91
- 'r3mix|18000' => 18000); // 3.94, 3.95
- if (!isset($ExpectedLowpass[$ExplodedOptions[1].'|'.$thisfile_mpeg_audio_lame['lowpass_frequency']])) {
+ 'r3mix|18000' => 18000, // 3.94, 3.95
+ );
+ if (!isset($ExpectedLowpass[$ExplodedOptions[1].'|'.$thisfile_mpeg_audio_lame['lowpass_frequency']]) && ($thisfile_mpeg_audio_lame['lowpass_frequency'] < 22050) && (round($thisfile_mpeg_audio_lame['lowpass_frequency'] / 1000) < round($thisfile_mpeg_audio['sample_rate'] / 2000))) {
$encoder_options .= ' --lowpass '.$thisfile_mpeg_audio_lame['lowpass_frequency'];
}
break;
@@ -1636,23 +1637,17 @@ class getid3_mp3
if (isset($MPEGaudioVersionLookup[$rawarray['version']])) {
$decodedVersion = $MPEGaudioVersionLookup[$rawarray['version']];
} else {
- if ($echoerrors) {
- echo "\n".'invalid Version ('.$rawarray['version'].')';
- }
+ echo ($echoerrors ? "\n".'invalid Version ('.$rawarray['version'].')' : '');
return false;
}
if (isset($MPEGaudioLayerLookup[$rawarray['layer']])) {
$decodedLayer = $MPEGaudioLayerLookup[$rawarray['layer']];
} else {
- if ($echoerrors) {
- echo "\n".'invalid Layer ('.$rawarray['layer'].')';
- }
+ echo ($echoerrors ? "\n".'invalid Layer ('.$rawarray['layer'].')' : '');
return false;
}
if (!isset($MPEGaudioBitrateLookup[$decodedVersion][$decodedLayer][$rawarray['bitrate']])) {
- if ($echoerrors) {
- echo "\n".'invalid Bitrate ('.$rawarray['bitrate'].')';
- }
+ echo ($echoerrors ? "\n".'invalid Bitrate ('.$rawarray['bitrate'].')' : '');
if ($rawarray['bitrate'] == 15) {
// known issue in LAME 3.90 - 3.93.1 where free-format has bitrate ID of 15 instead of 0
// let it go through here otherwise file will not be identified
@@ -1664,27 +1659,19 @@ class getid3_mp3
}
}
if (!isset($MPEGaudioFrequencyLookup[$decodedVersion][$rawarray['sample_rate']])) {
- if ($echoerrors) {
- echo "\n".'invalid Frequency ('.$rawarray['sample_rate'].')';
- }
+ echo ($echoerrors ? "\n".'invalid Frequency ('.$rawarray['sample_rate'].')' : '');
return false;
}
if (!isset($MPEGaudioChannelModeLookup[$rawarray['channelmode']])) {
- if ($echoerrors) {
- echo "\n".'invalid ChannelMode ('.$rawarray['channelmode'].')';
- }
+ echo ($echoerrors ? "\n".'invalid ChannelMode ('.$rawarray['channelmode'].')' : '');
return false;
}
if (!isset($MPEGaudioModeExtensionLookup[$decodedLayer][$rawarray['modeextension']])) {
- if ($echoerrors) {
- echo "\n".'invalid Mode Extension ('.$rawarray['modeextension'].')';
- }
+ echo ($echoerrors ? "\n".'invalid Mode Extension ('.$rawarray['modeextension'].')' : '');
return false;
}
if (!isset($MPEGaudioEmphasisLookup[$rawarray['emphasis']])) {
- if ($echoerrors) {
- echo "\n".'invalid Emphasis ('.$rawarray['emphasis'].')';
- }
+ echo ($echoerrors ? "\n".'invalid Emphasis ('.$rawarray['emphasis'].')' : '');
return false;
}
// These are just either set or not set, you can't mess that up :)
diff --git a/modules/id3/getid3/module.tag.id3v2.php b/modules/id3/getid3/module.tag.id3v2.php
index c855bf21..8bb6c3cb 100644
--- a/modules/id3/getid3/module.tag.id3v2.php
+++ b/modules/id3/getid3/module.tag.id3v2.php
@@ -101,47 +101,51 @@ class getid3_id3v2
$thisfile_id3v2['tag_offset_start'] = $StartingOffset;
$thisfile_id3v2['tag_offset_end'] = $thisfile_id3v2['tag_offset_start'] + $thisfile_id3v2['headerlength'];
- // Extended Header
+ // Extended Header
if (isset($thisfile_id3v2_flags['exthead']) && $thisfile_id3v2_flags['exthead']) {
- // Extended header size 4 * %0xxxxxxx
- // Number of flag bytes $01
- // Extended Flags $xx
- // Where the 'Extended header size' is the size of the whole extended header, stored as a 32 bit synchsafe integer.
- $extheader = fread ($fd, 4);
- $thisfile_id3v2['extheaderlength'] = getid3_lib::BigEndian2Int($extheader, 1);
-
- // The extended flags field, with its size described by 'number of flag bytes', is defined as:
- // %0bcd0000
- // b - Tag is an update
- // Flag data length $00
- // c - CRC data present
- // Flag data length $05
- // Total frame CRC 5 * %0xxxxxxx
- // d - Tag restrictions
- // Flag data length $01
- $extheaderflagbytes = fread ($fd, 1);
- $extheaderflags = fread ($fd, $extheaderflagbytes);
- $id3_exthead_flags = getid3_lib::BigEndian2Bin(substr($header, 5, 1));
- $thisfile_id3v2['exthead_flags']['update'] = substr($id3_exthead_flags, 1, 1);
- $thisfile_id3v2['exthead_flags']['CRC'] = substr($id3_exthead_flags, 2, 1);
- if ($thisfile_id3v2['exthead_flags']['CRC']) {
- $extheaderrawCRC = fread ($fd, 5);
- $thisfile_id3v2['exthead_flags']['CRC'] = getid3_lib::BigEndian2Int($extheaderrawCRC, 1);
- }
- $thisfile_id3v2['exthead_flags']['restrictions'] = substr($id3_exthead_flags, 3, 1);
- if ($thisfile_id3v2['exthead_flags']['restrictions']) {
- // Restrictions %ppqrrstt
- $extheaderrawrestrictions = fread ($fd, 1);
- $thisfile_id3v2['exthead_flags']['restrictions_tagsize'] = (bindec('11000000') & ord($extheaderrawrestrictions)) >> 6; // p - Tag size restrictions
- $thisfile_id3v2['exthead_flags']['restrictions_textenc'] = (bindec('00100000') & ord($extheaderrawrestrictions)) >> 5; // q - Text encoding restrictions
- $thisfile_id3v2['exthead_flags']['restrictions_textsize'] = (bindec('00011000') & ord($extheaderrawrestrictions)) >> 3; // r - Text fields size restrictions
- $thisfile_id3v2['exthead_flags']['restrictions_imgenc'] = (bindec('00000100') & ord($extheaderrawrestrictions)) >> 2; // s - Image encoding restrictions
- $thisfile_id3v2['exthead_flags']['restrictions_imgsize'] = (bindec('00000011') & ord($extheaderrawrestrictions)) >> 0; // t - Image size restrictions
+ // Extended header size 4 * %0xxxxxxx
+ // Number of flag bytes $01
+ // Extended Flags $xx
+ // Where the 'Extended header size' is the size of the whole extended header, stored as a 32 bit synchsafe integer.
+ $thisfile_id3v2['exthead_length'] = getid3_lib::BigEndian2Int(fread($fd, 4), 1);
+
+ $thisfile_id3v2['exthead_flag_bytes'] = ord(fread($fd, 1));
+ if ($thisfile_id3v2['exthead_flag_bytes'] == 1) {
+ // The extended flags field, with its size described by 'number of flag bytes', is defined as:
+ // %0bcd0000
+ // b - Tag is an update
+ // Flag data length $00
+ // c - CRC data present
+ // Flag data length $05
+ // Total frame CRC 5 * %0xxxxxxx
+ // d - Tag restrictions
+ // Flag data length $01
+ $extheaderflags = fread($fd, $thisfile_id3v2['exthead_flag_bytes']);
+ $id3_exthead_flags = getid3_lib::BigEndian2Bin(substr($header, 5, 1));
+ $thisfile_id3v2['exthead_flags']['update'] = substr($id3_exthead_flags, 1, 1);
+ $thisfile_id3v2['exthead_flags']['CRC'] = substr($id3_exthead_flags, 2, 1);
+ if ($thisfile_id3v2['exthead_flags']['CRC']) {
+ $extheaderrawCRC = fread($fd, 5);
+ $thisfile_id3v2['exthead_flags']['CRC'] = getid3_lib::BigEndian2Int($extheaderrawCRC, 1);
+ }
+ $thisfile_id3v2['exthead_flags']['restrictions'] = substr($id3_exthead_flags, 3, 1);
+ if ($thisfile_id3v2['exthead_flags']['restrictions']) {
+ // Restrictions %ppqrrstt
+ $extheaderrawrestrictions = fread($fd, 1);
+ $thisfile_id3v2['exthead_flags']['restrictions_tagsize'] = (bindec('11000000') & ord($extheaderrawrestrictions)) >> 6; // p - Tag size restrictions
+ $thisfile_id3v2['exthead_flags']['restrictions_textenc'] = (bindec('00100000') & ord($extheaderrawrestrictions)) >> 5; // q - Text encoding restrictions
+ $thisfile_id3v2['exthead_flags']['restrictions_textsize'] = (bindec('00011000') & ord($extheaderrawrestrictions)) >> 3; // r - Text fields size restrictions
+ $thisfile_id3v2['exthead_flags']['restrictions_imgenc'] = (bindec('00000100') & ord($extheaderrawrestrictions)) >> 2; // s - Image encoding restrictions
+ $thisfile_id3v2['exthead_flags']['restrictions_imgsize'] = (bindec('00000011') & ord($extheaderrawrestrictions)) >> 0; // t - Image size restrictions
+ }
+ } else {
+ $ThisFileInfo['warning'][] = '$thisfile_id3v2[exthead_flag_bytes] = "'.$thisfile_id3v2['exthead_flag_bytes'].'" (expecting "1")';
+ fseek($fd, $thisfile_id3v2['exthead_length'] - 1, SEEK_CUR);
+ //return false;
}
} // end extended header
-
// create 'encoding' key - used by getid3::HandleAllTags()
// in ID3v2 every field can have it's own encoding type
// so force everything to UTF-8 so it can be handled consistantly
@@ -159,10 +163,10 @@ class getid3_id3v2
// Flags $xx xx
$sizeofframes = $thisfile_id3v2['headerlength'] - 10; // not including 10-byte initial header
- if (isset($thisfile_id3v2['extheaderlength'])) {
- $sizeofframes -= $thisfile_id3v2['extheaderlength'];
+ if (@$thisfile_id3v2['exthead_length']) {
+ $sizeofframes -= ($thisfile_id3v2['exthead_length'] + 4);
}
- if (isset($thisfile_id3v2_flags['isfooter']) && $thisfile_id3v2_flags['isfooter']) {
+ if (@$thisfile_id3v2_flags['isfooter']) {
$sizeofframes -= 10; // footer takes last 10 bytes of ID3v2 header, after frame data, before audio
}
if ($sizeofframes > 0) {
@@ -170,7 +174,7 @@ class getid3_id3v2
$framedata = fread($fd, $sizeofframes); // read all frames from file into $framedata variable
// if entire frame data is unsynched, de-unsynch it now (ID3v2.3.x)
- if (isset($thisfile_id3v2_flags['unsynch']) && $thisfile_id3v2_flags['unsynch'] && ($id3v2_majorversion <= 3)) {
+ if (@$thisfile_id3v2_flags['unsynch'] && ($id3v2_majorversion <= 3)) {
$framedata = $this->DeUnsynchronise($framedata);
}
// [in ID3v2.4.0] Unsynchronisation [S:6.1] is done on frame level, instead
@@ -179,7 +183,7 @@ class getid3_id3v2
// there exists an unsynchronised frame, while the new unsynchronisation flag in
// the frame header [S:4.1.2] indicates unsynchronisation.
- $framedataoffset = 10; // how many bytes into the stream - start from after the 10-byte header
+ $framedataoffset = 10 + (@$thisfile_id3v2['exthead_length'] ? $thisfile_id3v2['exthead_length'] + 4 : 0); // how many bytes into the stream - start from after the 10-byte header (and extended header length+4, if present)
while (isset($framedata) && (strlen($framedata) > 0)) { // cycle through until no more frame data is left to parse
if (strlen($framedata) <= $this->ID3v2HeaderLength($id3v2_majorversion)) {
// insufficient room left in ID3v2 header for actual data - must be padding
@@ -350,7 +354,7 @@ class getid3_id3v2
// ID3v2 size 4 * %0xxxxxxx
if (isset($thisfile_id3v2_flags['isfooter']) && $thisfile_id3v2_flags['isfooter']) {
- $footer = fread ($fd, 10);
+ $footer = fread($fd, 10);
if (substr($footer, 0, 3) == '3DI') {
$thisfile_id3v2['footer'] = true;
$thisfile_id3v2['majorversion_footer'] = ord($footer{3});
diff --git a/modules/id3/getid3/write.id3v2.php b/modules/id3/getid3/write.id3v2.php
index a575f20b..9d47e708 100644
--- a/modules/id3/getid3/write.id3v2.php
+++ b/modules/id3/getid3/write.id3v2.php
@@ -101,6 +101,7 @@ class getid3_write_id3v2
fclose($fp_source);
copy($tempfilename, $this->filename);
unlink($tempfilename);
+ ob_end_clean();
return true;
} else {
diff --git a/modules/id3/getid3/write.php b/modules/id3/getid3/write.php
index 40a79e9e..db09decd 100644
--- a/modules/id3/getid3/write.php
+++ b/modules/id3/getid3/write.php
@@ -394,6 +394,14 @@ class getid3_writetags
}
break;
+ case 'real':
+ $real_writer = new getid3_write_real;
+ $real_writer->filename = $this->filename;
+ if (($success = $real_writer->RemoveReal()) === false) {
+ $this->errors[] = 'RemoveReal() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $real_writer->errors)).'</LI></UL></PRE>';
+ }
+ break;
+
default:
$this->errors[] = 'Invalid tag format to delete: "'.$tagformat.'"';
return false;
diff --git a/modules/id3/getid3/write.real.php b/modules/id3/getid3/write.real.php
index 5ede28d1..1e0240cc 100644
--- a/modules/id3/getid3/write.real.php
+++ b/modules/id3/getid3/write.real.php
@@ -16,7 +16,7 @@
class getid3_write_real
{
var $filename;
- var $tag_data;
+ var $tag_data = array();
var $warnings = array(); // any non-critical errors will be stored here
var $errors = array(); // any critical errors will be stored here
var $paddedlength = 512; // minimum length of CONT tag in bytes
@@ -33,50 +33,98 @@ class getid3_write_real
// Initialize getID3 engine
$getID3 = new getID3;
$OldThisFileInfo = $getID3->analyze($this->filename);
- if (empty($OldThisFileInfo['chunks']) && !empty($OldThisFileInfo['old_ra_header'])) {
+ if (empty($OldThisFileInfo['real']['chunks']) && !empty($OldThisFileInfo['real']['old_ra_header'])) {
$this->errors[] = 'Cannot write Real tags on old-style file format';
+ fclose($fp_source);
return false;
}
- $OldPROPinfo = false;
- $StartOfDATA = false;
- foreach ($OldThisFileInfo['chunks'] as $chunknumber => $chunkarray) {
- if ($chunkarray['name'] == 'PROP') {
- $OldPROPinfo = $chunkarray;
- } elseif ($chunkarray['name'] = 'DATA') {
- $StartOfDATA = $chunkarray['offset'];
- }
+ if (empty($OldThisFileInfo['real']['chunks'])) {
+ $this->errors[] = 'Cannot write Real tags because cannot find DATA chunk in file';
+ fclose($fp_source);
+ return false;
+ }
+ foreach ($OldThisFileInfo['real']['chunks'] as $chunknumber => $chunkarray) {
+ $oldChunkInfo[$chunkarray['name']] = $chunkarray;
+ }
+ if (!empty($oldChunkInfo['CONT']['length'])) {
+ $this->paddedlength = max($oldChunkInfo['CONT']['length'], $this->paddedlength);
}
- if (!empty($OldPROPinfo['length'])) {
- $this->paddedlength = max($OldPROPinfo['length'], $this->paddedlength);
+ $new_CONT_tag_data = $this->GenerateCONTchunk();
+ $new_PROP_tag_data = $this->GeneratePROPchunk($OldThisFileInfo['real']['chunks'], $new_CONT_tag_data);
+ $new__RMF_tag_data = $this->GenerateRMFchunk($OldThisFileInfo['real']['chunks']);
+
+ if (@$oldChunkInfo['.RMF']['length'] == strlen($new__RMF_tag_data)) {
+ fseek($fp_source, $oldChunkInfo['.RMF']['offset'], SEEK_SET);
+ fwrite($fp_source, $new__RMF_tag_data);
+ } else {
+ $this->errors[] = 'new .RMF tag ('.strlen($new__RMF_tag_data).' bytes) different length than old .RMF tag ('.$oldChunkInfo['.RMF']['length'].' bytes)';
+ fclose($fp_source);
+ return false;
}
- $new_real_tag_data = GenerateRealTag();
+ if (@$oldChunkInfo['PROP']['length'] == strlen($new_PROP_tag_data)) {
+ fseek($fp_source, $oldChunkInfo['PROP']['offset'], SEEK_SET);
+ fwrite($fp_source, $new_PROP_tag_data);
+ } else {
+ $this->errors[] = 'new PROP tag ('.strlen($new_PROP_tag_data).' bytes) different length than old PROP tag ('.$oldChunkInfo['PROP']['length'].' bytes)';
+ fclose($fp_source);
+ return false;
+ }
- if (@$OldPROPinfo['length'] == $new_real_tag_data) {
+ if (@$oldChunkInfo['CONT']['length'] == strlen($new_CONT_tag_data)) {
// new data length is same as old data length - just overwrite
- fseek($fp_source, $OldPROPinfo['offset'], SEEK_SET);
- fwrite($fp_source, $new_real_tag_data);
+ fseek($fp_source, $oldChunkInfo['CONT']['offset'], SEEK_SET);
+ fwrite($fp_source, $new_CONT_tag_data);
+ fclose($fp_source);
+ return true;
} else {
- if (empty($OldPROPinfo)) {
- // no existing PROP chunk
- $BeforeOffset = $StartOfDATA;
- $AfterOffset = $StartOfDATA;
+ if (empty($oldChunkInfo['CONT'])) {
+ // no existing CONT chunk
+ $BeforeOffset = $oldChunkInfo['DATA']['offset'];
+ $AfterOffset = $oldChunkInfo['DATA']['offset'];
} else {
// new data is longer than old data
- $BeforeOffset = $OldPROPinfo['offset'];
- $AfterOffset = $OldPROPinfo['offset'] + $OldPROPinfo['length'];
+ $BeforeOffset = $oldChunkInfo['CONT']['offset'];
+ $AfterOffset = $oldChunkInfo['CONT']['offset'] + $oldChunkInfo['CONT']['length'];
}
+ if ($tempfilename = tempnam('*', 'getID3')) {
+ ob_start();
+ if ($fp_temp = fopen($tempfilename, 'wb')) {
+
+ rewind($fp_source);
+ fwrite($fp_temp, fread($fp_source, $BeforeOffset));
+ fwrite($fp_temp, $new_CONT_tag_data);
+ fseek($fp_source, $AfterOffset, SEEK_SET);
+ while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) {
+ fwrite($fp_temp, $buffer, strlen($buffer));
+ }
+ fclose($fp_temp);
+
+ if (copy($tempfilename, $this->filename)) {
+ unlink($tempfilename);
+ fclose($fp_source);
+ return true;
+ }
+ unlink($tempfilename);
+ $this->errors[] = 'FAILED: copy('.$tempfilename.', '.$this->filename.') - '.strip_tags(ob_get_contents());
+ } else {
+
+ $this->errors[] = 'Could not open '.$tempfilename.' mode "wb" - '.strip_tags(ob_get_contents());
+
+ }
+ ob_end_clean();
+ }
+ fclose($fp_source);
+ return false;
}
- fclose($fp_source);
- return true;
} else {
$this->errors[] = 'Could not open '.$this->filename.' mode "r+b"';
@@ -87,28 +135,88 @@ class getid3_write_real
return false;
}
- function GenerateRealTag() {
- $RealCONT = "\x00\x00"; // object version
+ function GenerateRMFchunk(&$chunks) {
+ $oldCONTexists = false;
+ foreach ($chunks as $key => $chunk) {
+ $chunkNameKeys[$chunk['name']] = $key;
+ if ($chunk['name'] == 'CONT') {
+ $oldCONTexists = true;
+ }
+ }
+ $newHeadersCount = $chunks[$chunkNameKeys['.RMF']]['headers_count'] + ($oldCONTexists ? 0 : 1);
- $RealCONT .= BigEndian2String(strlen(@$this->tag_data['title']), 4);
- $RealCONT .= @$this->tag_data['title'];
+ $RMFchunk = "\x00\x00"; // object version
+ $RMFchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['.RMF']]['file_version'], 4);
+ $RMFchunk .= getid3_lib::BigEndian2String($newHeadersCount, 4);
- $RealCONT .= BigEndian2String(strlen(@$this->tag_data['artist']), 4);
- $RealCONT .= @$this->tag_data['artist'];
+ $RMFchunk = '.RMF'.getid3_lib::BigEndian2String(strlen($RMFchunk) + 8, 4).$RMFchunk; // .RMF chunk identifier + chunk length
+ return $RMFchunk;
+ }
+
+ function GeneratePROPchunk(&$chunks, &$new_CONT_tag_data) {
+ $old_CONT_length = 0;
+ $old_DATA_offset = 0;
+ $old_INDX_offset = 0;
+ foreach ($chunks as $key => $chunk) {
+ $chunkNameKeys[$chunk['name']] = $key;
+ if ($chunk['name'] == 'CONT') {
+ $old_CONT_length = $chunk['length'];
+ } elseif ($chunk['name'] == 'DATA') {
+ if (!$old_DATA_offset) {
+ $old_DATA_offset = $chunk['offset'];
+ }
+ } elseif ($chunk['name'] == 'INDX') {
+ if (!$old_INDX_offset) {
+ $old_INDX_offset = $chunk['offset'];
+ }
+ }
+ }
+ $CONTdelta = strlen($new_CONT_tag_data) - $old_CONT_length;
- $RealCONT .= BigEndian2String(strlen(@$this->tag_data['copyright']), 4);
- $RealCONT .= @$this->tag_data['copyright'];
+ $PROPchunk = "\x00\x00"; // object version
+ $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['max_bit_rate'], 4);
+ $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['avg_bit_rate'], 4);
+ $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['max_packet_size'], 4);
+ $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['avg_packet_size'], 4);
+ $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['num_packets'], 4);
+ $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['duration'], 4);
+ $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['preroll'], 4);
+ $PROPchunk .= getid3_lib::BigEndian2String(max(0, $old_INDX_offset + $CONTdelta), 4);
+ $PROPchunk .= getid3_lib::BigEndian2String(max(0, $old_DATA_offset + $CONTdelta), 4);
+ $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['num_streams'], 2);
+ $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['flags_raw'], 2);
- $RealCONT .= BigEndian2String(strlen(@$this->tag_data['comment']), 4);
- $RealCONT .= @$this->tag_data['comment'];
+ $PROPchunk = 'PROP'.getid3_lib::BigEndian2String(strlen($PROPchunk) + 8, 4).$PROPchunk; // PROP chunk identifier + chunk length
+ return $PROPchunk;
+ }
- if ($this->paddedlength > (strlen($RealCONT) + 8)) {
- $RealCONT .= str_repeat("\x00", $this->paddedlength - strlen($RealCONT) - 8);
+ function GenerateCONTchunk() {
+ foreach ($this->tag_data as $key => $value) {
+ // limit each value to 0xFFFF bytes
+ $this->tag_data[$key] = substr($value, 0, 65535);
}
- $RealCONT = 'CONT'.BigEndian2String(strlen($RealCONT) + 8, 4).$RealCONT; // CONT chunk identifier + chunk length
+ $CONTchunk = "\x00\x00"; // object version
+
+ $CONTchunk .= getid3_lib::BigEndian2String(strlen(@$this->tag_data['title']), 2);
+ $CONTchunk .= @$this->tag_data['title'];
+
+ $CONTchunk .= getid3_lib::BigEndian2String(strlen(@$this->tag_data['artist']), 2);
+ $CONTchunk .= @$this->tag_data['artist'];
+
+ $CONTchunk .= getid3_lib::BigEndian2String(strlen(@$this->tag_data['copyright']), 2);
+ $CONTchunk .= @$this->tag_data['copyright'];
- return $RealCONT;
+ $CONTchunk .= getid3_lib::BigEndian2String(strlen(@$this->tag_data['comment']), 2);
+ $CONTchunk .= @$this->tag_data['comment'];
+
+ if ($this->paddedlength > (strlen($CONTchunk) + 8)) {
+ $CONTchunk .= str_repeat("\x00", $this->paddedlength - strlen($CONTchunk) - 8);
+ }
+
+ $CONTchunk = 'CONT'.getid3_lib::BigEndian2String(strlen($CONTchunk) + 8, 4).$CONTchunk; // CONT chunk identifier + chunk length
+
+ return $CONTchunk;
}
function RemoveReal() {
@@ -116,22 +224,69 @@ class getid3_write_real
if (is_writeable($this->filename)) {
if ($fp_source = @fopen($this->filename, 'r+b')) {
-return false;
- //fseek($fp_source, -128, SEEK_END);
- //if (fread($fp_source, 3) == 'TAG') {
- // ftruncate($fp_source, filesize($this->filename) - 128);
- //} else {
- // // no real tag to begin with - do nothing
- //}
+ // Initialize getID3 engine
+ $getID3 = new getID3;
+ $OldThisFileInfo = $getID3->analyze($this->filename);
+ if (empty($OldThisFileInfo['real']['chunks']) && !empty($OldThisFileInfo['real']['old_ra_header'])) {
+ $this->errors[] = 'Cannot remove Real tags from old-style file format';
+ fclose($fp_source);
+ return false;
+ }
+
+ if (empty($OldThisFileInfo['real']['chunks'])) {
+ $this->errors[] = 'Cannot remove Real tags because cannot find DATA chunk in file';
+ fclose($fp_source);
+ return false;
+ }
+ foreach ($OldThisFileInfo['real']['chunks'] as $chunknumber => $chunkarray) {
+ $oldChunkInfo[$chunkarray['name']] = $chunkarray;
+ }
+
+ if (empty($oldChunkInfo['CONT'])) {
+ // no existing CONT chunk
+ fclose($fp_source);
+ return true;
+ }
+
+ $BeforeOffset = $oldChunkInfo['CONT']['offset'];
+ $AfterOffset = $oldChunkInfo['CONT']['offset'] + $oldChunkInfo['CONT']['length'];
+ if ($tempfilename = tempnam('*', 'getID3')) {
+ ob_start();
+ if ($fp_temp = fopen($tempfilename, 'wb')) {
+
+ rewind($fp_source);
+ fwrite($fp_temp, fread($fp_source, $BeforeOffset));
+ fseek($fp_source, $AfterOffset, SEEK_SET);
+ while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) {
+ fwrite($fp_temp, $buffer, strlen($buffer));
+ }
+ fclose($fp_temp);
+
+ if (copy($tempfilename, $this->filename)) {
+ unlink($tempfilename);
+ fclose($fp_source);
+ return true;
+ }
+ unlink($tempfilename);
+ $this->errors[] = 'FAILED: copy('.$tempfilename.', '.$this->filename.') - '.strip_tags(ob_get_contents());
+
+ } else {
+
+ $this->errors[] = 'Could not open '.$tempfilename.' mode "wb" - '.strip_tags(ob_get_contents());
+
+ }
+ ob_end_clean();
+ }
fclose($fp_source);
- return true;
+ return false;
+
} else {
$this->errors[] = 'Could not open '.$this->filename.' mode "r+b"';
+ return false;
}
- } else {
- $this->errors[] = $this->filename.' is not writeable';
}
+ $this->errors[] = 'File is not writeable: '.$this->filename;
return false;
}
diff --git a/modules/init.php b/modules/init.php
index fa57c29e..0fc904c4 100644
--- a/modules/init.php
+++ b/modules/init.php
@@ -1,7 +1,7 @@
<?php
/*
- Copyright (c) 2001 - 2005 Ampache.org
+ Copyright (c) 2001 - 2006 Ampache.org
All rights reserved.
This program is free software; you can redistribute it and/or
@@ -83,7 +83,7 @@ if (!$results['conf']['allow_stream_playback']) {
$results['conf']['raw_web_path'] = $results['conf']['web_path'];
$results['conf']['web_path'] = $http_type . $_SERVER['HTTP_HOST'] . $results['conf']['web_path'];
-$results['conf']['version'] = '3.3.2-Beta1 (Build 001)';
+$results['conf']['version'] = '3.3.2-Beta1 (Build 002)';
$results['conf']['catalog_file_pattern']= 'mp3|mpc|m4p|m4a|mp4|aac|ogg|rm|wma|asf|flac|spx';
$results['libglue']['local_table'] = 'session';
$results['libglue']['local_sid'] = 'id';
diff --git a/modules/validatemail/validateEmail.php b/modules/validatemail/validateEmail.php
new file mode 100644
index 00000000..e8cc037a
--- /dev/null
+++ b/modules/validatemail/validateEmail.php
@@ -0,0 +1,638 @@
+<?php
+/*
+validateEmail.php
+version 2.0
+by Clay Loveless <clay@killersoft.com>
+
+
+Originally
+By: Jon S. Stevens jon@clearink.com
+Copyright 1998 Jon S. Stevens, Clear Ink
+This code has all the normal disclaimers.
+It is free for any use, just keep the credits intact.
+
+Enhancements and modifications:
+
+ By: Shane Y. Gibson shane@tuna.org
+Organization: The Unix Network Archives (http://www.tuna.org/)
+ Date: November 16th, 1998
+ Changes: - Added **all** comments, as original code lacked them.
+ - Added some return codes to include a bit more description
+ for useability.
+
+ By: berber
+Organization: webdev.berber.co.il
+ Date: April 10th, 1999
+ Changes: - The script now handles all kinds of domains (not only @xxx.yyy) as before.
+ - Added a debugging mode which also works as a verbose mode.
+
+ By: Frank Vogel vogel@simec.com
+Organization: Simec Corp. (http://www.simec.com)
+ Date: June 13th, 2000
+ Changes: - Check for MX records for each qualification step of the domain name
+ - Use nobody@$SERVER_NAME as MAIL FROM: argument
+ Disclaimers: I disclaim nothing...nor do I claim anything...but
+ it would be nice if you included this disclaimer...
+
+
+ NOTE: berber and Frank Vogel made some of the same changes regarding
+ domain name checking to seperate versions of Shane Gibson's validateEmail variant.
+ Their changes have been merged into version 2.0.
+
+
+ By: Clay Loveless <clay@killersoft.com>
+Organization: KillerSoft < http://www.killersoft.com/ >
+ Date: March 12th, 2002
+ Changes: - Added 'Preferences' section, enabling several variables to be easily set
+ - Changed "nobody@$SERVER_NAME" for MAIL FROM: argument to be
+ "$from@$serverName" - set via Preferences section
+ - Signifcantly enhanced berber's 'debug' mode. It has become 'Verbose' mode
+ to ease debugging.
+ - Made 'Verbose' mode a function argument. Call validateEmail($email,1) to enable.
+ - Added environment detection - 'Verbose' output is adaptable to command-line
+ execution and embedded web execution.
+ - Added $socketTimeout Preferences variable for controlling how long we'll wait
+ during fsockopen() to any given host.
+ - Added $waitTimeout Preferences variable to control how long we'll wait for
+ a server we've successfully connected with to actually respond with an SMTP greeting.
+ Note -- this is a complete replacement of the previous "wait" method of simply
+ increasing a counter, which proved extremely inadequate in testing on sluggish hosts.
+ - Added $mxcutoff Preferences variable to control how many MX hosts we're willing to
+ talk to before calling it quits. (So we're not required to hear "no" from 14
+ hotmail.com mail servers if we don't want to.)
+ - Added routine to check SMTP server greeting line for ESTMP, and respond accordingly
+ with EHLO.
+ - Added routines to listen for multi-line output from servers.
+ - Fixed all commands ending in "\n" to end in "\r\n" as specified by accurate SMTP
+ communication. THIS FIXES THE "HANG" PROBLEM EXPERIENCED WITH MANY MAIL SERVERS,
+ INCLUDING AOL.COM. (See Disclaimers about AOL.com connections, though ...)
+ - Added support for Jeffrey E.F. Friedl's definitive email format regex, translated
+ from perl into PHP. Will reject email addresses with invalid formatting before
+ opening any server connections.
+ - Changed initial "listening" routine to listen for one of two SMTP greeting responses
+ (220 or 421) instead of just listening for anything. validateEmail is now well-behaved
+ if a 421 "temporary rejection" code is received.
+ - Assorted optimizations -- using explode() instead of split(), preg_match()
+ instead of ereg(), etc.
+ - Improved error reporting on failures.
+ - Fixed typos in comments. : )
+ - Modified comments where Shane Gibson's were no longer needed or accurate (due to changes).
+ Added the comments for features that didn't exist in Shane's version.
+ - Incremented version number.
+
+ Disclaimers: - All additions and modifications Copyright 2002 KillerSoft.com.
+ - Program is free for any use as long as these notes & credits remain intact.
+ - Yes, I know there is no foolproof way to validate an e-mail address. But this is better than
+ nothing.
+ - Yes, I know that fewer and fewer mail servers are supporting the type of connection
+ and validation that this script performs. There are still a hell of a lot more of them
+ that DO support it than those that DON'T. Yes, this may change over time.
+ - This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ - By using this code you agree to indemnify Clay Loveless, KillerSoft, and Crawlspace, Inc.
+ from any liability that might arise from its use.
+ - Use at your own risk. This may not work for you. It may produce results other than what you'd expect,
+ or even prefer.
+ - AOL.COM Disclaimer: As of this release, mail servers operated by AOL.com (netscape.com,
+ aol.com, cs.com, anything with aoltw.net, just to name a few) return "250" (recipient OK) codes for
+ _any_ address you throw at them. Bounces for invalid recipients are handled and sent out
+ through alternate means. So -- this script won't help you in validating AOL.com (and affiliated)
+ e-mail addresses. BUT ... at least it won't choke/hang on them either, as previous versions
+ of this script would.
+
+ - Please send bugs, comments or suggestions to info@killersoft.com!
+*/
+
+/* This function takes in an email address (say 'shane@tuna.org')
+* and tests to see if it's a valid email address.
+*
+* An array with the results is passed back to the caller.
+*
+* Possible result codes for the array items are:
+*
+* Item 0: [true|false] true for valid email address
+* false for NON-valid email address
+*
+* Item 1: [SMTP Code] if a valid MX mail server found, then
+* fill this array in with failed SMTP
+* reply codes
+* IF no MX mail server found or connected to,
+* errors will be explained in this response.
+*
+* Possible Internal error messages:
+* Invalid email address (bad domain name) [ default message from the old days ]
+* fsockopen error $errno: $errstr
+* 554 No MX records found for $domain
+* 554 No DNS reverse record found for $domain
+*
+* (554 Response code borrowed from ESMTP's "Transaction failed" response)
+*
+* Item 2: [true|false] true for valid mail server found for
+* host/domain
+* false if no valid mail server found
+*
+* Item 3: [MX server] if a valid MX host was found and
+* connected to then fill in this item
+* with the MX server hostname
+*
+* EXAMPLE CODE for use is available at:
+* http://www.killersoft.com/contrib/
+*/
+
+function validateEmail ( $email, $verbose=0 ) {
+ global $SERVER_NAME;
+
+ // DEFINE PREFERENCES
+
+ // Passed along with the HELO/EHLO statement.
+ // Leave blank to use $SERVER_NAME.
+ // Note that most modern MTAs will ignore (but require) whatever you say here ...
+ // the server will determine your domain via other means.
+ if (conf('mail_domain')){
+ $serverName = conf('mail_domain');
+ } else {
+ $serverName = "domain.tld";
+ }
+ // MAIL FROM -- who's asking?
+ // Good values: nobody, postmaster, info, buckwheat, gumby
+ $from = "info";
+
+ // fsockopen() timeout - in seconds
+ $socketTimeout = 15;
+
+ // waitTimeout - how long we'll wait for a server to respond after
+ // a successful connection. In seconds.
+ // Recommended to keep this above 35 seconds - some servers are really slow.
+ $waitTimeout = 50;
+
+ // MX Server cutoff
+ // Some hosts (like hotmail.com) have MANY MX hosts -- 12 or more.
+ // Set this to a number where you'd like to say "I get the picture"
+ // ... so you don't wind up having to hit EVERY MX host.
+ $mxcutoff = 15;
+
+ // END OF PREFERENCES
+
+ ///////////////////////////////////////////////////////////////////////////////
+ // DO NOT EDIT BELOW THIS LINE
+ ///////////////////////////////////////////////////////////////////////////////
+
+
+ // Default initiation statement
+ $send = "HELO";
+
+ // Let's give good commands
+ $CRLF = "\r\n";
+
+ // Make a few adjustments for verbose mode
+ if ( $verbose ) {
+
+ // Version
+ $version = "validateEmail 2.0 - http://killersoft.com/contrib/";
+
+ // Start stopwatch
+ list ( $msecStart, $secStart ) = explode ( " ", microtime() );
+
+ // Adjust verbose output format
+ // for php.cgi or webserver interface
+ $sapi_type = php_sapi_name();
+ if ( $sapi_type == "cgi" ) {
+ // format < >
+ $leftCarrot = "<";
+ $rightCarrot = ">";
+ // set type of "new line"
+ $vNL = "echo \"\n\";";
+ // verbose Flush Only
+ $vFlush = "";
+ // output for debugging
+ eval("echo \"Internal: $version - running as ".AddSlashes($sapi_type)."\"; $vNL");
+ } else {
+ // format < >
+ $leftCarrot = "&lt;";
+ $rightCarrot = "&gt;";
+ // set type of "new line" ... flush output for web browsing
+ echo "<pre>";
+ $vNL = "echo \"\n\"; flush();";
+ // verbose Flush Only
+ $vFlush = "flush();";
+ // output for debugging
+ eval("echo \"Internal: $version - running as ".AddSlashes($sapi_type)."\"; $vNL");
+ }
+ }
+
+ // How we'll identify ourselves in SMTP HELO/EHLO argument
+ if ( $serverName == "" ) $serverName = "$SERVER_NAME";
+ if ( $serverName == "" ) $serverName = "localhost";
+
+ // Initialize return values with default
+ $return[0] = false;
+ $return[1] = "Invalid email address (bad domain name)";
+ $return[2] = false;
+ $return[3] = "";
+
+ // make sure that we're dealing with a valid email address format
+ $isValid = true; // just in case validateEmailFormat is not available
+ if ( function_exists('validateEmailFormat') ) $isValid = validateEmailFormat ( $email );
+
+ // abort if necessary
+ if ( !$isValid ) {
+ if ( $verbose ) eval("echo \"Internal: $email format is invalid! Quitting ...\"; $vNL");
+ return $return;
+
+ } else {
+ if ( $verbose ) eval("echo \"Internal: $email is a valid RFC 822 formatted address\"; $vNL");
+
+ // assign our user part and domain parts respectively to seperate
+ // variables
+ list ( $user, $domain ) = explode ( "@", $email );
+ if ( $verbose ) {
+ eval("echo \"Internal: user ..... $user\"; $vNL");
+ eval("echo \"Internal: domain ... $domain\"; $vNL");
+ }
+
+ // split up the domain into sub-parts
+ $arr = explode ( ".", $domain );
+
+ // figure out how many parts there are in the host/domain name portion
+ $count = count ( $arr );
+
+ // flag to indicate success
+ $bSuccess = false;
+
+ // we try this for each qualification step of domain name
+ // (from full qualified to TopLevel)
+ for ( $i = 0; $i < $count - 1 && !$bSuccess; $i = $i + 1 ) {
+
+ // create the domain name
+ $domain = "";
+ for ( $j = $i; $j < $count; $j = $j + 1 ) {
+ $domain = $domain . $arr[$j];
+ if ( $j < $count - 1 )
+ // tack on the last dot
+ $domain = $domain . ".";
+ }
+ if ( $verbose ) eval("echo \"Internal: checking DNS for $domain ... \"; $vNL");
+
+ // check that an MX record exists for Top-Level domain
+ // If it exists, start our email address checking
+ if ( checkdnsrr ( $domain, "MX" ) ) {
+
+ // Okay -- we've got a valid DNS reverse record.
+ if ( $verbose ) eval("echo \"Internal: ... Check DNS RR OK!\"; $vNL");
+ // Test that MX record for host exists,
+ // then fill 'mxhosts' and 'weight' arrays with correct info
+ if ( getmxrr ( $domain, $mxhosts, $weight ) ) {
+
+ // Now we've got MX records
+ if ( $verbose ) {
+ eval("echo \"Internal: MX LOOKUP RESULTS:\"; $vNL");
+ for ( $i = 0; $i < count ( $mxhosts ); $i++) {
+ eval("echo \" $mxhosts[$i]\"; $vNL");
+ }
+ }
+ // sift through the 'mxhosts', connecting to each one
+ // ONLY until we get a good match
+ $mxcount = count( $mxhosts );
+ // determine our MX host cutoff
+ $mxstop = ($mxcount > $mxcutoff) ? $mxcutoff : $mxcount;
+ for ( $i = 0; $i < $mxstop ; $i++ ) {
+
+ // open socket on port 25 to mxhost, setting
+ // returned socket pointer to $sp
+ if( $verbose ) eval("echo \"Internal: attempting to open $mxhosts[$i] ...\"; $vNL");
+ $sp = fsockopen ( $mxhosts[$i], 25, $errno, $errstr, $socketTimeout);
+
+ // Greeting Code default
+ // Sets default greeting code to 421, just in case we
+ // don't ever hear ANYTHING from this host.
+ // If we hear nothing, we'll want to skip it.
+ $greetCode = "421";
+
+ // if $sp connection is good, let's rock on
+ if ( $sp ) {
+ if ( $verbose ) {
+ eval("echo \"* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\"; $vNL");
+ eval("echo \"Internal: socket open to $mxhosts[$i]\"; $vNL");
+ }
+ // work variables
+ $waitMarker = 0;
+ $msec = 0; // milisec count
+ $tsec = 0; // tensec count
+ $out = "";
+
+ // set our created socket for $sp to
+ // non-blocking mode so that our fgets()
+ // calls will return with a quickness
+ set_socket_blocking ( $sp, false );
+
+ // as long as our 'out' variable does not begin
+ // with a valid SMTP greeting (220 or 421),
+ // keep looping (do) until we get something
+ do {
+
+ // prepare for clean debug output if necessary
+ // (puts a line break after the waitMarkers)
+ if ( $verbose && $msec > 0 ) {
+ $elapsed = $tsec + ($msec/4);
+ $clean = "echo \"($elapsed seconds)\"; $vNL";
+ }
+ // output of the stream assigned to
+ // 'out' variable
+ $out = fgets ( $sp, 2500 );
+
+ // Check for multi-line output (###-)
+ if ( preg_match ( "/^2..-/", $out ) ) {
+ $end = false;
+ while ( !$end ) {
+ // keep listening
+ $line = fgets ( $sp, 2500 );
+ $out .= $line;
+ if ( preg_match ( "/^2.. /", $line ) ) {
+ // the last line of output shouldn't
+ // have a dash after the response code
+ $end = true;
+ }
+ }
+ }
+
+ if ( $verbose && $out != "" ) eval("$clean echo \"Server: ".AddSlashes($out)."\"; $vNL");
+
+ // if we get a "220" code (service ready),
+ // we're ready to rock on
+ if ( substr ( $out, 0, 3 ) == "220" ) {
+ if ( $verbose ) eval("echo \"Internal: service ready on $mxhosts[$i] ... moving on\"; $vNL");
+ $return[2] = true;
+ $return[3] = "$mxhosts[$i]";
+ // determine if we should speak in terms of HELO or EHLO
+ if ( preg_match ( "/ESMTP/", $out ) ) {
+ $send = "HELO";
+ } else {
+ $send = "HELO";
+ }
+
+ // Set Greeting Code
+ $greetCode = "220";
+
+ }
+
+ // else if ...
+ // Perhaps we've gotten a 421 Temporarily Refused error
+ else if ( substr ( $out, 0, 3 ) == "421" ) {
+
+ //if ( $verbose ) echo " ... moving on\n";
+ if ( $verbose ) eval("echo \"Internal: $mxhosts[$i] temporarily rejected connection. (421 response)\"; $vNL");
+ $return[2] = false;
+ // Set Greeting Code
+ $greetCode = "421";
+ break; // get out of this loop
+
+ }
+
+ // increase our waitTimeout counters
+ // if we still haven't heard anything ...
+ // Note that the time looping isn't an exact science
+ // with usleep or the Windows hack ... but
+ // it's in the ballpark. Close enough.
+ if ( $out == "" && $msec < $waitTimeout ) {
+
+ // wait for a quarter of a second
+ if ( $verbose ) {
+ if ( $msec == 0 ) {
+ eval("echo \"Internal: Waiting: one '.' ~ 0.25 seconds of waiting\"; $vNL");
+ }
+ eval("echo \".\"; $vFlush");
+ $waitMarker++;
+ if ( $waitMarker == 40 ) {
+ // ten seconds
+ $tsec += 10;
+ eval("echo \" ($tsec seconds)\"; $vNL");
+ $waitMarker = 0;
+ }
+ }
+ $msec = $msec + 0.25;
+ usleep(250000);
+
+ } elseif ( $msec == $waitTimeout ) {
+
+ // let's get out of here. Toooo sloooooww ...
+ if ( $verbose ) eval("$clean echo \"Internal: !! we've waited $waitTimeout seconds !!\nbreaking ...\"; $vNL");
+ break;
+
+ }
+
+
+ // end of 'do' loop
+ } while ( substr ( $out, 0, 3 ) != "220" );
+
+ // Make sure we got a "220" greetCode
+ // before we start shoveling requests
+ // at this server.
+ if ( $greetCode == "220" ) {
+
+ // reset our file pointer to blocking mode,
+ // so we can wait for communication to finish
+ // before moving on ...
+ set_socket_blocking ( $sp, true );
+
+ // talk to the MX mail server, attempt to validate
+ // ourself. Use "HELO" or "EHLO", as determined above
+ fputs ( $sp, "$send $serverName"."$CRLF" );
+ if ( $verbose ) eval("echo \"Client: $send $serverName\"; $vNL");
+
+ // get the mail server's reply, check it
+ //
+ $originalOutput = fgets ( $sp, 2500 );
+ // Check for multi-line positive output
+ if ( preg_match ( "/^...-/", $originalOutput ) ) {
+ $end = false;
+ while ( !$end ) {
+ // keep listening
+ $line = fgets ( $sp, 2500 );
+ $originalOutput .= $line;
+ if ( preg_match ( "/^... /", $line ) ) {
+ // the last line of output shouldn't
+ // have a dash after the response code
+ $end = true;
+ }
+ }
+ }
+ if ( $verbose ) eval("echo \"Server: ".AddSlashes($originalOutput)."\"; $vNL");
+
+
+ // if there's a HELP option, let's see it
+ if ( $verbose ) {
+ if( preg_match( "/250.HELP/m", $originalOutput ) && $verbose == true ) {
+
+ eval("echo \"Internal: VERBOSE-MODE ONLY: Getting the HELP output\"; $vNL");
+ // Get the output of the HELP command
+ fputs ( $sp, "HELP"."$CRLF" );
+ if ( $verbose ) eval("echo \"Client: HELP\"; $vNL");
+ // Get output again
+ $output = fgets ( $sp, 2500 );
+ // Check for multi-line positive output
+ if ( preg_match ( "/^...-/", $output ) ) {
+ $end = false;
+ while ( !$end ) {
+ // keep listening
+ $line = fgets ( $sp, 2500 );
+ $output .= $line;
+ if ( preg_match ( "/^... /", $line ) ) {
+ // the last line of output shouldn't
+ // have a dash after the response code
+ $end = true;
+ }
+ }
+ }
+ if ( $verbose ) eval("echo \"Server: ".AddSlashes($output)."\"; $vNL");
+
+ }
+ }
+
+ // Give the MAIL FROM: header to the server
+ fputs ( $sp, "MAIL FROM: <$from" . "@" . "$serverName" . ">"."$CRLF");
+ if ( $verbose ) eval("echo \"Client: MAIL FROM: $leftCarrot"."$from" . "@" . "$serverName" . "$rightCarrot\"; $vNL");
+
+ // Get output again
+ $output = fgets ( $sp, 2500 );
+ // Check for multi-line positive output
+ if ( preg_match ( "/^...-/", $output ) ) {
+ $end = false;
+ while ( !$end ) {
+ // keep listening
+ $line = fgets ( $sp, 2500 );
+ $output .= $line;
+ if ( preg_match ( "/^... /", $line ) ) {
+ // the last line of output shouldn't
+ // have a dash after the response code
+ $end = true;
+ }
+ }
+ }
+ if ( $verbose ) eval("echo \"Server: ".AddSlashes($output)."\"; $vNL");
+
+ // Give the RCPT TO: header for the email address we're testing
+ fputs ( $sp, "RCPT TO: <$email>"."$CRLF" );
+ if ( $verbose ) eval("echo \"Client: RCPT TO: $leftCarrot"."$email"."$rightCarrot\"; $vNL");
+
+ // Get output again
+ // This will be the one we check for validity
+ $output = fgets ( $sp, 2500 );
+ // Check for multi-line positive output
+ if ( preg_match ( "/^...-/", $output ) ) {
+ $end = false;
+ while ( !$end ) {
+ // keep listening
+ $line = fgets ( $sp, 2500 );
+ $output .= $line;
+ if ( preg_match ( "/^... /", $line ) ) {
+ // the last line of output shouldn't
+ // have a dash after the response code
+ $end = true;
+ }
+ }
+ }
+ if ( $verbose ) eval("echo \"Server: ".AddSlashes($output)."\"; $vNL");
+
+ // test the last reply code from the mail server
+ // for the 250 (okay) response
+ if ( substr ( $output, 0, 3 ) == "250" ) {
+
+ // set our true/false(ness)
+ // array item for testing
+ $return[0] = true;
+ $return[1] = $output;
+ if ( $verbose ) eval("echo \"Internal: Check for 250 ... Recipient OK\"; $vNL");
+
+ } else {
+
+ // we didn't get a 250
+ // may be a bogus address
+ if ( $verbose ) eval("echo \"Internal: Check for 250 ... Response did not begin with 250!\"; $vNL");
+ // fill in 2nd array item with mail server's
+ // reply for user to test if they want
+ $return[0] = false;
+ $return[1] = $output;
+
+ }
+
+ // tell the mail server we're done
+ fputs ( $sp, "QUIT"."$CRLF" );
+ if ( $verbose ) {
+ eval("echo \"Client: QUIT\"; $vNL");
+ eval("echo \"* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\"; $vNL $vNL");
+ }
+
+ // close the socket/file pointer
+ fclose ( $sp );
+
+ // If we got a good response back on RCPT TO,
+ // break here
+ // Otherwise, keep trying MX servers until we
+ // get a good response or run out of MX servers
+ // to try.
+ if ( $return[0] == true ) {
+ if ( $verbose ) {
+ eval("echo \"Internal: Recipient is OK - thanks, $mxhosts[$i]!\"; $vNL");
+ eval("echo \"Internal: Stop checking MX hosts ...\"; $vNL");
+ }
+ $bSuccess = true;
+ break;
+ }
+
+ } else {
+
+ // greetCode wasn't "220"
+ // we better skip this one and move on
+ if ( $verbose ) eval("echo \"Internal: SKIPPING $mxhosts[$i] -- never got 220 welcome\"; $vNL");
+ // close out this connection
+ fclose ( $sp );
+
+ } // end of greetCode check
+
+ } else {
+ // $sp socket pointer was false -- couldn't open it
+ if ( $verbose ) {
+ eval("echo \"* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\"; $vNL");
+ eval("echo \"Internal: could not open socket to $mxhosts[$i]!\"; $vNL");
+ eval("echo \"fsockopen error $errno: $errstr\"; $vNL");
+ eval("echo \"* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\"; $vNL $vNL");
+ }
+ $return[0] = false;
+ $return[1] = "fsockopen error $errno: $errstr";
+ } // end of $sp check
+
+ } // end for $mxhosts
+
+ } // getmxrr test
+ else {
+ // getmxrr failed
+ if ( $verbose ) eval("echo \"Internal: No MX reverse records found for $domain\"; $vNL");
+ $return[0] = false;
+ $return[1] = "554 No MX records found for $domain";
+ } // end getmxrr test
+
+ } // continue checkdnsrr test
+ else {
+ if ( $verbose ) eval("echo \"Internal: No DNS Reverse Record available!\"; $vNL");
+ $return[0] = false;
+ $return[1] = "554 No DNS reverse record found for $domain";
+ } // end checkdnsrr test
+
+ } // end walking through each domain possibility
+
+ } // end isValid
+
+ // output elapsed time if Verbose
+ if ( $verbose ) {
+ list ( $msecStop, $secStop ) = explode ( " ", microtime() );
+ $elapsedTime = (double)($secStop + $msecStop) - ($secStart + $msecStart);
+ $elapsedTime = number_format($elapsedTime,3);
+ eval("echo \"Internal: VERBOSE-MODE execution time: $elapsedTime seconds (silent mode somewhat faster)\"; $vNL");
+ if ( $sapi_type != "cgi" ) echo "</pre>";
+ }
+
+ // return the array for the user to test against
+ return $return;
+
+} // END validateEmail-2.0
+?>
diff --git a/modules/validatemail/validateEmailFormat.php b/modules/validatemail/validateEmailFormat.php
new file mode 100644
index 00000000..aa03e62d
--- /dev/null
+++ b/modules/validatemail/validateEmailFormat.php
@@ -0,0 +1,219 @@
+<?php
+////////////////////////////////////////////////////////////////////////
+//
+// validateEmailFormat.php - v 1.0
+//
+// PHP translation of Email Regex Program (optimized)
+// Derived from:
+// Appendix B - Email Regex Program
+// _Mastering Regular Expressions_ (First Edition, May 1997 revision)
+// by Jeffrey E.F. Friedl
+// Copyright 1997 O'Reilly & Associates
+// ISBN: 1-56592-257-3
+// For more info on this title, see:
+// http://www.oreilly.com/catalog/regex/
+// For original perl version, see:
+// http://examples.oreilly.com/regex/
+//
+// Follows RFC 822 about as close as is possible.
+// http://www.faqs.org/rfcs/rfc822.html
+//
+//
+// DESCRIPTION:
+// bool validateEmailFormat ( string string )
+//
+// Returns TRUE if the email address passed is in a valid format
+// according to RFC 822, returns FALSE if email address passed
+// is not in a valid format.
+//
+// EXAMPLES:
+// Example #1:
+// $email = "Jeffy <\"That Tall Guy\"@foo.com (blah blah blah)";
+// $isValid = validateEmailFormat($email);
+// if($isValid) {
+// ... // Yes, the above address is a valid format!
+// } else {
+// echo "sorry, that address isn't formatted properly.";
+// }
+//
+// Example #2:
+// $email = "foo@bar.co.il";
+// $isValid = validateEmailFormat($email);
+// if($isValid) {
+// ...
+// } else {
+// echo "sorry ...";
+// }
+//
+// Translated by Clay Loveless <clay@killersoft.com> on March 11, 2002
+// ... in hopes that the "here's how to check an e-mail address!"
+// discussion can finally end. After all ...
+//
+// Friedl is the master -- Hail to the King, baby!
+//
+////////////////////////////////////////////////////////////////////////
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+//
+// Hell, it might not even work for you.
+//
+// By using this code you agree to indemnify Clay Loveless,
+// KillerSoft, and Crawlspace, Inc. from any liability that might
+// arise from its use.
+//
+// Have fun!
+//
+////////////////////////////////////////////////////////////////////////
+function validateEmailFormat ( $email ) {
+
+ // Some shortcuts for avoiding backslashitis
+ $esc = '\\\\'; $Period = '\.';
+ $space = '\040'; $tab = '\t';
+ $OpenBR = '\['; $CloseBR = '\]';
+ $OpenParen = '\('; $CloseParen = '\)';
+ $NonASCII = '\x80-\xff'; $ctrl = '\000-\037';
+ $CRlist = '\n\015'; // note: this should really be only \015.
+
+ // Items 19, 20, 21 -- see table on page 295 of 'Mastering Regular Expressions'
+ $qtext = "[^$esc$NonASCII$CRlist\"]"; // for within "..."
+ $dtext = "[^$esc$NonASCII$CRlist$OpenBR$CloseBR]"; // for within [...]
+ $quoted_pair = " $esc [^$NonASCII] "; // an escaped character
+
+ // *********************************************
+ // Items 22 and 23, comment.
+ // Impossible to do properly with a regex, I make do by allowing at most
+ // one level of nesting.
+ $ctext = " [^$esc$NonASCII$CRlist()] ";
+
+ // $Cnested matches one non-nested comment.
+ // It is unrolled, with normal of $ctext, special of $quoted_pair.
+ $Cnested = "";
+ $Cnested .= "$OpenParen"; // (
+ $Cnested .= "$ctext*"; // normal*
+ $Cnested .= "(?: $quoted_pair $ctext* )*"; // (special normal*)*
+ $Cnested .= "$CloseParen"; // )
+
+ // $comment allows one level of nested parentheses
+ // It is unrolled, with normal of $ctext, special of ($quoted_pair|$Cnested)
+ $comment = "";
+ $comment .= "$OpenParen"; // (
+ $comment .= "$ctext*"; // normal*
+ $comment .= "(?:"; // (
+ $comment .= "(?: $quoted_pair | $Cnested )"; // special
+ $comment .= "$ctext*"; // normal*
+ $comment .= ")*"; // )*
+ $comment .= "$CloseParen"; // )
+
+ // *********************************************
+ // $X is optional whitespace/comments
+ $X = "";
+ $X .= "[$space$tab]*"; // Nab whitespace
+ $X .= "(?: $comment [$space$tab]* )*"; // If comment found, allow more spaces
+
+
+ // Item 10: atom
+ $atom_char = "[^($space)<>\@,;:\".$esc$OpenBR$CloseBR$ctrl$NonASCII]";
+ $atom = "";
+ $atom .= "$atom_char+"; // some number of atom characters ...
+ $atom .= "(?!$atom_char)"; // ... not followed by something that
+ // could be part of an atom
+
+ // Item 11: doublequoted string, unrolled.
+ $quoted_str = "";
+ $quoted_str .= "\""; // "
+ $quoted_str .= "$qtext *"; // normal
+ $quoted_str .= "(?: $quoted_pair $qtext * )*"; // ( special normal* )*
+ $quoted_str .= "\""; // "
+
+
+ // Item 7: word is an atom or quoted string
+ $word = "";
+ $word .= "(?:";
+ $word .= "$atom"; // Atom
+ $word .= "|"; // or
+ $word .= "$quoted_str"; // Quoted string
+ $word .= ")";
+
+ // Item 12: domain-ref is just an atom
+ $domain_ref = $atom;
+
+ // Item 13: domain-literal is like a quoted string, but [...] instead of "..."
+ $domain_lit = "";
+ $domain_lit .= "$OpenBR"; // [
+ $domain_lit .= "(?: $dtext | $quoted_pair )*"; // stuff
+ $domain_lit .= "$CloseBR"; // ]
+
+ // Item 9: sub-domain is a domain-ref or a domain-literal
+ $sub_domain = "";
+ $sub_domain .= "(?:";
+ $sub_domain .= "$domain_ref";
+ $sub_domain .= "|";
+ $sub_domain .= "$domain_lit";
+ $sub_domain .= ")";
+ $sub_domain .= "$X"; // optional trailing comments
+
+ // Item 6: domain is a list of subdomains separated by dots
+ $domain = "";
+ $domain .= "$sub_domain";
+ $domain .= "(?:";
+ $domain .= "$Period $X $sub_domain";
+ $domain .= ")*";
+
+ // Item 8: a route. A bunch of "@ $domain" separated by commas, followed by a colon.
+ $route = "";
+ $route .= "\@ $X $domain";
+ $route .= "(?: , $X \@ $X $domain )*"; // additional domains
+ $route .= ":";
+ $route .= "$X"; // optional trailing comments
+
+ // Item 5: local-part is a bunch of $word separated by periods
+ $local_part = "";
+ $local_part .= "$word $X";
+ $local_part .= "(?:";
+ $local_part .= "$Period $X $word $X"; // additional words
+ $local_part .= ")*";
+
+ // Item 2: addr-spec is local@domain
+ $addr_spec = "$local_part \@ $X $domain";
+
+ // Item 4: route-addr is <route? addr-spec>
+ $route_addr = "";
+ $route_addr .= "< $X";
+ $route_addr .= "(?: $route )?"; // optional route
+ $route_addr .= "$addr_spec"; // address spec
+ $route_addr .= ">";
+
+ // Item 3: phrase........
+ $phrase_ctrl = '\000-\010\012-\037'; // like ctrl, but without tab
+
+ // Like atom-char, but without listing space, and uses phrase_ctrl.
+ // Since the class is negated, this matches the same as atom-char plus space and tab
+ $phrase_char = "[^()<>\@,;:\".$esc$OpenBR$CloseBR$NonASCII$phrase_ctrl]";
+
+ // We've worked it so that $word, $comment, and $quoted_str to not consume trailing $X
+ // because we take care of it manually.
+ $phrase = "";
+ $phrase .= "$word"; // leading word
+ $phrase .= "$phrase_char *"; // "normal" atoms and/or spaces
+ $phrase .= "(?:";
+ $phrase .= "(?: $comment | $quoted_str )"; // "special" comment or quoted string
+ $phrase .= "$phrase_char *"; // more "normal"
+ $phrase .= ")*";
+
+ // Item 1: mailbox is an addr_spec or a phrase/route_addr
+ $mailbox = "";
+ $mailbox .= "$X"; // optional leading comment
+ $mailbox .= "(?:";
+ $mailbox .= "$addr_spec"; // address
+ $mailbox .= "|"; // or
+ $mailbox .= "$phrase $route_addr"; // name and address
+ $mailbox .= ")";
+
+ // test it and return results
+ $isValid = preg_match("/^$mailbox$/xS",$email);
+
+ return($isValid);
+} // END validateEmailFormat
+?>