From 83f3c4114164617f48f3b013027f9079ddc2675c Mon Sep 17 00:00:00 2001 From: Karl 'vollmerk' Vollmer Date: Mon, 2 Jan 2006 01:06:06 +0000 Subject: updated registration mojo thx pb1dft also some fluf updates --- activate.php | 49 ++ albumart.php | 2 +- albums.php | 3 + artists.php | 3 + browse.php | 2 +- config/ampache.cfg.php.dist | 12 + docs/CHANGELOG | 2 + flag.php | 3 + genre.php | 3 +- index.php | 2 +- install.php | 2 +- lib/artist.lib.php | 3 + lib/batch.lib.php | 25 +- lib/class/access.class.php | 3 + lib/class/album.class.php | 5 +- lib/class/artist.class.php | 2 +- lib/class/catalog.class.php | 3 + lib/class/error.class.php | 2 +- lib/class/genre.class.php | 2 +- lib/class/playlist.class.php | 2 +- lib/class/rating.class.php | 2 +- lib/class/song.class.php | 2 +- lib/class/stream.class.php | 2 +- lib/class/update.class.php | 2 +- lib/class/user.class.php | 33 +- lib/class/view.class.php | 2 +- lib/debug.php | 2 +- lib/duplicates.php | 22 + lib/flag.php | 3 +- lib/general.lib.php | 22 + lib/gettext.php | 3 + lib/install.php | 2 +- lib/log.lib.php | 3 + lib/mpd.php | 3 + lib/preferences.php | 2 +- lib/rss.php | 2 +- lib/search.php | 2 +- lib/song.php | 3 + lib/stream.lib.php | 2 +- lib/themes.php | 2 +- lib/ui.lib.php | 64 ++- lib/upload.php | 3 + lib/xmlrpc.php | 2 +- localplay.php | 2 +- login.php | 3 + logout.php | 36 +- modules/captcha/COLLEGE.ttf | Bin 0 -> 5896 bytes modules/captcha/captcha.php | 303 ++++++++++++ modules/id3/getid3/getid3.php | 140 +++--- modules/id3/getid3/module.audio-video.asf.php | 9 +- modules/id3/getid3/module.audio-video.flv.php | 210 ++++++++ modules/id3/getid3/module.audio-video.riff.php | 4 +- modules/id3/getid3/module.audio.mp3.php | 33 +- modules/id3/getid3/module.tag.id3v2.php | 88 ++-- modules/id3/getid3/write.id3v2.php | 1 + modules/id3/getid3/write.php | 8 + modules/id3/getid3/write.real.php | 251 ++++++++-- modules/init.php | 4 +- modules/validatemail/validateEmail.php | 638 +++++++++++++++++++++++++ modules/validatemail/validateEmailFormat.php | 219 +++++++++ mpd.php | 2 +- playlist.php | 2 +- randomplay.php | 2 +- register.php | 81 +++- templates/show_user_registration.inc.php | 30 +- templates/show_users.inc | 13 + 66 files changed, 2124 insertions(+), 270 deletions(-) create mode 100644 activate.php create mode 100644 modules/captcha/COLLEGE.ttf create mode 100644 modules/captcha/captcha.php create mode 100644 modules/id3/getid3/module.audio-video.flv.php create mode 100644 modules/validatemail/validateEmail.php create mode 100644 modules/validatemail/validateEmailFormat.php diff --git a/activate.php b/activate.php new file mode 100644 index 00000000..4aeb00da --- /dev/null +++ b/activate.php @@ -0,0 +1,49 @@ +"; +show_template('style'); +echo ""; + + +$username = $_GET['u']; +$validation = $_GET['act_key']; +$user = new User($username); +$val1 = $GLOBALS['user']->get_user_validation($username,$validation); +if (!$val1){ + $GLOBALS['error']->add_error('no_such_user',_("No user with this name registered")); + $GLOBALS['error']->print_error('no_such_user'); + echo ""; + break; + } +$activate = $GLOBALS['user']->activate_user($username); +show_confirmation('User activated','This User ID is activated and can be used','/login.php'); +echo ""; + +?> diff --git a/albumart.php b/albumart.php index fd7c7f41..7d29decb 100644 --- a/albumart.php +++ b/albumart.php @@ -1,7 +1,7 @@ offset_limit = $info->offset_limit; $this->email = $info->email; $this->last_seen = $info->last_seen; + $this->reg_date = $info->reg_date; + $this->validation = $info->validation; $this->set_preferences(); // Make sure the Full name is always filled @@ -476,10 +480,9 @@ class User { $username = sql_escape($username); $fullname = sql_escape($fullname); $email = sql_escape($email); - /* Now Insert this new user */ - $sql = "INSERT INTO user (username, fullname, email, password, access) VALUES" . - " ('$username','$fullname','$email',PASSWORD('$password'),'$access')"; + $sql = "INSERT INTO user (username, fullname, email, password, access, reg_date) VALUES" . + " ('$username','$fullname','$email',PASSWORD('$password'),'$access', unix_timestamp())"; $db_results = mysql_query($sql, dbh()); if (!$db_results) { return false; } @@ -737,9 +740,31 @@ class User { @description calcs difference between now and last_seen if less than delay, we consider them still online */ + function is_online( $delay = 1200 ) { return time() - $this->last_seen <= $delay; } + /*! + @function get_user_validation + @check if user exists before activation can be done. + */ + function get_user_validation($username,$validation){ + $sql = "SELECT validation FROM user where username='$username'"; + $db_results = mysql_query($sql, dbh()); + $row = mysql_fetch_array($db_results); + $val = $row[validation]; + return $val; + } // get_user_validation + + /*! + @function activate_user + @activates the user from public_registration + */ + function activate_user($username) { + $sql = "UPDATE user SET disabled='0' WHERE username='$username'"; + $db_results = mysql_query($sql, dbh()); + } // activate_user } //end class + ?> diff --git a/lib/class/view.class.php b/lib/class/view.class.php index 112148ad..afc81b22 100644 --- a/lib/class/view.class.php +++ b/lib/class/view.class.php @@ -1,7 +1,7 @@ $i; $i++) { + $str .= $seeds{mt_rand(0, $seeds_count - 1)}; + } + + return $str; +} //str_rand + +/** + * send_confirmation + * + * + */ +function send_confirmation($username, $fullname, $email, $password, $validation) { + +$title = conf('site_title'); +$from = "From: Ampache <".conf('mail_from').">"; +$body = "Welcome to $title + +Please keep this email for your records. Your account information is as follows: + +---------------------------- +Username: $username +Password: $password +---------------------------- + +Your account is currently inactive. You cannot use it until you visit the following link: +" +. conf('web_path'). "/activate.php?mode=activate&u=$username&act_key=$validation + +Please do not forget your password as it has been encrypted in our database and we cannot retrieve it for you. However, should you forget your password you can request a new one which will be activated in the same way as this account. + +Thank you for registering."; + + +mail($email, "Welcome to $title" , $body, $from); + +} //send_confirmation + + ?> diff --git a/lib/upload.php b/lib/upload.php index 97c60118..a24c55c5 100644 --- a/lib/upload.php +++ b/lib/upload.php @@ -1,5 +1,8 @@ - - - - -Sample logout page - - -Congrats, you are logged out. -
login - diff --git a/modules/captcha/COLLEGE.ttf b/modules/captcha/COLLEGE.ttf new file mode 100644 index 00000000..400fe5ce Binary files /dev/null and b/modules/captcha/COLLEGE.ttf 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 @@ + 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 fields html string (no complete form), with captcha + image already embedded as data:-URI + */ + function form($title="→ retype that here", $more="
Enter the correct letters and numbers from the image into the text box.
This small test serves as access restriction against malicious bots.
Simply reload the page if this graphic is too hard to read.
") { + $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 = + '' + . '' + . '' + . '
'.$alt. ''.$title. '
' + . '*' + . '
'.$more.'
'; + +// '' +// ''.$alt. '' +// . '' +// . '' +// . '
'.$alt. ''.$title. '
' +// ''.$title. '
' +// . '' +// . '
'.$more.'
'; + + #-- 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; + } + $html = "
$html
"; + return($html); + } +/*/* + + + /* 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= '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 ([^ ]{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 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +// // +// FLV module by Seth Kaufman // +// * version 0.1 (26 June 2005) // +// minor modifications by James Heinrich // +// * 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):
  • '.trim(implode('
  • ', $real_writer->errors)).'
'; + } + 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 @@ + + +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 +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 = "<"; + $rightCarrot = ">"; + // set type of "new line" ... flush output for web browsing + echo "
";
+            $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 "
"; + } + + // 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 @@ + 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 = ""; + $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 +?> diff --git a/mpd.php b/mpd.php index 35828bcc..ca495fe8 100644 --- a/mpd.php +++ b/mpd.php @@ -1,7 +1,7 @@ add_error('captcha',_("Error Captcha Required")); + } + if (isset ($captcha)){ + if ($captcha) { + $msg="SUCCESS"; + } + else { + $GLOBALS['error']->add_error('captcha',_("Error Captcha Failed")); + } + } + if(conf('user_agreement')==true){ if(!$accept_agreement){ - echo("
You must accept the user agreement
"); - echo("Click here to go back"); - break; + $GLOBALS['error']->add_error('user_agreement',_("You must accept the user agreement")); } } if(!$username){ - echo("
You did not enter a username
"); - echo("Click here to go back"); - break; + $GLOBALS['error']->add_error('username',_("You did not enter a username")); } if(!$fullname){ - echo("
Please enter your full name
"); - echo("Click here to go back"); - break; + $GLOBALS['error']->add_error('fullname',_("Please fill in your full name (Firstname Lastname)")); } - if(!good_email($email)){ - echo("
You must enter a valid email address
"); - echo("Click here to go back"); - break; - } +//Check the mail for correct address formation. + + $attempt = 0; + $max_attempts = 3; + $response_code = ""; + + while ( $response_code == "" || strstr( $response_code, "fsockopen error" )) { + $validate_results = validateEmail( $email ); + $response_code = $validate_results[1]; + if($attempt == $max_attempts) break; + $attempt++; + } + + if ( $validate_results[0] ) { + $mmsg = "MAILOK"; + } + else { + $GLOBALS['error']->add_error('email',_("Error Email address not confirmed
$validate_results[1]")); + } +// End of mailcheck if(!$pass1){ - echo("
You must enter a password
"); - echo("Click here to go back"); - break; + $GLOBALS['error']->add_error('password',_("You must enter a password")); } if ( $pass1 != $pass2 ) { - echo("
Your passwords do not match
"); - echo("Click here to go back"); + $GLOBALS['error']->add_error('password',_("Your passwords do not match")); + } + + if($GLOBALS['error']->error_state){ + show_user_registration($values); break; } + $new_user = new_user("$username", "$fullname", "$email", "$pass1"); if(!$new_user){ - echo("
That username already exists
"); - echo("Click here to go back"); + $GLOBALS['error']->add_error('duplicate_user',_("That username already exists")); + } + if($GLOBALS['error']->error_state){ + show_user_registration($values); break; } - break; +break; // This is the default action. case 'show_add_user': default: diff --git a/templates/show_user_registration.inc.php b/templates/show_user_registration.inc.php index b8ab67c6..4b637e58 100644 --- a/templates/show_user_registration.inc.php +++ b/templates/show_user_registration.inc.php @@ -46,16 +46,18 @@ $htmllang = str_replace("_","-",conf('lang'));
- +
Ampache New User Registration @@ -84,6 +86,7 @@ echo "$password";
I Accept + print_error('user_agreement'); ?>
@@ -104,13 +107,15 @@ echo "$password";
- +
@@ -118,7 +123,8 @@ echo "$password"; : @@ -126,7 +132,8 @@ echo "$password"; : @@ -135,6 +142,7 @@ echo "$password"; @@ -146,12 +154,16 @@ echo "$password"; - + + - + + @@ -74,6 +80,9 @@ while ( $results = mysql_fetch_object($db_result) ) { $last_seen = date("m\/d\/Y - H:i",$user->last_seen); if (!$user->last_seen) { $last_seen = "Never"; } + $reg_date = date("m\/d\/Y - H:i",$user->reg_date); + if (!$user->reg_date) { $reg_date = "Unknown"; } + ?> + +
: - * + * + print_error('username'); ?> + print_error('duplicate_user'); ?>
- * + * + print_error('fullname'); ?>
- * + * + print_error('email'); ?>
* + print_error('password'); ?>
+ + print_error('captcha'); ?> +
*Required fields
+ diff --git a/templates/show_users.inc b/templates/show_users.inc index dc43fb20..f91fbcc8 100644 --- a/templates/show_users.inc +++ b/templates/show_users.inc @@ -48,6 +48,12 @@ $admin_menu = "admin/"; + + + +
@@ -84,6 +93,10 @@ while ( $results = mysql_fetch_object($db_result) ) { + + -- cgit