Animated Talking Captcha php class : Ming functions for Flash : Functions PHP Source Code


PHP Source Code » Functions » Ming functions for Flash »

 

Animated Talking Captcha php class


Captcha talk - SWF audio captcha with Ming

<?php
/********************************************************************************
* @ CAPTCHA-TALK
* @ A Ming based Animated Talking Captcha PHP5 Class
* @ with optional DNSBL ( IP blacklist) check
* @ swfcaptcha class - scripts.titude.nl -(c) 2007
* @ A Ming based Animated and Talking SFW based Captcha class
* @ Requires: PHP5 with Ming  ( PHP 4 class also in the download )
* @ License - Disclaimer at the bottom
* @ Version Build: 15-04-2007
* @
* @ 16-04 Update for valid x-html output for the swf ( No embed tag anymore )
********************************************************************************/
// error_reporting(E_ALL);
class swfcaptcha {
/********************************************************************************
*  swfcaptcha class user options
********************************************************************************/
       // Default colors for characters
   
public $colors = array('EE0000','00EE00','0000EE','00EEEE','EEEE00','EE00EE');

       
// Default background color
   
public $bgcolors = array('000000');

       
// Swf movie width and height
   
public $swf_width 210;
   public 
$swf_height 75;

       
// Offset for char movie in main swf movie
   
public $swf_x 10;
   public 
$swf_y 50;

       
// Scale char movie in main movie
   
public $x_scale 5;
   public 
$y_scale 5;

       
// Space between chars in char movie
   
public $font_width 30;

       
// Thickness of font lines in char movie
   
public $font_line 65;

       
// Captcha char lenght
   
public $captcha_length 6;

       
// Time settings for animation: draw - still display and rotate
   
public $drawtime 70;
   public 
$stilltime 3000;
   public 
$rotatetime 150;

       
// different animation type = 0, 1, 2, 3
   
public $animation 3;
       
// animation scale to ( from zero to scale )
   
public $animateoffset 0;
       
// y float, 0 = off ( value is random y axis movement )
   
public $animatefloat 5;

       
// Default fonts to use - empty is read all .fdb fonts from font dir
       // Note: only .fdb fonts
   
public $font = array();
   public 
$fontdir "font";
       
// Free bitstream vera fdb fonts - http://ming.sourceforge.net/

       // Default Ming captcha url for output in html code
   
public $swf_url "swfmovie.php";
       
// Html code with swf transparency
   
public $swf_transparent false;
   public 
$swf_validxhtml true;

       
// form input catpcha value name
   
public $post_captcha "captcha";
       
// captcha session name
   
private $session_captcha "captcha";
   
       
// Default background image ( optional )
       // jpg - png or gif ( NB this Ming function is buggy with jpg's )
       // $bgimages empty and $bgimgauto true is autoscan dir for images
   
public $bgimages = array();
   public 
$bgimgauto false;
       
// Default dir to background images
   
public $imagesdir "images";

       
// Enable talking catcha
       // Captcha code will always be set in lowercase on true
   
private $talking true;
       
// Default dir to sounds
   
public $sounddir "sounds";
       
// Language folder user defined (  country code lowercase )
       // All sound files have a prefix: msc_ - except the starting sound codestart.mp3
   
public $soundlang "";

       
// Movie frame rate
   
public $framerate 12;
       
// Frames for dound duration - depends on framerate
       // Experiment with values for correct sound duration
   
public $soundframes 20;
       
// Sample sounds 22 kc mono - 24kb/s mp3
   
       // for more dnsbl lists and info see http://www.moensted.dk/spam/
       // http://www.sdsc.edu/~jeff/spam/cbc.html - http://www.declude.com/Articles.asp?ID=97
   
public $dnsblists = array('zen.spamhaus.org','bl.spamcop.net','list.dsbl.org','tor.ahbl.org','opm.tornevall.org');
       
// use DNSBL check if dnsblist is not empty and usednsbl = true
   
private $usednsbl false;
   private 
$session_dnsbl "dnsbl";
       
// On DNSBL listed - generate an error background ( NOTE: from sound dir )
   
public $forbidden_img "forbidden.gif";
       
// On DNSBL listed - generate an error sound ( from sound dir )
   
public $forbidden_sound "forbidden.mp3";

       
// Internal vars
   
private $bgcolor "";
   private 
$captchaval "";
   private 
$soundlangdefault "us";
   
/********************************************************************************
* @ public function swfcaptcha()
* @ init class
********************************************************************************/
   
public function swfcaptcha() {
       if (!
session_id()) {
           @
session_start();
       }    
   }
   
/********************************************************************************
* @ public function swfmovie()
* @ output the swf captcha movie
********************************************************************************/
   
public function swfmovie() {
       
// A little delay - can prevents quick reload hickups
       // sleep(1);
       // Create movie and properties
       
ming_useswfversion(6);
       
$movie = new SWFMovie();
       
$movie->setRate($this->framerate);
       
$movie->setDimension($this->swf_width$this->swf_height);
       
shuffle($this->bgcolors);
       
$bgcol $this->deccolors($this->bgcolors[0]);
       
$this->bgcolor "#" $bgcol[0] . $bgcol[1] . $bgcol[2];
       
$movie->setBackground($bgcol[0], $bgcol[1], $bgcol[2]);    

       
// Captcha characters
       
$captchars "abcdefghijklmnopqrstuvwxyz";
       
$captchars .= "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
       
$captchars .= "0123456789";

       
// $asscript var contains main as script animation code
       
$asscript "fontshapes = new Array(); captchalen = " $this->captcha_length "; ";
       
$asscript .= "animation = " $this->animation "; anioffset = " $this->animateoffset ";";
       
// Set ( random ) font and Get font outlines
       
if (substr($this->fontdir, -) == '/') {
           
$this->fontdir substr($this->fontdir0, -1);
       }
       
$fontset = array();
       
// Empty font -> read dir auto for .fdb fonts
       
if (empty($this->font)) {
           
$this->font glob($this->fontdir "/*.fdb");
           
// Create multiple font objects
           
for ($i 0$i count($this->font); $i++)  {
               
$cfont $this->font[$i];
               
$fontset[$i] = new SWFFont($cfont);
           }
       } else {
           
// Create multiple font objects
           
for ($i 0$i count($this->font); $i++)  {
               
$cfont $this->fontdir "/" $this->font[$i];
               
$fontset[$i] = new SWFFont($cfont);
           }
       }
       
$fontshapes = array();
       
// Get outline per character
       
for ($i 0$i $this->captcha_length$i++) {
           
shuffle($fontset);
           
$this->captchaval .= $captchars[rand(1strlen($captchars))];
           
$fontshape $fontset[0]->getShape(ord($this->captchaval[$i]));
           
$fontshape str_replace("\n"":"$fontshape);
           
$fontshape str_replace("to ""to:"$fontshape);
           
$fontshape str_replace(" "","$fontshape);
           
$fontshapes[$i] = $fontshape;
           
$asscript .= " fontshapes[" $i "] = \"" $fontshapes[$i] . "\"; ";
       }
       
// Talking catcha code always in lowercase
       
if ($this->talking === true) {
           
$this->captchaval strtolower($this->captchaval);
       }
       
$_SESSION[$this->session_captcha] = $this->captchaval;
       if (
substr($this->imagesdir, -) == '/') {
           
$this->imagesdir substr($this->imagesdir0, -1);
       }
       
// Reset all Captcha on DNSBL forbidden
       
if ($this->usednsbl === true && isset($_SESSION[$this->session_dnsbl])) {
           if (
$_SESSION[$this->session_dnsbl] != "oke") {
               
$this->bgimages = array($this->forbidden_img);
               if (
substr($this->sounddir, -) == '/') {
                   
$this->imagesdir substr($this->sounddir0, -1);
               } else {
                   
$this->imagesdir $this->sounddir;
               }
               
$asscript "fontshapes = new Array(); captchalen =0; ";
               
$asscript .= "animation = 0; anioffset = " $this->animateoffset ";";
               if (isset(
$_SESSION[$this->session_captcha])) {
                   unset(
$_SESSION[$this->session_captcha]);
               }
           }
       }
       if (
$this->bgimgauto === true && empty($this->bgimages)) {
           
// Add a bg image auto - NOTE jpg are buggy in this Ming function
           
$this->bgimages glob($this->imagesdir "/" "{*.gif,*.jpg,*.png}"GLOB_BRACE);
           if (!empty(
$this->bgimages)) {
               
shuffle($this->bgimages);
               
$img $this->bgimages[0];
               if (
file_exists($img)) {
                   
$bgimg = new SWFBitmap(fopen($img,"rb"));
                   
$movie->add($bgimg);
               }
           }
       } else {
           
// Add a bg image - optional - NOTE jpg are buggy in this Ming function
           
if (!empty($this->bgimages)) {
               
shuffle($this->bgimages);
               
$img $this->imagesdir "/" $this->bgimages[0];
               if (
file_exists($img)) {
                   
$bgimg = new SWFBitmap(fopen($img,"rb"));
                   
$movie->add($bgimg);
               }
           }
       }
       
$asscript .= "
       fontparts = new Array();
       for (i = 0; i < fontshapes.length; i++) {
           fontparts[i] = fontshapes[i].split(':');
       }
       for (i = 0; i < captchalen; i++) {
           mcname = 'mc'+ (i + 1);
           this.createEmptyMovieClip(mcname, (i + 1));
           mc = eval(mcname);
           mc._x = " 
$this->swf_x " + " $this->font_width " * i;
           mc._y = " 
$this->swf_y ";
           mc._xscale = " 
$this->x_scale ";
           mc._yscale = " 
$this->y_scale ";
       }
       tmpp = 0;
       curn = 0;
       curr = 0;
       intval = new Object();
       intvalreset = new Object();
       intvalrotate = new Object();
       randomcolors = new Array(" 
$this->randomcolors() . ");
       randnum = Math.floor(Math.random()*randomcolors.length);
       fontdraw = function() {
           var fntprts = fontparts[curn];
           if (tmpp >= fntprts.length){
               curn = (++curn%fontshapes.length);
               curr++;
               tmpp = 0;    
               randnum = Math.floor(Math.random()*randomcolors.length);    
           } else {
               mcname = 'mc'+ (curn + 1);
               mc = eval(mcname);
               mc.lineStyle(" 
$this->font_line ", randomcolors[randnum], 100);
               var cord = fntprts[tmpp + 1].split(',');
               if (fntprts[tmpp] == 'curveto') {
                   mc[fntprts[tmpp]](cord[0], cord[1], cord[2], cord[3]);
               } else {
                   mc[fntprts[tmpp]](cord[0], cord[1]);
               }
               tmpp += 2;
           }
           if (curr >= captchalen) {
               clearInterval(intval);
               clearInterval(intvalrotate);
               intvalrotate = setInterval(fontrotate, " 
$this->rotatetime ", 'r');
               for (m = 0; m < captchalen; m++) {
                   mcname = 'mc'+ (m + 1);
                   mc = eval(mcname);
               }
               intvalreset = setInterval(fontdrawreset, " 
$this->stilltime ");
           }
       };
       fontdrawreset = function() {
       // Reset captcha animation
           for (m = 0; m < captchalen; m++) {
               mcname = 'mc'+ (m + 1);
               mc = eval(mcname);
               mc.clear();
               mc._xscale = " 
$this->x_scale ";
               mc._yscale = " 
$this->y_scale ";
               mc._x = " 
$this->swf_x " + " $this->font_width " * m;
               mc._y = " 
$this->swf_y ";
               tmpp = 0;
               curn = 0;
               curr = 0;
           }
           clearInterval(intvalreset);
           intval = setInterval(fontdraw, " 
$this->drawtime ");
           clearInterval(intvalrotate);
           intvalrotate = setInterval(fontrotate, " 
$this->rotatetime ", 'l');
       };
       fontrotate = function(direction) {
           // Defines type of animation
           for (m = 0; m < curr; m++) {
               mcname = 'mc'+ (m + 1);
               mcr = eval(mcname);
               if (animation == 1 || animation == 3) {
                   if (mcr._xscale > anioffset && direction == 'l') {
                       mcr._xscale--;
                   } else if (mcr._xscale < " 
$this->x_scale " && direction == 'r') {
                       mcr._xscale++;
                   }
               }
               if (animation == 2 || animation == 3) {
                   if (mcr._yscale > anioffset && direction == 'l') {
                       mcr._yscale--;
                   } else if (mcr._yscale < " 
$this->y_scale " && direction == 'r') {
                       mcr._yscale++;
                   }
               }
               if (" 
$this->animatefloat " != 0) {
                   yfloat = Math.floor(Math.random()*" 
$this->animatefloat ");
                   mcr._y = yfloat + " 
$this->swf_y ";
               }
           }
       };
       intval = setInterval(fontdraw, " 
$this->drawtime ");
       intvalrotate = setInterval(fontrotate, " 
$this->rotatetime ", 'l');
       _parent.soundtrigger = false;
       _parent.stop();
       "
;
       
// As main code to sprite movie
       
$capmc = new SWFSprite();
       
$capmc->add(new SWFAction($asscript));
       
$capmc->nextFrame();
       
$movie->add($capmc);
       if (
$this->talking === true) {
           
$sounds = array();
           
$sounds $this->checksound();
           
// Only sound and button when all files are valid (  with language or default )
           
if (!empty($sounds)) {
               
// Create a full movie shape
               
$shp = new SWFShape();
               
$shp->setRightFill($shp->addFill(000));
               
$shp->drawLine($this->swf_width0);
               
$shp->drawLine(0$this->swf_height);
               
$shp->drawLine((-$this->swf_width), 0);
               
$shp->drawLine(0, (-$this->swf_height));
               
// Add shape to a button and define actions
               
$but = new SWFButton();
               
$but->addShape($shpSWFBUTTON_HIT);
               
$asscript "if (soundtrigger == true) { soundtrigger = false; stop(); }
               else { soundtrigger = true; gotoAndPlay(1); }
               "
;
               
$but->addAction(new SWFAction($asscript), SWFBUTTON_HIT);
               
$movie->add($but);    
               
// Stream multiple mp3 (  experimental - may be buggy )
               
for ($i 0$i count($sounds); $i++) {
                   
$mp3f fopen($sounds[$i],"rb");
                   
$movie->streamMp3($mp3f);
                   for (
$a 0$a $this->soundframes$a++) {
                       
$movie->nextFrame();
                   }
                   
// Do not close filehandle here - kills stream for multiple files
               
}
           }
       }
       
header('Content-type: application/x-shockwave-flash');
           
// $movie->save('captcha.swf');  
           
$movie->output();
       
// Close filehandle - NOTE: rem this if sound stream gets aborted
       
if (isset($mp3f)) {
           
fclose($mp3f);
       }
   }
   
/********************************************************************************
* @ public function swfhtml()
* @ returns x-html code for the swf captcha
********************************************************************************/    
   
public function swfhtml() {
       
// Prevent cache display by adding unique date query
       // No cache headers for the swf output causes a problem in IE7 browsers
       
$url $this->swf_url "?nc=" date('YmHis');
       
// Two types off html output
       // First = swf valid xhtml ( prefered ) - Second = standard ( normaly used ) xhtml
       
if ($this->swf_validxhtml === true) {
           
$htmlout '<object type="application/x-shockwave-flash" ';
           
$htmlout .= 'width="' $this->swf_width '" height="' $this->swf_height '" id="talkingcaptcha" ';
           
$htmlout .= 'data="' $url '" /><param name="allowScriptAccess" value="sameDomain" />';
           
$htmlout .= '<param name="movie" value="' $url '" />';
           
$htmlout .= '<param name="quality" value="high" />';
           if (
$this->swf_transparent !== true) {
               
$htmlout .= '<param name="bgcolor" value="' $this->bgcolor '" />';
           } else {
               
$htmlout .= '<param name="wmode" value="transparent" />';
           }
           
$htmlout .= '</object>';
       } else {
           
$htmlout '<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" ';
           
$htmlout .= 'width="' $this->swf_width '" height="' $this->swf_height '" id="talkingcaptcha">';
           
$htmlout .= '<param name="allowScriptAccess" value="sameDomain" />';
           
$htmlout .= '<param name="movie" value="' $url '" />';
           
$htmlout .= '<param name="quality" value="high" />';
           if (
$this->swf_transparent !== true) {
               
$htmlout .= '<param name="bgcolor" value="' $this->bgcolor '" />';
               
$embedcode 'bgcolor="' $this->bgcolor '" ';
           } else {
               
$htmlout .= '<param name="wmode" value="transparent" />';
               
$embedcode 'wmode="transparent" ';
           }
           
$htmlout .= '<embed src="' $url '" quality="high" ' $embedcode;
           
$htmlout .= 'width="' $this->swf_width '" height="' $this->swf_height '" name="talkingcaptcha" ';
           
$htmlout .= 'allowScriptAccess="sameDomain" type="application/x-shockwave-flash"';
           
$htmlout .= ' /></object>';
       }
       return 
$htmlout;
   }

/********************************************************************************
* @ public function validate()
* @ returns true on post captcha value == session value
********************************************************************************/    
   
public function validate() {
       if (
$_SERVER['REQUEST_METHOD'] == 'POST') {
           if (
$this->usednsbl === true && !isset($_SESSION[$this->session_dnsbl])) {
               if (
$this->dnsbl() === false) {
                   return 
false;
               }
           } else if (
$this->usednsbl === true && $_SESSION[$this->session_dnsbl] != "oke") {
                   return 
false;
           }
           if (isset(
$_POST[$this->post_captcha]) && isset($_SESSION[$this->session_captcha])) {
               
$val $_POST[$this->post_captcha];
               if (
$this->talking === true) {
                   
$val strtolower($val);
               }
               if (
$val != "" && $val == $_SESSION[$this->session_captcha]) {
                   unset(
$_SESSION[$this->session_captcha]);
                   return 
true;
               }
           }
       }
       if (isset(
$_SESSION[$this->session_captcha])) {
           unset(
$_SESSION[$this->session_captcha]);
       }
       return 
false;
   }
   
/********************************************************************************
* @ private function checksounds()
* @ check user defined lang first ( fill in missing from default dir )
* @ returns sound files array or empty on a missing file (  disables sounds auto on missing )
********************************************************************************/
   
private function checksound() {
       
$sounds = array();
       if (
substr($this->sounddir, -) == '/') {
           
$this->sounddir substr($this->sounddir0, -1);
       }
       
$defdir $this->sounddir "/" strtolower($this->soundlangdefault);
       
$sdir $this->sounddir "/" strtolower($this->soundlang);
       if (
strlen($this->soundlang) != 2) {
           
$sdir $defdir;
       }
       if (
$this->usednsbl === true && isset($_SESSION[$this->session_dnsbl])) {
           if (
$_SESSION[$this->session_dnsbl] != "oke") {
               
// forbidden.mp3 - used for blocked IP
               
if (file_exists($sdir "/" $this->forbidden_sound)) {
                   
$sounds[0] = $sdir "/" $this->forbidden_sound;
               } else if (
file_exists($defdir "/" $this->forbidden_sound)) {
                   
$sounds[0] = $defdir "/" $this->forbidden_sound;    
               } else {
                   return array();
               }
               return 
$sounds;
           }
       }
       
// codestart.mp3 - used for captcha code start
       
if (file_exists($sdir "/codestart.mp3")) {
           
$sounds[0] = $sdir "/codestart.mp3";
       } else if (
file_exists($defdir "/codestart.mp3")) {
           
$sounds[0] = $defdir "/codestart.mp3";    
       } else {
           return array();
       }
       for (
$i 1$i <= $this->captcha_length$i++) {
           
// all other sound files have format  msc_captcha-character.mp3
           
if (file_exists($sdir "/msc_" strtolower($this->captchaval[$i-1]) . ".mp3")) {
               
$sounds[$i] = $sdir "/msc_" strtolower($this->captchaval[$i-1]) . ".mp3";
           } else if (
file_exists($defdir "/msc_" strtolower($this->captchaval[$i-1]) . ".mp3")) {
               
$sounds[$i] = $defdir "/msc_" strtolower($this->captchaval[$i-1]) . ".mp3";    
           } else {
               return array();
           }    
       }
       return 
$sounds;
   }

/********************************************************************************
* @ private function deccolors()
* @ input string hex color
* @ returns array with RGB color
********************************************************************************/
   
private function deccolors($webcolors) {
       
$webcolors str_replace("#"""$webcolors);
       
$webcolors str_replace("0x"""$webcolors);
       
$deccolor = array();
       
$deccolor[0] = hexdec(substr($webcolors02));
       
$deccolor[1] = hexdec(substr($webcolors22));
       
$deccolor[2] = hexdec(substr($webcolors42));
       return 
$deccolor;
   }
   
/********************************************************************************
* @ private function randomcolors()
* @ returns string for random font colors for ming captcha swf
********************************************************************************/
   
private function randomcolors() {
       
$colorstr "";
       for (
$i 0$i count($this->colors); $i++) {
           
$var str_replace("#"""$this->colors[$i]);
           
$var str_replace("0x"""$var);
           if (
$i count($this->colors) - 1) {
               
$colorstr .= "0x" strtolower($var) . ",";
           } else {
               
$colorstr .= "0x" strtolower($var) . "";            
           }
       }
       if (
count($this->colors) == 1) {
           
// adjust string if 1 color for flash ani ( needs 2 )
           
$var str_replace("#"""$this->colors[0]);
           
$var str_replace("0x"""$var);
           
$colorstr "0x" strtolower($var) . ", 0x" strtolower($var) . "";
       }
       return 
$colorstr;
   }
   
/********************************************************************************
* @ private function dnsbl
* @ validate IP on DNSBL ( blacklists )
* @ returns false if not found
* @ sets $_SESSION[$this->sessiondnsbl] - returns true on not listed
********************************************************************************/
   
private function dnsbl() {
       if (
preg_match('/^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/'$_SERVER["REMOTE_ADDR"])) {
           
$reversip implode("."array_reverse(explode("."$_SERVER["REMOTE_ADDR"])));
           
$win_os strtolower(substr(PHP_OS03)) == "win" true false;
           foreach (
$this->dnsblists as $list){
               if (
function_exists("checkdnsrr")) {
                   if (
checkdnsrr($reversip "." $list ".""A")) {
                       
$_SESSION[$this->session_dnsbl] = $list;
                       return 
false;
                   }
               } else if (
$win_os) {
                   
$lookup = array();
                   @
exec("nslookup -type=A " $reversip "." $list "."$lookup);
                   foreach (
$lookup as $line) {
                       if (
strstr($line$list)) {
                           
$_SESSION[$this->session_dnsbl] = $list;
                           return 
false;
                       }
                   }
               }
           }
       }
       
$_SESSION[$this->session_dnsbl] = "oke";
       return 
true;
   }
}
/********************************************************************************
Copyright (c) 2007, scripts.titude.nl - all rights reserved.
Author: scripts(_AT_NO_SPAM_)titude(_dot_)nl - scripts.titude.nl - Netherlands

- The English (US)  alphabet and number sound files originate from:
 Quadravox Inc - http://www.quadravox.com/
- The dutch ( belgian ) alphabet and number sound files originate from:
 Dutch Grammar  - http://www.dutchgrammar.com/
- The English (EN) , France and German alphabet and number sound files originate from:
 LanguageGuide.org  - http://www.languageguide.org/
( Thanks a lot for letting me use these )

Disclaimer & License

NOTE THAT THE RIGHTS OF THE USED AUDIO FILES LAY WITH THE RESPECTED
OWNERS AND THAT THE USE IS GRANTED TO USE THEM WITH THIS SCRIPT

Current state: development - educational - experimental

   THE AUTHOR MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
   IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, THE AUTHOR MAKES NO
   REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR
   ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SCRIPT, COMPONENTS,
   OR DOCUMENTATION WILL NOT INFRINGE ANY PATENTS, COPYRIGHTS,
   TRADEMARKS, OR OTHER RIGHTS.  THE AUTHOR SHALL NOT BE HELD LIABLE
   FOR ANY LIABILITY NOR FOR ANY DIRECT, INDIRECT, OR CONSEQUENTIAL
   DAMAGES WITH RESPECT TO ANY CLAIM BY RECIPIENT OR ANY THIRD PARTY
   ON ACCOUNT OF OR ARISING FROM THIS AGREEMENT OR USE OF THIS SCRIPT
   AND ITS COMPONENTS.

   Released under GNU Lesser General Public License - http://www.gnu.org/licenses/lgpl.html

********************************************************************************/
?> 


HTML code for linking to this page:

Follow Navioo On Twitter

PHP Source Code

 Navioo Functions
» Ming functions for Flash