├── .gitignore ├── favicon.ico ├── images ├── 1x1.gif ├── clear.gif ├── lbulb.gif ├── start.gif ├── stop.gif ├── lloogg.png ├── lloogg16.gif ├── led-red-on.gif └── led-red-off.gif ├── geoipdata └── GeoIP.dat ├── javascript ├── usermain.js ├── .usermain_unstable.js.swp ├── log.js.php ├── switchuser.js ├── feedbacks.js ├── login.js ├── form.js ├── register.js └── mfx.js ├── eventcodes.php ├── dbapi.php ├── footer.php ├── welcome.html.php ├── index.php ├── success.html.php ├── rmallowed.php ├── g.php ├── lib.php ├── ajax ├── checkusername.php ├── allow.php └── login.php ├── localconfig.php.example ├── mkpro.php ├── admin.html.php ├── mail ├── welcome.txt └── validate.txt ├── bcrypt.php ├── url.php ├── logout.php ├── sendfeedback.html.php ├── .htaccess ├── redisinfo.php ├── config.php ├── adm.php ├── synopsys.php ├── login.html.php ├── feedbacks.html.php ├── header.php ├── usernav.php ├── register.php ├── l.js ├── l_local.js ├── register.html.php ├── pro.php ├── secureid.php ├── search.php ├── recv.php ├── user.php ├── usercode.php ├── poll.php ├── usermain.php ├── utils.php ├── trends.php ├── README.md ├── trendsframe.php ├── css ├── iphone_style.css └── style.css ├── display.php ├── redis.php ├── geoip.inc └── json.php /.gitignore: -------------------------------------------------------------------------------- 1 | *.swf 2 | localconfig.php 3 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antirez/lloogg/HEAD/favicon.ico -------------------------------------------------------------------------------- /images/1x1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antirez/lloogg/HEAD/images/1x1.gif -------------------------------------------------------------------------------- /images/clear.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antirez/lloogg/HEAD/images/clear.gif -------------------------------------------------------------------------------- /images/lbulb.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antirez/lloogg/HEAD/images/lbulb.gif -------------------------------------------------------------------------------- /images/start.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antirez/lloogg/HEAD/images/start.gif -------------------------------------------------------------------------------- /images/stop.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antirez/lloogg/HEAD/images/stop.gif -------------------------------------------------------------------------------- /geoipdata/GeoIP.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antirez/lloogg/HEAD/geoipdata/GeoIP.dat -------------------------------------------------------------------------------- /images/lloogg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antirez/lloogg/HEAD/images/lloogg.png -------------------------------------------------------------------------------- /images/lloogg16.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antirez/lloogg/HEAD/images/lloogg16.gif -------------------------------------------------------------------------------- /images/led-red-on.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antirez/lloogg/HEAD/images/led-red-on.gif -------------------------------------------------------------------------------- /images/led-red-off.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antirez/lloogg/HEAD/images/led-red-off.gif -------------------------------------------------------------------------------- /javascript/usermain.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antirez/lloogg/HEAD/javascript/usermain.js -------------------------------------------------------------------------------- /eventcodes.php: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /javascript/.usermain_unstable.js.swp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antirez/lloogg/HEAD/javascript/.usermain_unstable.js.swp -------------------------------------------------------------------------------- /javascript/log.js.php: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /dbapi.php: -------------------------------------------------------------------------------- 1 | connect(); 8 | return $r; 9 | } 10 | ?> 11 | -------------------------------------------------------------------------------- /footer.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /welcome.html.php: -------------------------------------------------------------------------------- 1 | 6 | Your account is now active, it's time to Log in. 7 | 10 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /javascript/switchuser.js: -------------------------------------------------------------------------------- 1 | function switchUser() { 2 | var su = $('seluser').options[$('seluser').selectedIndex].value; 3 | var now = new Date; 4 | var t = now.getTime(); 5 | now.setTime(t+(3600*24*1000*1000)); 6 | mfxSetCookie("requser",su,now,"/"); 7 | document.location.reload(); 8 | } 9 | -------------------------------------------------------------------------------- /success.html.php: -------------------------------------------------------------------------------- 1 | 6 |

7 | Your account has been created. 8 |

9 | Go to the /settings">setting page and get the code for your pages. 10 | 13 | -------------------------------------------------------------------------------- /rmallowed.php: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /g.php: -------------------------------------------------------------------------------- 1 | 16 | -------------------------------------------------------------------------------- /lib.php: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /ajax/checkusername.php: -------------------------------------------------------------------------------- 1 | get("username:$username:id")) { 14 | echo("ERROR"); 15 | } else { 16 | echo("OK"); 17 | } 18 | 19 | ?> 20 | -------------------------------------------------------------------------------- /localconfig.php.example: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /mkpro.php: -------------------------------------------------------------------------------- 1 | 16 | -------------------------------------------------------------------------------- /admin.html.php: -------------------------------------------------------------------------------- 1 | 7 |

PRO accounts

8 |
9 | Set PRO user: 10 | 11 |
12 |

Last registered users

13 | 14 |

Pro users

15 | 16 | 19 | -------------------------------------------------------------------------------- /javascript/feedbacks.js: -------------------------------------------------------------------------------- 1 | function checkFeedbacksForm() { 2 | clearFields(new Array("email","body"), null); 3 | if (!isValidEmail(document.f.email.value)) { 4 | alert("Invalid email"); 5 | warnField("email"); 6 | return false; 7 | } 8 | var t = document.f.body.value; 9 | t = t.replace(/\r/g,""); 10 | t = t.replace(/\n/g,""); 11 | if (!validate(t, "^.*[^ ]+.*$", "Message can't be empty","body")) 12 | return false; 13 | document.f.submit(); 14 | } 15 | -------------------------------------------------------------------------------- /mail/welcome.txt: -------------------------------------------------------------------------------- 1 | Welcome to LLOOGG! 2 | 3 | You are now registered with the following username/password: 4 | 5 | username: %username% 6 | password: %password% 7 | 8 | Every time you want to use our service please 9 | log in at: 10 | 11 | http://lloogg.com/login.html.php 12 | 13 | Thanks for using our service. 14 | 15 | Regards, 16 | LLOOGG staff 17 | 18 | -- 19 | Note that this email is generated by a 20 | computer program, replies are not read, 21 | check our help section at lloogg.com for 22 | help. 23 | 24 | -------------------------------------------------------------------------------- /bcrypt.php: -------------------------------------------------------------------------------- 1 | = 5.5. 4 | // With 5.5 password_hash() with algo CRYPT_BLOWFISH implements bcrypt() 5 | // directly. 6 | 7 | function bcrypt_hash($pass) { 8 | $salt = substr(str_replace('+', '.', base64_encode(sha1(microtime(true), true))), 0, 22); 9 | $hash = crypt($pass, '$2a$12$' . $salt); 10 | return $hash; 11 | } 12 | 13 | function bcrypt_check($pass,$hash) { 14 | return $hash == crypt($pass, $hash); 15 | } 16 | 17 | ?> 18 | -------------------------------------------------------------------------------- /url.php: -------------------------------------------------------------------------------- 1 | 20 | -------------------------------------------------------------------------------- /logout.php: -------------------------------------------------------------------------------- 1 | get("uid:$userid:auth"); 13 | 14 | $r->set("uid:$userid:auth",$newauthsecret); 15 | $r->set("auth:$newauthsecret",$userid); 16 | $r->delete("auth:$oldauthsecret"); 17 | } 18 | header("Location: /"); 19 | exit; 20 | ?> 21 | -------------------------------------------------------------------------------- /mail/validate.txt: -------------------------------------------------------------------------------- 1 | LLOOGG email verification 2 | 3 | Dear user %username% 4 | 5 | Thanks for giving a try to LLOOGG 6 | 7 | In order to finish your registration please 8 | click in the address below to confirm your 9 | email address: 10 | 11 | http://lloogg.com/verify/%secret% 12 | 13 | Your email will be used only if you lose 14 | your password and will never be sent 15 | to others nor exposed in our web site or 16 | used to spam. 17 | 18 | Regards, 19 | LLOOGG staff 20 | 21 | -- 22 | Note that this email is generated by a 23 | computer program, replies are not read, 24 | check our help section at lloogg.com for 25 | help. 26 | 27 | -------------------------------------------------------------------------------- /ajax/allow.php: -------------------------------------------------------------------------------- 1 | 20 | -------------------------------------------------------------------------------- /sendfeedback.html.php: -------------------------------------------------------------------------------- 1 | 17 |

Message successfully sent

18 | Thanks for your message, we will try to reply soon. 19 | 22 | -------------------------------------------------------------------------------- /.htaccess: -------------------------------------------------------------------------------- 1 | AddHandler application/x-httpd-php .php .html.php 2 | DirectoryIndex index.html.php index.php 3 | RewriteEngine on 4 | 5 | RewriteCond %{REQUEST_FILENAME} !-d 6 | RewriteCond %{REQUEST_FILENAME} !-f 7 | RewriteRule ^(admin)$ admin.html.php [QSA,L] 8 | 9 | RewriteCond %{REQUEST_FILENAME} !-d 10 | RewriteCond %{REQUEST_FILENAME} !-f 11 | RewriteRule ^(feedbacks)$ feedbacks.html.php [QSA,L] 12 | 13 | RewriteCond %{REQUEST_FILENAME} !-d 14 | RewriteCond %{REQUEST_FILENAME} !-f 15 | RewriteRule ^(sendfeedback)$ sendfeedback.html.php [QSA,L] 16 | 17 | RewriteCond %{REQUEST_FILENAME} !-d 18 | RewriteCond %{REQUEST_FILENAME} !-f 19 | RewriteRule ^(settings)$ usercode.php [QSA,L] 20 | 21 | php_flag magic_quotes_gpc off 22 | php_flag register_globals off 23 | -------------------------------------------------------------------------------- /redisinfo.php: -------------------------------------------------------------------------------- 1 | 2 |

Hi! This is LLOOGG Redis server INFO output

3 | As LLOOGG is now a project I and the other guy that created it with me are running for free without any earning aim, we run the latest Redis unstable here, so that we get the latest benefits in terms of performances and memory usage, and we can use the service as a bedtest for Redis unstable releases. 4 | 5 | This INFO output can be useful for people interested to see what happens to a Redis instance stressed with a big number of requests per second. 6 |
 7 | info(true));
13 | ?>
14 | 
15 | -------------------------------------------------------------------------------- /config.php: -------------------------------------------------------------------------------- 1 | "); 23 | Config("emailcom","LLOOGG Message "); 24 | Config("emailfback","Feedback from LLOOGG user "); 25 | Config("adminuser","admin"); 26 | 27 | define("FREE_MAXTIME",60*5); 28 | define("FREE_WAIT",60*10); 29 | 30 | ?> 31 | -------------------------------------------------------------------------------- /adm.php: -------------------------------------------------------------------------------- 1 | mget($keys); 5 | if (!$user[0]) return; 6 | echo(""); 7 | echo("".utf8entities($user[0])."".utf8entities($user[1]).""."".strelapsed($user[2]).""); 8 | echo(""); 9 | } 10 | 11 | function adminLastRegistered($count=20) { 12 | $r = redisLink(); 13 | $id = $r->get("global:nextUserId"); 14 | echo(""); 15 | for ($i = 0; $i < 50; $i++) { 16 | $uid = $id-$i; 17 | uid2html($r,$uid); 18 | } 19 | echo("
"); 20 | } 21 | 22 | function adminProUsers() { 23 | $r = redisLink(); 24 | $prousers = $r->smembers("global:prousers"); 25 | echo(""); 26 | foreach($prousers as $uid) { 27 | uid2html($r,$uid); 28 | } 29 | echo("
"); 30 | } 31 | ?> 32 | -------------------------------------------------------------------------------- /synopsys.php: -------------------------------------------------------------------------------- 1 |

2 | Welcome to LLOOGG, a service that lets you check who is visiting your site in real time: which pages are being visited, with which operating system and browser, the referrer of each visit etc. 3 |

4 | Do you already use Google Analytics or a similar service? Great. But LLOOGG is different. We will show you stats about your visitors as they access your site, i.e. in real time. 5 |

6 | If you're a blogger, LLOOGG will allow you to check readers' reactions to your posts and observe their navigation patterns in real time. 7 |

How does this work?

8 |

9 | It's easy. Create a user account, paste the javascript on your web pages and you're done! In just a few seconds you will start seeing your stats in real time.

10 | LLOOGG is an open source project. Source code can be found in this Github repository. 11 | -------------------------------------------------------------------------------- /login.html.php: -------------------------------------------------------------------------------- 1 | 6 | 7 |

8 | 9 | 10 | 11 | 12 | 13 |
Username
Password
Remember me 
14 |
15 | 16 |

You are already logged in as 17 | ! 18 |

To switch user logout first. 19 | 20 | 23 | -------------------------------------------------------------------------------- /ajax/login.php: -------------------------------------------------------------------------------- 1 | get("username:$user:id"); 11 | if(!$userid){ 12 | echo("ERR"); 13 | exit; 14 | } 15 | 16 | if(bcrypt_check($pass,$r->get("uid:$userid:hashpass"))) { 17 | $secret=$r->get("uid:$userid:auth"); 18 | if (gi('rememberme',0) == 1) { 19 | $now = time()+3600*24*365; 20 | setCookie("secret",$secret,$now,"/"); 21 | setCookie("secret",$secret,$now,"/",Config("domain")); 22 | setCookie("secret",$secret,$now,"/",".".Config("domain")); 23 | } else { 24 | // Just for this session. 25 | setCookie("secret",$secret,0,"/"); 26 | setCookie("secret",$secret,0,"/",Config("domain")); 27 | setCookie("secret",$secret,0,"/",".".Config("domain")); 28 | } 29 | echo("OK:AUTHENTICATED"); 30 | } else { 31 | echo "ERR"; 32 | } 33 | ?> 34 | -------------------------------------------------------------------------------- /feedbacks.html.php: -------------------------------------------------------------------------------- 1 | 6 |

Send feedbacks

7 |
8 | 9 | 17 | 18 | 19 | 20 | 21 |
Subject: 10 | 16 |
Your email (for reply):
Message:
22 |
23 | 24 | -------------------------------------------------------------------------------- /header.php: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 15 | 16 | 17 | <?echo Config("title")?> 18 | 19 | 20 | 21 | 36 | 37 |
38 | 39 | -------------------------------------------------------------------------------- /javascript/login.js: -------------------------------------------------------------------------------- 1 | function tryLogin() { 2 | clearFields(new Array("username","pass")); 3 | if (!validate(document.f.username.value, "^[A-z0-9]+$", "Invalid username","username")){ 4 | return false; 5 | } 6 | // Use AJAX to check if the user/password are valid. 7 | mfxGetRand("/ajax/login.php?username="+mfxEscape(document.f.username.value)+"&pass="+mfxEscape(document.f.pass.value)+"&rememberme="+(document.f.rememberme.checked?'1':'0'), loginHandler); 8 | } 9 | function loginHandler(res) { 10 | if (res.indexOf("OK:") != -1) { 11 | // Login success. 12 | var l = window.location.toString(); 13 | var i = l.indexOf("?goto="); 14 | if (i == -1) { 15 | if (typeof(window.opera) != 'undefined') { 16 | window.location = '/?l=1'; 17 | } else { 18 | window.location = '/'; 19 | } 20 | } else { 21 | i+=5; 22 | l = unescape(l.substring(i,l.length+1)); 23 | window.location = l; 24 | } 25 | } else { 26 | // Login failed. Show an error. 27 | warnField("username"); 28 | warnField("pass"); 29 | alert('The username and password you entered don\'t match a valid account, please verify and retry.'); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /usernav.php: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | | 5 | $link) { 17 | if ($_SERVER['REQUEST_URI'] == $link || 18 | strpos($_SERVER['REQUEST_URI'],$link."?") === 0) { 19 | echo("".htmlentities($title)." "); 20 | } else { 21 | echo("".htmlentities($title)." "); 22 | } 23 | if ($c++ != count($n)) echo(" | "); 24 | } 25 | $allowed = getAllowed(); 26 | if (count($allowed)) { 27 | $ru = isset($_COOKIE['requser']) ? $_COOKIE['requser'] : userName(); 28 | echo(" | "); 36 | } 37 | } 38 | userNav(); 39 | ?> 40 |
41 | -------------------------------------------------------------------------------- /javascript/form.js: -------------------------------------------------------------------------------- 1 | /* Copyright(C) 2005-2007 Salvatore Sanfilippo 2 | * All Rights Reserved. */ 3 | 4 | function validate(string, regexp, err, fieldName) { 5 | if (string.match(regexp) == null) { 6 | alert(err); 7 | warnField(fieldName); 8 | return false; 9 | } 10 | return true; 11 | } 12 | function validateEmpty(string,err,filedName) { 13 | return validate(string, "^.*[^ ]+.*$", err, filedName); 14 | } 15 | function isValidEmail(a) 16 | { 17 | var at = a.indexOf("@"); 18 | var name = a.substring(0, at); 19 | var isp = a.substring(at + 1, a.length); 20 | var dot = a.lastIndexOf("."); 21 | if (at == -1 || at == 0 || name == "" || isp == "" || dot == -1 || 22 | dot== (a.length - 1)) 23 | { 24 | return false; 25 | } 26 | return true; 27 | } 28 | function warnField(fieldName) 29 | { 30 | eval("document.f."+fieldName+".style.border='1px red solid'"); 31 | eval("document.f."+fieldName+".focus();"); 32 | } 33 | function clearFields(fields, hidelist) { 34 | for (i = 0; i < fields.length; i++) { 35 | eval("document.f."+fields[i]+".style.border='1px inset #ddd'"); 36 | } 37 | if (hidelist != null) { 38 | for (i = 0; i < hidelist.length; i++) { 39 | mfxHide(hidelist[i]); 40 | } 41 | } 42 | } 43 | function areyousure(message) { 44 | return confirm(message + ": are you sure?"); 45 | } 46 | -------------------------------------------------------------------------------- /register.php: -------------------------------------------------------------------------------- 1 | get("username:$username:id")) 16 | panic("Username already in use"); 17 | 18 | # Everything is ok, Register the user! 19 | $userid = $r->incr("global:nextUserId"); 20 | $r->set("username:$username:id",$userid); 21 | $r->set("uid:$userid:username",$username); 22 | $r->set("uid:$userid:hashpass",bcrypt_hash($password)); 23 | $r->set("uid:$userid:email",$email); 24 | $r->set("uid:$userid:regtime",time()); 25 | $r->set("uid:$userid:excludemyvisits",'0'); 26 | 27 | $authsecret = getrand(); 28 | $r->set("uid:$userid:auth",$authsecret); 29 | $r->set("auth:$authsecret",$userid); 30 | 31 | # Manage a Set with all the users, may be userful in the future 32 | $r->sadd("global:users",$userid); 33 | 34 | # User registered! Login this guy 35 | $now = time()+3600*24*365; 36 | setCookie("secret",$authsecret,$now,"/"); 37 | setCookie("secret",$authsecret,$now,"/",Config("domain")); 38 | setCookie("secret",$authsecret,$now,"/",".".Config("domain")); 39 | 40 | # Send the email 41 | sendMail("welcome.txt",Config("emailreg"),$email,"%password%",$password,"%username%",$username); 42 | 43 | header("Location: /success.html.php"); 44 | ?> 45 | -------------------------------------------------------------------------------- /l.js: -------------------------------------------------------------------------------- 1 | function __lloogg__() { 2 | var dc = document.cookie; 3 | var nv = 0; 4 | var rv = 1; 5 | if (dc.indexOf('__llooggrvc__=') == -1) { 6 | document.cookie = "__llooggrvc__=1; expires=Sun, 18 Jan 2038 00:00:00 GMT; path=/"; 7 | rv = 0; 8 | } 9 | if (dc.indexOf('__lloogguvc__=') == -1) { 10 | nv = 1; 11 | var now = new Date; 12 | now.setTime(now.getTime()+(3600*24*1000)); 13 | document.cookie = "__lloogguvc__=1; expires="+now.toGMTString()+"; path=/"; 14 | } 15 | var u = lloogg_clientid; 16 | var l = document.location; 17 | var r = (typeof(document.referrer) == 'undefined') ? '' : document.referrer; 18 | var w = screen.width; 19 | var h = screen.height; 20 | var a = navigator.userAgent; 21 | var hl = 0; 22 | var c = 'na'; 23 | if (typeof(navigator.cookieEnabled) != 'undefined') 24 | c = navigator.cookieEnabled ? 'y' : 'n'; 25 | if (typeof(history) != 'undefined' && typeof(history.length) != 'undefined') 26 | hl = history.length; 27 | var e = function (s) { 28 | try { 29 | return encodeURIComponent(s); 30 | } catch(e) { 31 | var e = escape(s); 32 | e = e.replace(/@/g,"%40"); 33 | e = e.replace(/\//g,"%2f"); 34 | e = e.replace(/\+/g,"%2b"); 35 | return e; 36 | } 37 | }; 38 | var args=''; 39 | var img=new Image(1,1); 40 | args += '?u='+e(u); args += '&l='+e(l); args += '&r='+e(r); 41 | args += '&w='+e(w); args += '&h='+e(h); args += '&a='+e(a); 42 | args += '&c='+e(c); args += '&hl='+e(hl); args += "&nv="+e(nv); 43 | args += '&rv='+e(rv); 44 | img.src='http://lloogg.com/recv.php'+args; 45 | img.onload = function() { return; }; 46 | } 47 | __lloogg__(); 48 | -------------------------------------------------------------------------------- /l_local.js: -------------------------------------------------------------------------------- 1 | function __lloogg__() { 2 | var dc = document.cookie; 3 | var nv = 0; 4 | var rv = 1; 5 | if (dc.indexOf('__llooggrvc__=') == -1) { 6 | document.cookie = "__llooggrvc__=1; expires=Sun, 18 Jan 2038 00:00:00 GMT; path=/"; 7 | rv = 0; 8 | } 9 | if (dc.indexOf('__lloogguvc__=') == -1) { 10 | nv = 1; 11 | var now = new Date; 12 | now.setTime(now.getTime()+(3600*24*1000)); 13 | document.cookie = "__lloogguvc__=1; expires="+now.toGMTString()+"; path=/"; 14 | } 15 | var u = lloogg_clientid; 16 | var l = document.location; 17 | var r = (typeof(document.referrer) == 'undefined') ? '' : document.referrer; 18 | var w = screen.width; 19 | var h = screen.height; 20 | var a = navigator.userAgent; 21 | var hl = 0; 22 | var c = 'na'; 23 | if (typeof(navigator.cookieEnabled) != 'undefined') 24 | c = navigator.cookieEnabled ? 'y' : 'n'; 25 | if (typeof(history) != 'undefined' && typeof(history.length) != 'undefined') 26 | hl = history.length; 27 | var e = function (s) { 28 | try { 29 | return encodeURIComponent(s); 30 | } catch(e) { 31 | var e = escape(s); 32 | e = e.replace(/@/g,"%40"); 33 | e = e.replace(/\//g,"%2f"); 34 | e = e.replace(/\+/g,"%2b"); 35 | return e; 36 | } 37 | }; 38 | var args=''; 39 | var img=new Image(1,1); 40 | args += '?u='+e(u); args += '&l='+e(l); args += '&r='+e(r); 41 | args += '&w='+e(w); args += '&h='+e(h); args += '&a='+e(a); 42 | args += '&c='+e(c); args += '&hl='+e(hl); args += "&nv="+e(nv); 43 | args += '&rv='+e(rv); 44 | img.src='http://local.lloogg.com/recv.php'+args; 45 | img.onload = function() { return; }; 46 | } 47 | __lloogg__(); 48 | -------------------------------------------------------------------------------- /register.html.php: -------------------------------------------------------------------------------- 1 | 10 |

Create account

11 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
Username:
Your email address is only used for registration and to resend lost passwords, we'll not send any spam message to you
Email:Retype email:
Password:Retype password:
41 | "> 42 |
43 | 46 | -------------------------------------------------------------------------------- /pro.php: -------------------------------------------------------------------------------- 1 | get("uid:$id:pro"); 13 | if (!$pro) { 14 | $__procache = false; 15 | return $__procache; 16 | } 17 | $__procache = $pro; 18 | return true; 19 | } 20 | 21 | function setPro($uid,$level,$duration=3600) { 22 | $r = redisLink(); 23 | if ($level == 0) { 24 | $r->del("uid:$uid:pro"); 25 | $r->del("uid:$uid:pro.since"); 26 | $r->del("uid:$uid:pro.until"); 27 | $r->srem("global:prousers",$uid); 28 | } else { 29 | $r->set("uid:$uid:pro",$level); 30 | $r->set("uid:$uid:pro.since",time()); 31 | $r->set("uid:$uid:pro.until",time()+$duration); 32 | $r->sadd("global:prousers",$uid); 33 | } 34 | } 35 | 36 | function getAllowing($uid=false) { 37 | if ($uid === false) $uid = userId(); 38 | $r = redisLink(); 39 | return $r->smembers("uid:$uid:allowing"); 40 | } 41 | 42 | function getAllowed($uid=false) { 43 | if ($uid === false) $uid = userId(); 44 | $r = redisLink(); 45 | return $r->smembers("uid:$uid:allowed"); 46 | } 47 | 48 | function addAllowed($allowing_id,$allowed_id) { 49 | $r = redisLink(); 50 | $r->sadd("uid:$allowing_id:allowing",$allowed_id); 51 | return $r->sadd("uid:$allowed_id:allowed",$allowing_id); 52 | } 53 | 54 | function delAllowed($allowing_id,$allowed_id) { 55 | $r = redisLink(); 56 | $r->srem("uid:$allowing_id:allowing",$allowed_id); 57 | return $r->srem("uid:$allowed_id:allowed",$allowing_id); 58 | } 59 | 60 | function reqUserId() { 61 | global $_COOKIE; 62 | 63 | $r = redisLink(); 64 | if (!isset($_COOKIE['requser'])) return userId(); 65 | $username = $_COOKIE['requser']; 66 | if (($id = getIdFromUsername($username)) == -1) { 67 | return userId(); 68 | } 69 | if ($r->sismember("uid:$id:allowing",userId())) { 70 | return $id; 71 | } else { 72 | return userId(); 73 | } 74 | } 75 | 76 | function getProLevel() { 77 | global $__procache; 78 | return $__procache; 79 | } 80 | ?> 81 | -------------------------------------------------------------------------------- /secureid.php: -------------------------------------------------------------------------------- 1 | 4 | # All Rights Reserved. 5 | 6 | # This file implements secure IDs, that can be safely exposed on the web. 7 | # The algoritm is simple. Given the ID, the createSecureId() function 8 | # returns (ID in hex)|(MD5 (|ID in hex|)) 9 | # The function readSecureId() check the signature and returns 10 | # the ID if the signature matches, otherwise -1 is returned. 11 | # 12 | # The ID value is bit-shuffled before to be conveted into an HEX number 13 | # in order to obtain some degree of obfuscation of the actual ID number. 14 | # 15 | # Note that the IDs are not encrpyted, but just signed and obfuscated. 16 | # The attacker can't create a new ID, nor a random one, but 17 | # can be able to read the ID cracking just the shuffle step. 18 | # This is usually not a problem for the applications in segnalo.com 19 | # where it is important only that IDs can't be forged, but they can be 20 | # safely read. 21 | 22 | $idsecret = "@/@#?2/32kjhasfdkjhsdfe982394895723489uSDIUPSDYsdfupydsfuisyf3"; 23 | $shuffleseq = Array("5","3","20","16","9","27","29","23","4","0","21","24","7","6","12","14","11","30","28","22","18","2","15","17","10","25","26","8","13","1","19","31"); 24 | 25 | function shuffleId($id) { 26 | global $shuffleseq; 27 | $sid = 0; 28 | for ($i = 0; $i < 32; $i++) { 29 | $sid |= (($id&(1<<$i))>>$i)<<$shuffleseq[$i]; 30 | } 31 | return $sid; 32 | } 33 | 34 | function unshuffleId($sid) { 35 | global $shuffleseq; 36 | $id = 0; 37 | for ($i = 0; $i < 32; $i++) { 38 | $id |= ((($sid&(1<<$shuffleseq[$i]))>>$shuffleseq[$i])) << $i; 39 | } 40 | return $id; 41 | } 42 | 43 | function createSecureId($id,$subkey) { 44 | global $idsecret; 45 | $hex = sprintf("%x",shuffleId($id)); 46 | $mac = md5($idsecret.$hex.$subkey.$idsecret); 47 | $mac = substr($mac,0,8); 48 | return $hex.$mac; 49 | } 50 | 51 | function readSecureId($str,$subkey) { 52 | global $idsecret; 53 | $l = strlen($str); 54 | if ($l <= 8) return -1; 55 | $hex = substr($str, 0, $l-8); 56 | $mac = substr($str, -8, 8); 57 | $vmac = md5($idsecret.$hex.$subkey.$idsecret); 58 | $vmac = substr($vmac,0,8); 59 | if ($mac !== $vmac) return -1; 60 | sscanf($hex, "%x", $id); 61 | return unshuffleId($id); 62 | } 63 | 64 | ?> 65 | -------------------------------------------------------------------------------- /javascript/register.js: -------------------------------------------------------------------------------- 1 | function submitForm() { 2 | document.f.submit(); 3 | } 4 | function registrationCheckUsername() { 5 | mfxGetRand("/ajax/checkusername.php?username="+document.f.usernameReg.value, 6 | function(c) { 7 | if (c.indexOf("OK") != -1) { 8 | submitForm(); 9 | } else { 10 | warnField('usernameReg'); 11 | regErrUsername(); 12 | enableButton(); 13 | } 14 | }); 15 | } 16 | function regErrUsername() { 17 | var text='The username selected is already in use and can\'t be used again, please select a different username in order to continue.'; 18 | regErr(text,"usernameReg"); 19 | } 20 | function regErr(text,field){ 21 | mfxShow('regerrdiv'); 22 | $('regerrtxt').innerHTML=text; 23 | warnField(field); 24 | } 25 | function checkRegistrationForm() { 26 | disableButton(); 27 | clearFields(new Array("email","reemail","usernameReg","passReg","repassReg"), new Array("regerrdiv")); 28 | if (!validate(document.f.usernameReg.value, "^[A-z0-9]+$", "Empty username or special characters in username are invalid.","usernameReg")){ 29 | enableButton(); 30 | return false; 31 | } 32 | if (!validate(document.f.passReg.value, "^.{5,}$", "Password too short! Minimal password length is 5 chars.","passReg")){ 33 | enableButton(); 34 | return false; 35 | } 36 | if (document.f.passReg.value != document.f.repassReg.value) { 37 | alert("The two passwords fields are not the same."); 38 | warnField("passReg"); 39 | warnField("repassReg"); 40 | enableButton(); 41 | return false; 42 | } 43 | if (!isValidEmail(document.f.email.value)) { 44 | alert("Invalid email address."); 45 | warnField("email"); 46 | enableButton(); 47 | return false; 48 | } 49 | if(document.f.email.value != document.f.reemail.value){ 50 | alert("The two email fields are not the same."); 51 | warnField("email"); 52 | warnField("reemail"); 53 | enableButton(); 54 | return false; 55 | } 56 | registrationCheckUsername(); 57 | return false; 58 | } 59 | function disableButton(){ 60 | $("registerButton").disabled=true; 61 | $("registerButton").value="Wait..."; 62 | 63 | } 64 | function enableButton(){ 65 | $("registerButton").disabled=false; 66 | $("registerButton").value="Create my account"; 67 | } 68 | -------------------------------------------------------------------------------- /search.php: -------------------------------------------------------------------------------- 1 | "Google", 8 | "nbrand"=>"google".$m[2], 9 | "query"=>$m[6] 10 | ); 11 | } 12 | /* Bing */ 13 | else if (preg_match('/http:\/\/([^.]+\.)?bing.com\/(search|spresults).*(&|\?)q=([^&]+)/',$ref,$m)) { 14 | $res = Array( 15 | "brand"=> "Bing", 16 | "nbrand"=> "bing.com", 17 | "query"=> $m[4] 18 | ); 19 | } 20 | /* Yahoo */ 21 | else if (preg_match('/http:\/\/([^.]+\.)?search.yahoo.com\/search.*(&|\?)p=([^&]+)/',$ref,$m)) { 22 | $res = Array( 23 | "brand"=> "Yahoo", 24 | "nbrand"=> $m[1]."search.yahoo.com", 25 | "query"=> $m[3] 26 | ); 27 | } 28 | /* Microsoft Live */ 29 | else if (preg_match('/http:\/\/search.live.com\/results.aspx.*(&|\?)q=([^&]+)/',$ref,$m)) { 30 | $res = Array( 31 | "brand"=> "MS LIVE", 32 | "nbrand"=> "search.live.com", 33 | "query"=> $m[2] 34 | ); 35 | } 36 | /* Alice */ 37 | else if (preg_match('/http:\/\/search.alice.it\/search\/cgi\/search.cgi.*(&|\?)qs=([^&]+)/',$ref,$m)) { 38 | $res = Array( 39 | "brand"=> "Alice", 40 | "nbrand"=> "search.alice.it", 41 | "query"=> $m[2] 42 | ); 43 | } 44 | /* Virgilio */ 45 | // http://ricerca.virgilio.it/ricerca?qs=collettivamente.com&Cerca=&lr= 46 | else if (preg_match('/http:\/\/ricerca.virgilio.it\/ricerca.*(&|\?)qs=([^&]+)/',$ref,$m)) { 47 | $res = Array( 48 | "brand"=> "Virgilio", 49 | "nbrand"=> "ricerca.virgilio.it", 50 | "query"=> $m[2] 51 | ); 52 | } 53 | /* Arianna */ 54 | else if (preg_match('/http:\/\/arianna.libero.it\/search\/abin\/integrata.cgi.*(&|\?)query=([^&]+)/',$ref,$m)) { 55 | $res = Array( 56 | "brand"=> "Arianna", 57 | "nbrand"=> "arianna.libero.it", 58 | "query"=> $m[2] 59 | ); 60 | } 61 | /* MSN */ 62 | else if (preg_match('/http:\/\/search.msn.([^.]+)\/results.asp.*(&|\?)q=([^&]+)/',$ref,$m)) { 63 | $res = Array( 64 | "brand"=> "MSN", 65 | "nbrand"=> "search.msn.".$m[1], 66 | "query"=> $m[3] 67 | ); 68 | } else { 69 | return false; 70 | } 71 | $res['query'] = urldecode($res['query']); 72 | return $res; 73 | } 74 | ?> 75 | -------------------------------------------------------------------------------- /recv.php: -------------------------------------------------------------------------------- 1 | incr("global:nextLogId"); 45 | $aux = Array( "time" => $time, 46 | "user_id" => $uid, 47 | "event_id" => $eventid, 48 | "location" => $location, 49 | "proto" => $url['proto'], 50 | "domain" => $url['domain'], 51 | "path" => $url['path'], 52 | "query" => $url['query'], 53 | "ref" => $referer, 54 | "swidth" => $width, 55 | "sheight" => $height, 56 | "cookies" => $cookies, 57 | "ip" => $ip, 58 | "agent" => $agent, 59 | "historylen" => $historylen, 60 | "id" => $logid); 61 | # Insert new 62 | $r->push("last:$uid",serialize($aux),false); 63 | 64 | # History length is different for PRO / non PRO user. 65 | $r->ltrim("last:$uid",0,$ispro ? 1000 : 50); 66 | 67 | if ($eventid != LOG_EVENT_PAGEVIEW) return; 68 | # Update stats 69 | $nv = gi('nv',0); 70 | $rv = gi('rv',0); 71 | $t = time(); 72 | $t = $t-($t%(3600*24)); 73 | 74 | # Update unique visits 75 | if ($nv) { 76 | $r->incr("day:uv:$uid:$t"); 77 | if ($rv) { 78 | $r->incr("day:rv:$uid:$t"); 79 | } 80 | } 81 | $r->incr("day:pv:$uid:$t"); 82 | } 83 | 84 | function outputGif() { 85 | header("Content-Type: image/gif"); 86 | include("images/1x1.gif"); 87 | } 88 | 89 | $exclude = 0; 90 | if (isset($_COOKIE['excludemyvisits'])) { 91 | $uid=readSecureId(g('u',''),"userid"); 92 | $xid=readSecureId($_COOKIE['excludemyvisits'],"excludeid"); 93 | if($uid==$xid) $exclude=1; 94 | } 95 | if (!$exclude) recv(); 96 | outputgif(); 97 | ?> 98 | -------------------------------------------------------------------------------- /user.php: -------------------------------------------------------------------------------- 1 | get("auth:$authcookie")) { 44 | if ($r->get("uid:$userid:auth") != $authcookie) return false; 45 | loadUserInfo($userid); 46 | return true; 47 | } 48 | } 49 | return false; 50 | } 51 | 52 | function loadUserInfo($userid) { 53 | global $User; 54 | $r = redisLink(); 55 | $keys=Array('username','password',"email",'excludemyvisits','regtime','showuseragent'); 56 | $uidkeys = Array(); 57 | foreach($keys as $k) { 58 | $uidkeys[] = "uid:$userid:$k"; 59 | } 60 | $User['id'] = $userid; 61 | $values=$r->mget($uidkeys); 62 | for($i = 0; $i < count($keys); $i++) { 63 | $User[$keys[$i]] = $values[$i]; 64 | } 65 | } 66 | 67 | function isAdmin() { 68 | global $User; 69 | if (!isLoggedIn()) return 0; 70 | if (Config("adminuser") && $User['username'] == Config("adminuser")) return 1; 71 | return 0; 72 | } 73 | 74 | function getUsernameById($id) { 75 | if ($id == -1) return "Utente anonimo"; 76 | $r = redisLink(); 77 | $username = $r->get("uid:$id:username"); 78 | if ($username) { 79 | return $username; 80 | } else { 81 | return "Utente rimosso"; // Should never happen. Here as sentinel. 82 | } 83 | } 84 | 85 | // Get the ID from the username, if the username does not exists 86 | // -1 is returned. 87 | function getIdFromUsername($username) { 88 | $username = str_replace(" ","_",$username); 89 | $r = redisLink(); 90 | $id = $r->get("username:$username:id"); 91 | if (!$id) { 92 | return -1; 93 | } else { 94 | return $id; 95 | } 96 | } 97 | 98 | function userUpdateExcludeVisitsCookie() { 99 | global $_COOKIE; 100 | 101 | if (!isLoggedIn()) return; 102 | if (userExcludeMyVisits()) { 103 | if (!isset($_COOKIE['excludemyvisits'])) { 104 | $exclid = createSecureId(userId(),"excludeid"); 105 | setCookie("excludemyvisits",$exclid,2147483647,"/"); 106 | } 107 | } else { 108 | if (isset($_COOKIE['excludemyvisits'])) { 109 | setCookie("excludemyvisits","",time()-3600*48,"/"); 110 | } 111 | } 112 | } 113 | ?> 114 | -------------------------------------------------------------------------------- /usercode.php: -------------------------------------------------------------------------------- 1 | set("uid:".$User['id'].":excludemyvisits",$excludemyvisits); 10 | $r->set("uid:".$User['id'].":showuseragent",$showuseragent); 11 | userUpdateExcludeVisitsCookie(); 12 | header("Location: /settings?s=1"); 13 | exit; 14 | } 15 | 16 | Config("title","LLOOGG - web 2.0 tail -f access.log"); 17 | include("header.php"); 18 | ?> 19 |
20 |

21 | Cut and paste the following code in every page of your site for wich you are interested in seeing accesses in real time. 22 | Important notes: 23 |

    24 |
  • It's better to put the code just above the closing </body> tag.
  • 25 |
  • If you are using wordpress you can find the </body> tag is in the footer.php page.
  • 26 |
27 |

28 | 30 | lloogg_clientid = \"%%clientid%%\"; 31 | 32 | "; 34 | $_js=str_replace("%%clientid%%",createSecureId(userId(),"userid"),$_js); 35 | $_js=str_replace("%%urldomain%%",Config("urldomain"),$_js); 36 | ?> 37 | 40 |

41 | 42 |

Status

43 | lindex("last:".userId(),-1); 46 | $statusok = false; 47 | if ($lastvisit) { 48 | $row = unserialize($lastvisit); 49 | if ((time() - $row['time']) < 3600*24) 50 | $statusok = true; 51 | } 52 | if ($statusok) { 53 | echo("We are correctly receiving data"); 54 | } else { 55 | echo("We are NOT receiving data, make sure to install the javascript tag in your web site"); 56 | } 57 | ?> 58 | (saved)' : ''; 62 | ?> 63 |

Options

64 |
65 | Don't log my own visits >

66 | Show clients user agent >

67 | 68 |

69 |
70 |

Give access

71 | It is possible to give read-only access to your stats to other LLOOGG users. You can later remove the access if you want.

72 | Allow 73 | 74 | to see my stats in read only 75 | 76 | Allowed users"); 80 | echo("
    "); 81 | foreach($allowed as $id) { 82 | echo("
  • ".utf8entities(getUsernameById($id))." remove
  • "); 83 | } 84 | echo("
"); 85 | } 86 | ?> 87 |
88 | 101 | 104 | -------------------------------------------------------------------------------- /poll.php: -------------------------------------------------------------------------------- 1 | lindex("last:$uid",0)); 21 | $minid = $latest['id']; 22 | } else if ($minid < 0) { 23 | # minid < 0 means: load history, with abs(minid) elements 24 | $numres = abs($minid); 25 | if ($numres > 1000) $numres = 1000; 26 | $loadhistory = 1; 27 | } 28 | $rows = $r->lrange("last:$uid",0,$numres-1); 29 | 30 | # Handle free accounts timeouts. 31 | # NOTE: this is actually disabled, left here for historical reasons 32 | # and because the javascript still handles it. 33 | $ispro = isPro($uid); 34 | if (0 && !$ispro) { 35 | $startpoll = $r->get("startpoll:$uid"); 36 | if (!$startpoll) { 37 | $r->set("startpoll:$uid",time()); 38 | } else { 39 | $delta = time()-$r->get("startpoll:$uid"); 40 | if ($delta > FREE_MAXTIME+FREE_WAIT) { 41 | $r->delete("startpoll:$uid"); 42 | } else if ($delta > FREE_MAXTIME) { 43 | echo("TRYLATER:".floor(1+((FREE_MAXTIME+FREE_WAIT-$delta)/60))); 44 | exit; 45 | } 46 | } 47 | } 48 | 49 | # Empty list? 50 | if (count($rows) == 0) { 51 | echo("NODATA"); 52 | exit; 53 | } 54 | 55 | # Check if even the most recent element (the first one) 56 | # is still too old. If so, no new data to return. 57 | $latest = unserialize($rows[0]); 58 | if ($latest['id'] < $minid) { 59 | echo("NODATA"); 60 | exit; 61 | } 62 | 63 | # Try to get all the data required. Up to 50 elements for request 64 | while(!$loadhistory) { 65 | $oldest = unserialize($rows[count($rows)-1]); 66 | if ($oldest['id'] > $minid && $numres < 50) { 67 | # We need more data 68 | $numres = ($numres+1)*2; 69 | if ($numres > 50) $numres=50; 70 | $rows = $r->lrange("last:$uid",0,$numres-1); 71 | } else { 72 | break; 73 | } 74 | } 75 | 76 | # Ok now reverse the array to sort ascending and return data to ajax. 77 | $rows = array_reverse($rows); 78 | $gi = geoip_open("geoipdata/GeoIP.dat",GEOIP_STANDARD); 79 | foreach($rows as $srow) { 80 | $row = unserialize($srow); 81 | if ($row['id'] < $minid) continue; 82 | $keys = Array("time","location","domain","ref","swidth","sheight","cookies","ip","agent","historylen"); 83 | $aux = Array(); 84 | foreach($keys as $k) 85 | $aux[$k] = $row[$k]; 86 | $aux['country'] = geoip_country_name_by_addr($gi,$aux['ip']); 87 | $aux['type'] = 'pageview'; 88 | 89 | # At some point LLOOGG supported the ability to display user clicks 90 | # adsense ADs. Now the javascript we inject no longer support this 91 | # but the support inside LLOOGG itself remains. 92 | if ($row['event_id'] == LOG_EVENT_ADCLICK) $aux['type'] = 'adclick'; 93 | $t[] = $aux; 94 | if($row['id'] > $maxid) $maxid = $row['id']; 95 | } 96 | geoip_close($gi); 97 | $t[] = $maxid+1; 98 | if ($proto >= 2) { 99 | $t[] = logTodayVisitors(); 100 | $t[] = logTodayPageviews(); 101 | } 102 | $json = new Services_JSON(); 103 | $output = $json->encode($t); 104 | echo($output); 105 | } 106 | header("Content-Type: text/html; charset=UTF-8"); 107 | poll(); 108 | ?> 109 | -------------------------------------------------------------------------------- /usermain.php: -------------------------------------------------------------------------------- 1 | 3 |
4 | 5 | 6 | 16 | 17 | 18 | 19 | 20 |
load historyopen filters
21 | 65 | 68 |
69 |
70 |
71 |

Today trend

72 | Visitors, Pageviews
73 | See history and graphs » 74 |
75 |
76 |
77 |
Realtime stats
78 |
79 |
80 |
81 | 82 |
83 |
Top referers
84 |
85 |
86 |
87 | 88 |
89 |
Top searches
90 |
91 |
92 |
93 | 94 |
95 |
Top pages
96 |
97 |
98 |
99 | 100 |
101 |
Browsers war
102 |
103 |
104 |
105 |
106 | 107 | 111 |
112 | -------------------------------------------------------------------------------- /utils.php: -------------------------------------------------------------------------------- 1 | "); 5 | if ($a === false) { 6 | echo("GET:"); 7 | var_dump($_GET); 8 | echo("POST:"); 9 | var_dump($_POST); 10 | echo("SESSION:"); 11 | var_dump($_SESSION); 12 | echo("FILES:"); 13 | var_dump($_FILES); 14 | } else { 15 | var_dump($a); 16 | } 17 | echo(""); 18 | } 19 | 20 | function panic($msg = "Unknown Error") { 21 | echo("$msg - contact the system administrator"); 22 | exit(1); 23 | } 24 | 25 | function requireGetVarsOrPanic($vars) { 26 | global $_GET; 27 | for ($i = 0; $i < count($vars); $i++) { 28 | if (!isset($_GET[$vars[$i]])) { 29 | panic("(GET) Missing arguments"); 30 | } 31 | } 32 | } 33 | 34 | function requireGetNonEmptyOrPanic($vars) { 35 | global $_GET; 36 | for ($i = 0; $i < count($vars); $i++) { 37 | if (!isset($_GET[$vars[$i]]) || strlen($_GET[$vars[$i]]) == 0) { 38 | panic("(GET) Empty arguments, $vars[$i]"); 39 | } 40 | } 41 | } 42 | 43 | function requirePostVarsOrPanic($vars) { 44 | global $_POST; 45 | for ($i = 0; $i < count($vars); $i++) { 46 | if (!isset($_POST[$vars[$i]])) { 47 | panic("(POST) Missing arguments"); 48 | } 49 | } 50 | } 51 | 52 | function requirePostNonEmptyOrPanic($vars) { 53 | global $_POST; 54 | for ($i = 0; $i < count($vars); $i++) { 55 | if (!isset($_POST[$vars[$i]]) || strlen($_POST[$vars[$i]]) == 0) { 56 | panic("(POST) Empty arguments, $vars[$i]"); 57 | } 58 | } 59 | } 60 | 61 | function getrand() { 62 | $fd = fopen("/dev/urandom", "r"); 63 | $data = fread($fd, 16); 64 | fclose($fd); 65 | return md5($data); 66 | } 67 | 68 | function fileGetContent($filename) { 69 | return implode("", file($filename)); 70 | } 71 | 72 | //sendMail(filename,from,to,var1,value1, ....,varN,valueN); 73 | function sendMail() { 74 | $argv = func_get_args(); 75 | $argc = count($argv); 76 | if (($argc % 2) == 0 || $argc < 3) { 77 | die("sendMail() called with wrong argument count"); 78 | } 79 | $from = $argv[1]; 80 | $to = $argv[2]; 81 | $email = fileGetContent("mail/$argv[0]"); 82 | $email = str_replace("\r", "", $email); 83 | $lines = explode("\n", $email); 84 | $subject = $lines[0]; 85 | array_shift($lines); 86 | $email = implode("\n", $lines); 87 | for ($i = 3; $i < count($argv); $i += 2) { 88 | $subject = str_replace($argv[$i], $argv[$i+1], $subject); 89 | $email = str_replace($argv[$i], $argv[$i+1], $email); 90 | } 91 | $e_hdr = "From: $from\nReply-To: $from\n"; 92 | mail($to, $subject, $email, $e_hdr); 93 | } 94 | 95 | function sendMailBody($from, $to, $subject, $body) { 96 | $e_hdr = "From: $from\nReply-To: $from\n"; 97 | mail($to, $subject, $body, $e_hdr); 98 | } 99 | 100 | function strelapsed($t) { 101 | $t = time()-$t; 102 | if ($t < 60) { 103 | return "$t seconds"; 104 | } 105 | if ($t < 3600) { 106 | $m = (int)($t/60); 107 | if($m == 1) 108 | return "$m minute"; 109 | else 110 | return "$m minutes"; 111 | } 112 | if ($t < (3600*48)) { 113 | $h = (int)($t/3600); 114 | if($h == 1) 115 | return "$h hour"; 116 | else 117 | return "$h hours"; 118 | } 119 | $d = (int)($t/(3600*24)); if($d == 1) 120 | return "$d day"; 121 | else 122 | return "$d days"; 123 | } 124 | 125 | function utf8entities($s) { 126 | return trim(htmlentities($s,ENT_QUOTES,'utf-8')); 127 | } 128 | 129 | function displayEmailNoSpam($email){ 130 | echo emailNoSpam($email); 131 | } 132 | 133 | function emailNoSpam($email){ 134 | if(empty($email)) return false; 135 | $array=explode("@",$email); 136 | if(!is_array($array) || count($array) != 2) return $email; 137 | return ''; 138 | } 139 | 140 | function first($a) { 141 | return $a[0]; 142 | } 143 | 144 | function rest($a) { 145 | return array_slice($a,1); 146 | } 147 | 148 | function goHome() { 149 | header("Location: /"); 150 | exit; 151 | } 152 | 153 | function timeBase($t = false) { 154 | if ($t === false) $t = time(); 155 | $t = $t-($t%(3600*24)); 156 | return $t; 157 | } 158 | 159 | ?> 160 | -------------------------------------------------------------------------------- /trends.php: -------------------------------------------------------------------------------- 1 | get("day:pv:$uid:$t"); 8 | if ($n) $pageviews = $n; 9 | $n = $r->get("day:uv:$uid:$t"); 10 | if ($n) $visitors = $n; 11 | return Array("pageviews"=>$pageviews,"visitors"=>$visitors); 12 | } 13 | 14 | function logTodayVisitors() { 15 | $trend = logTodayTrend(); 16 | return $trend['visitors']; 17 | } 18 | 19 | function logTodayPageviews() { 20 | $trend = logTodayTrend(); 21 | return $trend['pageviews']; 22 | } 23 | 24 | function logGraphMaxValue($data) { 25 | if (count($data) == 0) return 0; 26 | $max = $data[0]['val']; 27 | foreach ($data as $v) { 28 | $v = $v['val']; 29 | $max = ($v > $max) ? $v : $max; 30 | } 31 | return $max; 32 | } 33 | 34 | function logGraphBarHeight($logscale,$max,$value) { 35 | if ($max == 0 || $value == 0) return "0%"; 36 | if ($logscale) { 37 | $logrange=10; 38 | $value = ($logrange*$value)/$max; 39 | 40 | $value = log(1+$value); 41 | $max = log(1+$logrange); 42 | } 43 | $h = (int)floor((80*$value)/$max); 44 | return "$h%"; 45 | } 46 | 47 | function logGraph($id,$data,$conf = Array()) { 48 | $logscale = isset($conf['logscale']) ? $conf['logscale'] : 0; 49 | $width = isset($conf['width']) ? $conf['width'] : 600; 50 | $height = isset($conf['height']) ? $conf['height'] : 140; 51 | # Compute some value 52 | $slots = max(count($data),12); 53 | $w = floor((($slots > 15) ? 90 : 80)/$slots); 54 | $lstep = floor(90/$slots); 55 | # Our graph lives inside a main div having the given id 56 | $html = '
'; 58 | # Output the div for the title if any 59 | if (isset($conf['title'])) { 60 | $html .= '
'.utf8entities($conf['title'])."
\n"; 61 | } 62 | $max = logGraphMaxValue($data); 63 | # Output the div for the max line 64 | if ($max > 0) { 65 | $h = logGraphBarHeight($logscale,$max,$max); 66 | $html .= '
'.$max."
\n"; 67 | } 68 | # Output a div for every bar 69 | $pos = 0; 70 | for($i = 0; $i < $slots-count($data); $i++) { 71 | $l = $pos*$lstep; 72 | $html .= '
'."\n"; 73 | $pos++; 74 | } 75 | $tot = 0; 76 | for($i = 0; $i < count($data); $i++) { 77 | $tot += $data[$i]['val']; 78 | $h = logGraphBarHeight($logscale,$max,$data[$i]['val']); 79 | $l = $pos*$lstep; 80 | if ($i==count($data)-1) { 81 | $class = "bar lastbar"; 82 | } else if ($data[$i]['lab'] == 'S') { 83 | $class = "bar webar"; 84 | } else { 85 | $class = "bar stdbar"; 86 | } 87 | $tip = isset($data[$i]['tip']) ? $data[$i]['tip'] : $data[$i]['val']; 88 | $html .= '
'.($data[$i]['lab']).'
'."\n"; 89 | $pos++; 90 | } 91 | $avg = count($data) ? $tot/count($data) : 0; 92 | if ($avg > 10) { 93 | $avg = floor($avg); 94 | } else { 95 | $avg = round($avg,2); 96 | } 97 | # Output the div for the average line 98 | if ($avg > 0 && count($data) > 1) { 99 | if ($logscale) { 100 | if ($max < 10) $gridstep = .01; 101 | else if ($max < 100) $gridstep = 1; 102 | else if ($max < 5000) $gridstep = 100; 103 | else if ($max < 10000) $gridstep = 1000; 104 | else if ($max < 100000) $gridstep = 10000; 105 | if ($gridstep) { 106 | $step = 0; 107 | $lasth = "0%"; 108 | while(1) { 109 | $step += $gridstep; 110 | if ($step > $max) break; 111 | $h = logGraphBarHeight($logscale,$max,$step); 112 | $hmax = logGraphBarHeight($logscale,$max,$max); 113 | if (trim($h,"%")-trim($lasth,"%") > 10) { 114 | if (trim($h,"%") < 70) $html .= '
'.round($step,2)."
\n"; 115 | $lasth = $h; 116 | } 117 | } 118 | } 119 | } else { 120 | $h = logGraphBarHeight($logscale,$max,$avg); 121 | $hmax = logGraphBarHeight($logscale,$max,$max); 122 | if (trim($hmax,"%")/trim($h,"%") <= 1.15) $avg=""; 123 | else $avg = "avg ".$avg; 124 | $html .= '
'.$avg."
\n"; 125 | } 126 | } 127 | # Done, almost. 128 | $html .= '
'; 129 | return $html; 130 | } 131 | ?> 132 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | LLOOGG realtime log analyzer web app 2 | === 3 | 4 | LLOGG was web service I (Salvatore Sanfilippo) and my co-founder Fabio Pitrola ran for seven years for free. It started as a side project while we were doing different things for our company: we wrote the code in a matter of a few days, and tried to put it online to see the reactions. 5 | 6 | The site concept was simple but very addictive: LLOOGG displays the accesses on your web site as they happen, in a pretty raw format. It was one of the first services of this type ever when we created it back in 2007. Later we modified it in order to track Adsense clicks (a feature that was later removed and is not available in this source release). Basically it was very instructive to see what users did in your web site, it makes you able to capture patterns that are hard to capture with aggregated data as processed by Google Analytics. 7 | 8 | If you want to see the service running, use demo/demo as username and password at http://demo.lloogg.com. The website does not allow users to register, it is just a copy we ran for a few friends. 9 | 10 | Why we closed 11 | --- 12 | 13 | After the code was put online and the users started to jump and report that they were totally addicted to the service, unfortunately we were already doing other stuff. Fabio was doing new things and moved to Barcelona, while I was writing Redis, exactly because LLOOGG was not scalable using MySQL in our experience, and this stimulated me to think at a different kind of database, that I later applied to LLOOGG with good success. 14 | 15 | So long story short, if not for the port from MySQL to Redis and other little things, the service remained running without changes for years. 16 | 17 | While LLOOGG is a very lightweight PHP/Redis application, that was able to process 2 billions of pageviews from the connected sites over 5 years since the migration to Redis in a single cheap virtual machine, still the cost of running it, that was about 150 dollars per month, after many years started to annoy me and Fabio, so we discontinued the service. 18 | 19 | However there are still users that wanted to use LLOOGG, so even if the **code is completely embarassing** we decided to release it. 20 | 21 | But it served as Redis test bed... 22 | --- 23 | 24 | Yep, in theory at least. The LLOOGG web site was the only one where I was applying Redis directly to have a considerable traffic (350-400 commands per second when it was shut down), so I was running always Redis unstable releases in LLOOGG in order to catch bugs before to put releases in production for other users. 25 | 26 | The reality is that not a single Redis bug was found using LLOOGG, it ran always like oil, not a crash, not a latency issue, nothing, so there was no real benefit about this. At this point the majority of issues in Redis are discovered by users using Redis at a much bigger scale. 27 | 28 | Why the code is so bad? 29 | --- 30 | 31 | There are a couple of reasons: 32 | 33 | * The code was written in the spare time in a very casual way. 34 | * It started as a fork of another web site developed in back to early 2000 with terrible coding practices. 35 | * It was ported from MySQL to Redis with the logic of "minimal changes". 36 | 37 | Part of the lameness of the code is that Redis was very limited when the port was performed, there were no hashes for example. The code uses Redis in a very suboptimal way. The client library was very raw as well, so a connection to Redis is created at every page view. 38 | 39 | However there are good thing about this code as well. The internals and the Javascript are pretty ugly but they managed to run for 7 years seeing generations of new browsers and mobile devices without any incompatibility. It is also very simple in the design, so while a mess, we are confident that good programmers will be able to modify it in a matter of minutes. 40 | 41 | Before the release I removed tens of useless functions I found inside the code in the hope, at least, to avoid confusing the reader with total garbage. 42 | 43 | How to install LLOOGG? 44 | --- 45 | 46 | LLOOGG is a pure PHP/Redis application without dependencies AFAIK. Any old version of PHP will do, probably even PHP 5.3. To install it: 47 | 48 | * Unpack the code into your web root. 49 | * Setup a Redis server. 50 | * Copy `localconfig.php.example` to `localconfig.php` 51 | * Edit `localconfig.php` and put the Redis host and port. 52 | * Edit `config.php` and set your domain name and the username of the admin user. 53 | * Edit `l.js` and go at the end of the file. Where you see `img.src='http://lloogg.com/recv.php'+args;` replace `lloogg.com` with your domain name. 54 | 55 | How to use LLOOGG? 56 | --- 57 | 58 | Just visit the web site root, create an account, get the Javascript tag and put it in your web site. You should see visits in real time appearing in LLOOGG. 59 | 60 | The app still distinguish between PRO and normal accounts, since our idea was to adopt a Freemium business model. When you log with the admin account (as setup in `config.php`) you can set PRO accounts. 61 | 62 | PRO accounts have a longer persistent history. Also the app has a feature that let you gain read-only access to other accounts. PRO accounts can monitor multiple other websites while free accounts can monitor a single one. 63 | 64 | Why GPL v3? 65 | --- 66 | 67 | I don't like the GPL license normally, but this time we wanted to see changes merged back into the code because of the kind of service LLOOGG is and because of the reasons that ported to its source code open source release. 68 | -------------------------------------------------------------------------------- /trendsframe.php: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Trends 13 | 14 | 15 | 23 |
24 |

Control

25 | refresh graphs
26 |
41 | /> Logarithmic 42 |
43 | $t, 77 | "visitors" => $r->get("day:uv:$uid:$t"), 78 | "pageviews" => $r->get("day:pv:$uid:$t"), 79 | "retvisitors" => $r->get("day:rv:$uid:$t") 80 | ); 81 | if ($row['visitors'] === null) { 82 | $nullrecord++; 83 | if ($nullrecord == 5) break; 84 | } else { 85 | $nullrecord = 0; 86 | } 87 | $t -= 3600*24; 88 | $rows[] = $row; 89 | } 90 | while(count($rows) && ($rows[count($rows)-1]['pageviews']) == null) 91 | array_pop($rows); 92 | $numrows = count($rows); 93 | $graph1=Array("title"=>"Unique visitors","logscale"=>$logscale); 94 | $graph2=Array("title"=>"Pageviews","logscale"=>$logscale); 95 | $graph3=Array("title"=>"Returning visitors (percentage)","logscale"=>$logscale); 96 | $graph4=Array("title"=>"Pageviews per visitor","logscale"=>$logscale); 97 | $data1 = $data2 = $data3 = $data4 = Array(); 98 | $vtot = 0; 99 | $ptot = 0; 100 | $rtot = 0; 101 | $label = ""; 102 | $lastrow = false; 103 | 104 | while(count($rows)) { 105 | # lookahead of one row 106 | $row = array_shift($rows); 107 | if (count($rows)) { 108 | $nextrow = $rows[0]; 109 | $lastrow = false; 110 | } else { 111 | $lastrow = true; 112 | } 113 | 114 | $monthname = gmstrftime("%b",$row['basetime']); 115 | $weekname = gmstrftime("%a",$row['basetime']); 116 | $monthday = gmstrftime("%d",$row['basetime']); 117 | $year = gmstrftime("%y",$row['basetime']); 118 | 119 | if ($unit == "day") { 120 | $label = $weekname[0]; 121 | $vtot = $row['visitors']; 122 | $ptot = $row['pageviews']; 123 | $rtot = $row['retvisitors']; 124 | } else if ($unit == "week") { 125 | if (strtolower($weekname) == "sun") { 126 | $vtot = $ptot = $rtot = 0; 127 | } 128 | $vtot += $row['visitors']; 129 | $ptot += $row['pageviews']; 130 | $rtot += $row['retvisitors']; 131 | if (strtolower($weekname) != "mon" && !$lastrow) continue; 132 | $label = $monthname[0]." ".$monthday; 133 | } else if ($unit == "month") { 134 | if (!$lastrow) $nextmonthname = gmstrftime("%b",$nextrow['basetime']); 135 | if (isset($resetnext)) { 136 | $vtot = $ptot = $rtot = 0; 137 | unset($resetnext); 138 | } 139 | $vtot += $row['visitors']; 140 | $ptot += $row['pageviews']; 141 | $rtot += $row['retvisitors']; 142 | if (!$lastrow && $nextmonthname == $monthname) continue; 143 | $resetnext=true; 144 | $label = $monthname[0].$monthname[1]; 145 | } else if ($unit == "year") { 146 | if (!$lastrow) $nextyear = gmstrftime("%y",$nextrow['basetime']); 147 | if (isset($resetnext)) { 148 | $vtot = $ptot = $rtot = 0; 149 | unset($resetnext); 150 | } 151 | $vtot += $row['visitors']; 152 | $ptot += $row['pageviews']; 153 | $rtot += $row['retvisitors']; 154 | if (!$lastrow && $nextyear == $year) continue; 155 | $resetnext=true; 156 | $label = $year; 157 | } 158 | $data1[] = Array("val"=>$vtot,"lab"=>$label); 159 | $data2[] = Array("val"=>$ptot,"lab"=>$label); 160 | if ($vtot == 0) 161 | $perc = 0; 162 | else 163 | $perc = round(($rtot*100)/$vtot,2); 164 | $tip = $perc."% ($rtot visitors)"; 165 | $data3[] = Array("val"=>$perc,"lab"=>$label,"tip"=>$tip); 166 | if ($vtot) 167 | $pvpervisitor = round((float)$ptot/$vtot,2); 168 | else 169 | $pvpervisitor = 0; 170 | $data4[] = Array("val"=>$pvpervisitor,"lab"=>$label); 171 | if ($lastrow) break; 172 | } 173 | $data1 = array_reverse($data1); 174 | $data2 = array_reverse($data2); 175 | $data3 = array_reverse($data3); 176 | $data4 = array_reverse($data4); 177 | 178 | echo(logGraph("uv_graph",$data1,$graph1)); 179 | echo(logGraph("pv_graph",$data2,$graph2)); 180 | echo(logGraph("rv_graph",$data3,$graph3)); 181 | echo(logGraph("pvu_graph",$data4,$graph4)); 182 | 183 | ?> 184 | 187 | 188 | 189 | -------------------------------------------------------------------------------- /css/iphone_style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: verdana; 3 | } 4 | 5 | #header { 6 | padding-top:5px; 7 | border-bottom: 1px #bbbbbb solid; 8 | } 9 | 10 | #main { 11 | position:relative; 12 | min-height: 400px; 13 | } 14 | 15 | #footer { 16 | margin-top:10px; 17 | clear:both; 18 | border-top: 1px #bbbbbb solid; 19 | text-align: center; 20 | } 21 | 22 | #logsdiv { 23 | width:100%; 24 | float:left; 25 | } 26 | 27 | #statsdiv { 28 | position:absolute; 29 | width: 30%; 30 | border-left: 1px #bbbbbb solid; 31 | min-height: 250px; 32 | padding-left:3px; 33 | top:70px; 34 | right:0px; 35 | background-color:white; 36 | display:none; 37 | visibility:hidden; 38 | } 39 | 40 | #statsdiv H5 { 41 | margin:0px; 42 | padding:0px; 43 | } 44 | 45 | .fineprint { 46 | padding-bottom: 10px; 47 | font-size: 10px; 48 | color: #999999; 49 | } 50 | 51 | .inlinelogo { 52 | padding-left: 20px; 53 | background: url(/images/lloogg16.gif) 0 0 no-repeat; 54 | } 55 | 56 | #briciola { 57 | font-size: 12px; 58 | margin-bottom: 15px; 59 | } 60 | 61 | #briciola a { 62 | color: #ff0000; 63 | } 64 | 65 | a { 66 | text-decoration: none; 67 | color: #dd2222; 68 | font-weight: bold; 69 | } 70 | 71 | a:hover { 72 | text-decoration: underline; 73 | } 74 | 75 | .inputtext { 76 | border:1px inset #ddd; 77 | } 78 | 79 | .inputbutton { 80 | border:1px solid #aaa; 81 | } 82 | 83 | p { 84 | text-align: justify; 85 | width: 700px; 86 | } 87 | 88 | #invite { 89 | font-size: 12px; 90 | position: absolute; 91 | top: 25px; 92 | right: 8px; 93 | } 94 | 95 | #invite A { 96 | font-size: 12px; 97 | } 98 | 99 | #toplinks { 100 | position: absolute; 101 | top: 25px; 102 | left: 230px; 103 | } 104 | 105 | #toplinks A { 106 | font-weight: normal; 107 | } 108 | 109 | #slogan { 110 | font-size: 10px; 111 | color: #666666; 112 | position: absolute; 113 | top: 0px; 114 | left: 10px; 115 | } 116 | 117 | #uimain { 118 | position:relative; 119 | padding-top: 15px; 120 | } 121 | 122 | #trendbox { 123 | position:absolute; 124 | right:0px; 125 | top:0px; 126 | font-size: 12px; 127 | padding-right:10px; 128 | background-color:white; 129 | } 130 | 131 | #trendbox H3 { 132 | margin:0px; 133 | font-size: 14px; 134 | font-weight:bold; 135 | } 136 | 137 | #trendbox A { 138 | font-size: 12px; 139 | font-weight: normal; 140 | } 141 | 142 | #usernav { 143 | background-color: #eeeeee; 144 | padding-top: 0px; 145 | padding-bottom: 2px; 146 | font-size: 12px; 147 | } 148 | 149 | .username { 150 | font-weight: bold; 151 | } 152 | 153 | #usernav strong { 154 | color: white; 155 | background-color: #dd2222; 156 | } 157 | 158 | #usernav strong, #usernav A { 159 | padding-left: 4px; 160 | padding-right: 4px; 161 | } 162 | 163 | .loginnerdiv { 164 | color: #444444; 165 | font-size: 14px; 166 | border-bottom: 2px solid #aaa; 167 | padding-bottom: 4px; 168 | overflow: hidden; 169 | background-color: white; 170 | } 171 | .ipaddr A { 172 | color: #666666; 173 | } 174 | .screenres { 175 | font-size: 10px; 176 | color: #f55000; 177 | } 178 | .referer A { 179 | color: #5000f5; 180 | font-size: 14px; 181 | font-weight: normal; 182 | } 183 | .useragent { 184 | color: #444444; 185 | font-size: 14px; 186 | } 187 | .ipcolorbox { 188 | border: 1px #aaaaaa solid; 189 | padding:0px; 190 | margin: 0px; 191 | margin-right: 5px; 192 | width: 18px; 193 | height: 18px; 194 | } 195 | .date { 196 | color: #444444; 197 | font-size: 14px; 198 | } 199 | .historyinfo { 200 | color: #444444; 201 | font-size: 14px; 202 | } 203 | .country { 204 | font-size:14px; 205 | font-weight:bold; 206 | color:#33aa33; 207 | } 208 | .browser { 209 | font-weight: bold; 210 | } 211 | .os { 212 | font-weight: bold; 213 | } 214 | .favicon { 215 | vertical-align: middle; 216 | padding:2px; 217 | padding-right: 0px; 218 | padding-left: 0px; 219 | } 220 | .statbox { 221 | padding: 2px; 222 | border: 1px #aaaaaa solid; 223 | font-family: monospace; 224 | font-size:14px; 225 | margin-bottom: 10px; 226 | overflow:hidden; 227 | } 228 | .statbox A { 229 | font-weight: normal; 230 | } 231 | 232 | .statbox H5 { 233 | margin: 0px; 234 | color: white; 235 | background-color: #f55000; 236 | font-family:verdana; 237 | font-size: 14px; 238 | } 239 | 240 | UL.iphistory { 241 | margin:0px; 242 | padding-left:15px; 243 | } 244 | .iphistory LI { 245 | font-size: 12px; 246 | margin:0px; 247 | } 248 | .iphistory A { 249 | color: blue; 250 | font-weight: normal; 251 | text-decoration: none; 252 | } 253 | A.ctrlbut { 254 | border: 1px #f55000 solid; 255 | text-decoration: none; 256 | padding-left: 3px; 257 | padding-right: 3px; 258 | margin-left:6px; 259 | font-size:12px; 260 | } 261 | #trendmain { 262 | padding-top: 20px; 263 | } 264 | .graph { 265 | position: relative; 266 | border-bottom: 1px dotted #dddddd; 267 | padding:0px; 268 | padding-bottom: 0px; 269 | margin-bottom: 30px; 270 | } 271 | .graphtitle { 272 | position: absolute; 273 | left:5px; 274 | top:0px; 275 | padding:2px; 276 | color:#666666; 277 | font-family: "Trebuchet MS",Verdana; 278 | font-size:16px; 279 | font-weight:bold; 280 | color:999999; 281 | } 282 | .bar { 283 | position: absolute; 284 | border: 1px white solid; 285 | border-bottom: none; 286 | font-size:10px; 287 | text-align:center; 288 | color:white; 289 | cursor:pointer; 290 | z-index:1; 291 | } 292 | .stdbar { 293 | background-color: #9ad281; 294 | border-top: 1px #bfd7b4 solid; 295 | border-left: 1px #bfd7b4 solid; 296 | border-right: 1px #83d55e solid; 297 | } 298 | .webar { 299 | background-color: #8ac271; 300 | border-top: 1px #60a741 solid; 301 | border-left: 1px #60a741 solid; 302 | border-right: 1px #60a741 solid; 303 | } 304 | .lastbar { 305 | background-color: #60a741; 306 | border-top: 1px #bfd7b4 solid; 307 | border-left: 1px #bfd7b4 solid; 308 | border-right: 1px #83d55e solid; 309 | } 310 | .barmax { 311 | text-align: right; 312 | position: absolute; 313 | border-bottom: 1px #dddddd dotted; 314 | width: 100%; 315 | height: 15px; 316 | font-size: 12px; 317 | color: #666666; 318 | margin:1px; 319 | } 320 | .baravg { 321 | text-align: right; 322 | position: absolute; 323 | border-bottom: 1px #ddddff dotted; 324 | width: 100%; 325 | height: 15px; 326 | font-size: 10px; 327 | color: #666666; 328 | margin:0px; 329 | } 330 | .clicktip { 331 | background-color: white; 332 | border: 1px black solid; 333 | font-family: verdana; 334 | font-size: 10px; 335 | padding-left: 3px; 336 | padding-right: 3px; 337 | max-width: 300px; 338 | z-index:2; 339 | } 340 | #trendsctrl { 341 | font-size:12px; 342 | padding: 0px; 343 | padding-bottom:5px; 344 | margin-top:10px; 345 | border: 2px #9ad281 solid; 346 | position: absolute; 347 | left:650px; 348 | top:0px; 349 | } 350 | #trendsctrl SELECT { 351 | border: 1px #aaaaaa solid; 352 | margin:5px; 353 | } 354 | #trendsctrl A { 355 | font-weight: normal; 356 | padding:5px; 357 | } 358 | #trendsctrl H2 { 359 | margin:0px; 360 | font-size: 14px; 361 | font-weight: bold; 362 | color: white; 363 | background-color: #60a741; 364 | } 365 | #screencast { 366 | border: 1px #aaaaaa dotted; 367 | width: 740px; 368 | } 369 | #changelog { 370 | position: absolute; 371 | right:5px; 372 | top:0px; 373 | padding:1px; 374 | color:red; 375 | font-weight:bold; 376 | font-size:10px; 377 | } 378 | #changelog A { 379 | color:blue; 380 | } 381 | #adclick { 382 | font-size:14px; 383 | font-weight:bold; 384 | padding:5px; 385 | margin-top:4px; 386 | margin-bottom:4px; 387 | } 388 | #featuresdisclaimer { 389 | width:500px; 390 | padding:10px; 391 | border: 1px red dotted; 392 | font-size:10px; 393 | } 394 | .progressbar { 395 | padding:3px; 396 | background-color:red; 397 | color:white; 398 | font-size: 12px; 399 | font-weight: bold; 400 | z-index: 100; 401 | } 402 | #filters { 403 | margin-top:10px; 404 | margin-bottom:10px; 405 | padding:5px; 406 | border: 1px #909090 solid; 407 | background-color: #ffffc0; 408 | width:700px; 409 | font-size:12px; 410 | font-weight:bold; 411 | } 412 | .notabene { 413 | font-size:12px; 414 | font-weight:normal; 415 | border: 1px solid #f0f0f0; 416 | background-color: #ffffee; 417 | padding:3px; 418 | margin:2px; 419 | padding-left:18px; 420 | background:#ffffee url(/images/lbulb.gif) 0px 3px no-repeat; 421 | } 422 | -------------------------------------------------------------------------------- /css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: verdana; 3 | } 4 | 5 | #header { 6 | padding-top:5px; 7 | border-bottom: 1px #bbbbbb solid; 8 | } 9 | 10 | #main { 11 | position:relative; 12 | min-height: 400px; 13 | } 14 | 15 | #footer { 16 | margin-top:10px; 17 | clear:both; 18 | border-top: 1px #bbbbbb solid; 19 | text-align: center; 20 | } 21 | 22 | #logsdiv { 23 | width: 68%; 24 | float:left; 25 | } 26 | 27 | #statsdiv { 28 | position:absolute; 29 | width: 30%; 30 | border-left: 1px #bbbbbb solid; 31 | min-height: 250px; 32 | padding-left:3px; 33 | top:70px; 34 | right:0px; 35 | background-color:white; 36 | } 37 | 38 | #statsdiv H5 { 39 | margin:0px; 40 | padding:0px; 41 | } 42 | 43 | .fineprint { 44 | padding-bottom: 10px; 45 | font-size: 10px; 46 | color: #999999; 47 | } 48 | 49 | .inlinelogo { 50 | padding-left: 20px; 51 | background: url(/images/lloogg16.gif) 0 0 no-repeat; 52 | } 53 | 54 | #briciola { 55 | font-size: 12px; 56 | margin-bottom: 15px; 57 | } 58 | 59 | #briciola a { 60 | color: #ff0000; 61 | } 62 | 63 | a { 64 | text-decoration: none; 65 | color: #dd2222; 66 | font-weight: bold; 67 | } 68 | 69 | a:hover { 70 | text-decoration: underline; 71 | } 72 | 73 | .inputtext { 74 | border:1px inset #ddd; 75 | } 76 | 77 | .inputbutton { 78 | border:1px solid #aaa; 79 | } 80 | 81 | p { 82 | text-align: justify; 83 | width: 700px; 84 | } 85 | 86 | #invite { 87 | font-size: 12px; 88 | position: absolute; 89 | top: 25px; 90 | right: 8px; 91 | } 92 | 93 | #invite A { 94 | font-size: 12px; 95 | } 96 | 97 | #toplinks { 98 | position: absolute; 99 | top: 25px; 100 | left: 230px; 101 | } 102 | 103 | #toplinks A { 104 | font-weight: normal; 105 | } 106 | 107 | #slogan { 108 | font-size: 10px; 109 | color: #666666; 110 | position: absolute; 111 | top: 0px; 112 | left: 10px; 113 | } 114 | 115 | #uimain { 116 | position:relative; 117 | padding-top: 15px; 118 | } 119 | 120 | #trendbox { 121 | position:absolute; 122 | right:0px; 123 | top:0px; 124 | font-size: 12px; 125 | padding-right:10px; 126 | background-color:white; 127 | } 128 | 129 | #trendbox H3 { 130 | margin:0px; 131 | font-size: 14px; 132 | font-weight:bold; 133 | } 134 | 135 | #trendbox A { 136 | font-size: 12px; 137 | font-weight: normal; 138 | } 139 | 140 | #usernav { 141 | background-color: #eeeeee; 142 | padding-top: 0px; 143 | padding-bottom: 2px; 144 | font-size: 12px; 145 | } 146 | 147 | .username { 148 | font-weight: bold; 149 | } 150 | 151 | #usernav strong { 152 | color: white; 153 | background-color: #dd2222; 154 | } 155 | 156 | #usernav strong, #usernav A { 157 | padding-left: 4px; 158 | padding-right: 4px; 159 | } 160 | 161 | .loginnerdiv { 162 | color: #444444; 163 | font-size: 12px; 164 | border-bottom: 1px solid #cccccc; 165 | padding-bottom: 4px; 166 | overflow: hidden; 167 | background-color: white; 168 | } 169 | .ipaddr A { 170 | color: #666666; 171 | } 172 | .screenres { 173 | font-size: 10px; 174 | color: #f55000; 175 | } 176 | .referer A { 177 | color: #5000f5; 178 | font-size: 12px; 179 | font-weight: normal; 180 | } 181 | .useragent { 182 | color: #444444; 183 | font-size: 12px; 184 | } 185 | .ipcolorbox { 186 | border: 1px #aaaaaa solid; 187 | padding:0px; 188 | margin: 0px; 189 | margin-right: 5px; 190 | width: 18px; 191 | height: 18px; 192 | } 193 | .date { 194 | color: #444444; 195 | font-size: 12px; 196 | } 197 | .historyinfo { 198 | color: #444444; 199 | font-size: 12px; 200 | } 201 | .country { 202 | font-size:12px; 203 | font-weight:bold; 204 | color:#33aa33; 205 | } 206 | .browser { 207 | font-weight: bold; 208 | } 209 | .os { 210 | font-weight: bold; 211 | } 212 | .favicon { 213 | vertical-align: middle; 214 | padding:2px; 215 | padding-right: 0px; 216 | padding-left: 0px; 217 | } 218 | .statbox { 219 | padding: 2px; 220 | border: 1px #aaaaaa solid; 221 | font-family: monospace; 222 | font-size:12px; 223 | margin-bottom: 10px; 224 | overflow:hidden; 225 | } 226 | .statbox A { 227 | font-weight: normal; 228 | } 229 | 230 | .statbox H5 { 231 | margin: 0px; 232 | color: white; 233 | background-color: #f55000; 234 | font-family:verdana; 235 | font-size: 12px; 236 | } 237 | 238 | UL.iphistory { 239 | margin:0px; 240 | padding-left:15px; 241 | } 242 | .iphistory LI { 243 | font-size: 12px; 244 | margin:0px; 245 | } 246 | .iphistory A { 247 | color: blue; 248 | font-weight: normal; 249 | text-decoration: none; 250 | } 251 | A.ctrlbut { 252 | border: 1px #f55000 solid; 253 | text-decoration: none; 254 | padding-left: 3px; 255 | padding-right: 3px; 256 | margin-left:6px; 257 | font-size:12px; 258 | } 259 | #trendmain { 260 | padding-top: 20px; 261 | } 262 | .graph { 263 | position: relative; 264 | border-bottom: 1px dotted #dddddd; 265 | padding:0px; 266 | padding-bottom: 0px; 267 | margin-bottom: 30px; 268 | } 269 | .graphtitle { 270 | position: absolute; 271 | left:5px; 272 | top:0px; 273 | padding:2px; 274 | color:#666666; 275 | font-family: "Trebuchet MS",Verdana; 276 | font-size:16px; 277 | font-weight:bold; 278 | color:999999; 279 | } 280 | .bar { 281 | position: absolute; 282 | border: 1px white solid; 283 | border-bottom: none; 284 | font-size:10px; 285 | text-align:center; 286 | color:white; 287 | cursor:pointer; 288 | z-index:1; 289 | } 290 | .stdbar { 291 | background-color: #9ad281; 292 | border-top: 1px #bfd7b4 solid; 293 | border-left: 1px #bfd7b4 solid; 294 | border-right: 1px #83d55e solid; 295 | } 296 | .webar { 297 | background-color: #8ac271; 298 | border-top: 1px #60a741 solid; 299 | border-left: 1px #60a741 solid; 300 | border-right: 1px #60a741 solid; 301 | } 302 | .lastbar { 303 | background-color: #60a741; 304 | border-top: 1px #bfd7b4 solid; 305 | border-left: 1px #bfd7b4 solid; 306 | border-right: 1px #83d55e solid; 307 | } 308 | .barmax { 309 | text-align: right; 310 | position: absolute; 311 | border-bottom: 1px #dddddd dotted; 312 | width: 100%; 313 | height: 15px; 314 | font-size: 12px; 315 | color: #666666; 316 | margin:1px; 317 | } 318 | .baravg { 319 | text-align: right; 320 | position: absolute; 321 | border-bottom: 1px #ddddff dotted; 322 | width: 100%; 323 | height: 15px; 324 | font-size: 10px; 325 | color: #666666; 326 | margin:0px; 327 | } 328 | .clicktip { 329 | background-color: white; 330 | border: 1px black solid; 331 | font-family: verdana; 332 | font-size: 10px; 333 | padding-left: 3px; 334 | padding-right: 3px; 335 | max-width: 300px; 336 | z-index:2; 337 | } 338 | #trendsctrl { 339 | font-size:12px; 340 | padding: 0px; 341 | padding-bottom:5px; 342 | margin-top:10px; 343 | border: 2px #9ad281 solid; 344 | position: absolute; 345 | left:650px; 346 | top:0px; 347 | } 348 | #trendsctrl SELECT { 349 | border: 1px #aaaaaa solid; 350 | margin:5px; 351 | } 352 | #trendsctrl A { 353 | font-weight: normal; 354 | padding:5px; 355 | } 356 | #trendsctrl H2 { 357 | margin:0px; 358 | font-size: 14px; 359 | font-weight: bold; 360 | color: white; 361 | background-color: #60a741; 362 | } 363 | #screencast { 364 | border: 1px #aaaaaa dotted; 365 | width: 740px; 366 | } 367 | #changelog { 368 | position: absolute; 369 | right:5px; 370 | top:0px; 371 | padding:1px; 372 | color:red; 373 | font-weight:bold; 374 | font-size:10px; 375 | } 376 | #changelog A { 377 | color:blue; 378 | } 379 | #adclick { 380 | font-size:14px; 381 | font-weight:bold; 382 | padding:5px; 383 | margin-top:4px; 384 | margin-bottom:4px; 385 | } 386 | #featuresdisclaimer { 387 | width:500px; 388 | padding:10px; 389 | border: 1px red dotted; 390 | font-size:10px; 391 | } 392 | .progressbar { 393 | padding:3px; 394 | background-color:red; 395 | color:white; 396 | font-size: 12px; 397 | font-weight: bold; 398 | z-index: 100; 399 | } 400 | #filters { 401 | margin-top:10px; 402 | margin-bottom:10px; 403 | padding:5px; 404 | border: 1px #909090 solid; 405 | background-color: #ffffc0; 406 | width:700px; 407 | font-size:12px; 408 | font-weight:bold; 409 | } 410 | .notabene { 411 | font-size:12px; 412 | font-weight:normal; 413 | border: 1px solid #f0f0f0; 414 | background-color: #ffffee; 415 | padding:3px; 416 | margin:2px; 417 | padding-left:18px; 418 | background:#ffffee url(/images/lbulb.gif) 0px 3px no-repeat; 419 | } 420 | .trylater { 421 | background-color:white; 422 | border:1px black solid; 423 | width:50%; 424 | height:50%; 425 | top:25%; 426 | left:25%; 427 | position:absolute; 428 | padding:15px; 429 | } 430 | -------------------------------------------------------------------------------- /display.php: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 15 | 'voti OK', 18 | "no" => 'voti NO', 19 | "news" => 'nuove notizie', 20 | "comment" => 'commenti', 21 | "user" => 'nuovi utenti', 22 | "karma" => 'karma'); 23 | function noiseTypeSwitches() { 24 | global $_spyevents, $_spyeventstext; 25 | foreach($_spyevents as $e) { 26 | echo(''); 30 | } 31 | } 32 | 33 | function noiseJsEvents() { 34 | global $_spyevents; 35 | echo('\n"); 41 | } 42 | 43 | function noiseJsCategories() { 44 | echo('\n"); 51 | } 52 | 53 | function noiseCategorySelect() { 54 | $cats = dbListCategories(); 55 | echo("
'); 27 | echo(''); 28 | echo('
Visualizzazione '.$_spyeventstext[$e].''); 29 | echo('
"); 56 | for ($i = 0; $i < count($cats); $i++) { 57 | echo(' "); 58 | if ($i == 4) echo(""); 59 | } 60 | echo("
'.htmlentities($cats[$i]['name'])."
"); 61 | } 62 | 63 | noiseTypeSwitches(); 64 | ?> 65 | 74 | configurazione avanzata 75 | 76 | 77 | 78 | 79 | 109 | 110 | 112 | 113 |
114 |
115 | 320 |
321 | 322 | -------------------------------------------------------------------------------- /redis.php: -------------------------------------------------------------------------------- 1 | host = $host; 24 | $this->port = $port; 25 | } 26 | 27 | public function connect() { 28 | if ($this->_sock) return; 29 | if ($sock = fsockopen($this->host, $this->port, $errno, $errstr)) { 30 | $this->_sock = $sock; 31 | return; 32 | } 33 | $msg = "Cannot open socket to {$this->host}:{$this->port}"; 34 | if ($errno || $errmsg) 35 | $msg .= "," . ($errno ? " error $errno" : "") . 36 | ($errmsg ? " $errmsg" : ""); 37 | trigger_error("$msg.", E_USER_ERROR); 38 | } 39 | 40 | public function disconnect() { 41 | if ($this->_sock) @fclose($this->_sock); 42 | $this->_sock = null; 43 | } 44 | 45 | public function ping() { 46 | $this->connect(); 47 | $this->write_cmd("PING"); 48 | return $this->get_response(); 49 | } 50 | 51 | public function do_echo($s) { 52 | $this->connect(); 53 | $this->write_cmd("ECHO",$s); 54 | return $this->get_response(); 55 | } 56 | 57 | public function set($name, $value, $preserve=false) { 58 | $this->connect(); 59 | $this->write_cmd(($preserve ? 'SETNX' : 'SET'),$name,$value); 60 | return $this->get_response(); 61 | } 62 | 63 | public function get($name) { 64 | $this->connect(); 65 | $this->write_cmd("GET",$name); 66 | return $this->get_response(); 67 | } 68 | 69 | public function mget($keys) { 70 | $this->connect(); 71 | $this->write_cmd("MGET",$keys); 72 | return $this->get_response(); 73 | } 74 | 75 | public function incr($name, $amount=1) { 76 | $this->connect(); 77 | if ($amount == 1) 78 | $this->write_cmd("INCR",$name); 79 | else 80 | $this->write_cmd("INCRBY",$name,$amount); 81 | return $this->get_response(); 82 | } 83 | 84 | public function decr($name, $amount=1) { 85 | $this->connect(); 86 | if ($amount == 1) 87 | $this->write_cmd("DECR",$name); 88 | else 89 | $this->write_cmd("DECRBY",$name,$amount); 90 | return $this->get_response(); 91 | } 92 | 93 | public function exists($name) { 94 | $this->connect(); 95 | $this->write_cmd("EXISTS",$name); 96 | return $this->get_response(); 97 | } 98 | 99 | public function delete($name) { 100 | $this->connect(); 101 | $this->write_cmd("DEL",$name); 102 | return $this->get_response(); 103 | } 104 | 105 | public function keys($pattern) { 106 | $this->connect(); 107 | $this->write_cmd("KEYS",$pattern); 108 | return explode(' ', $this->get_response()); 109 | } 110 | 111 | public function randomkey() { 112 | $this->connect(); 113 | $this->write_cmd("RANDOMKEY"); 114 | return $this->get_response(); 115 | } 116 | 117 | public function rename($src, $dst) { 118 | $this->connect(); 119 | $this->write_cmd("RENAME",$src,$dst); 120 | return $this->get_response(); 121 | } 122 | 123 | public function renamenx($src, $dst) { 124 | $this->connect(); 125 | $this->write_cmd("RENAMENX",$src,$dst); 126 | return $this->get_response(); 127 | } 128 | 129 | public function expire($name, $time) { 130 | $this->connect(); 131 | $this->write_cmd("EXPIRE",$name,$time); 132 | return $this->get_response(); 133 | } 134 | 135 | public function push($name, $value, $tail=true) { 136 | $this->connect(); 137 | $this->write_cmd(($tail ? 'RPUSH' : 'LPUSH'),$name,$value); 138 | return $this->get_response(); 139 | } 140 | 141 | public function lpush($name, $value) { 142 | return $this->push($name, $value, false); 143 | } 144 | 145 | public function rpush($name, $value) { 146 | return $this->push($name, $value, true); 147 | } 148 | 149 | public function ltrim($name, $start, $end) { 150 | $this->connect(); 151 | $this->write_cmd("LTRIM",$name,$start,$end); 152 | return $this->get_response(); 153 | } 154 | 155 | public function lindex($name, $index) { 156 | $this->connect(); 157 | $this->write_cmd("LINDEX",$name,$index); 158 | return $this->get_response(); 159 | } 160 | 161 | public function pop($name, $tail=true) { 162 | $this->connect(); 163 | $this->write_cmd(($tail ? 'RPOP' : 'LPOP'),$name); 164 | return $this->get_response(); 165 | } 166 | 167 | public function lpop($name, $value) { 168 | return $this->pop($name, $value, false); 169 | } 170 | 171 | public function rpop($name, $value) { 172 | return $this->pop($name, $value, true); 173 | } 174 | 175 | public function llen($name) { 176 | $this->connect(); 177 | $this->write_cmd("LLEN",$name); 178 | return $this->get_response(); 179 | } 180 | 181 | public function lrange($name, $start, $end) { 182 | $this->connect(); 183 | $this->write_cmd("LRANGE",$name,$start,$end); 184 | return $this->get_response(); 185 | } 186 | 187 | public function lset($name, $value, $index) { 188 | $this->connect(); 189 | $this->write_cmd("LSET",$name,$index,$value); 190 | return $this->get_response(); 191 | } 192 | 193 | public function sadd($name, $value) { 194 | $this->connect(); 195 | $this->write_cmd("SADD",$name,$value); 196 | return $this->get_response(); 197 | } 198 | 199 | public function srem($name, $value) { 200 | $this->connect(); 201 | $this->write_cmd("SREM",$name,$value); 202 | return $this->get_response(); 203 | } 204 | 205 | public function sismember($name, $value) { 206 | $this->connect(); 207 | $this->write_cmd("SISMEMBER",$name,$value); 208 | return $this->get_response(); 209 | } 210 | 211 | public function sinter($sets) { 212 | $this->connect(); 213 | $this->write_cmd("SINTER",$sets); 214 | return $this->get_response(); 215 | } 216 | 217 | public function smembers($name) { 218 | $this->connect(); 219 | $this->write_cmd("SMEMBERS",$name); 220 | return $this->get_response(); 221 | } 222 | 223 | public function scard($name) { 224 | $this->connect(); 225 | $this->write_cmd("SCARD",$name); 226 | return $this->get_response(); 227 | } 228 | 229 | public function zadd($name, $score, $value) { 230 | $this->connect(); 231 | $this->write_cmd("ZADD",$name,$score,$value); 232 | return $this->get_response(); 233 | } 234 | 235 | public function zincrby($name, $score, $value) { 236 | $this->connect(); 237 | $this->write_cmd("ZINCRBY",$name,$score,$value); 238 | return $this->get_response(); 239 | } 240 | 241 | public function zrem($name, $value) { 242 | $this->connect(); 243 | $this->write_cmd("ZREM",$name,$value); 244 | return $this->get_response(); 245 | } 246 | 247 | public function zscore($name, $value) { 248 | $this->connect(); 249 | $this->write_cmd("ZSCORE",$name,$value); 250 | return $this->get_response(); 251 | } 252 | 253 | public function zrange($name, $first, $last, $opt=false) { 254 | $this->connect(); 255 | $this->write_cmd("ZRANGE",$name,$first,$last,$opt); 256 | return $this->get_response(); 257 | } 258 | 259 | public function zrevrange($name, $first, $last, $opt=false) { 260 | $this->connect(); 261 | $this->write_cmd("ZREVRANGE",$name,$first,$last,$opt); 262 | return $this->get_response(); 263 | } 264 | 265 | public function zremrangebyscore($name, $min, $max) { 266 | $this->connect(); 267 | $this->write_cmd("ZREMRANGEBYSCORE",$name,$min,$max); 268 | return $this->get_response(); 269 | } 270 | 271 | public function select_db($name) { 272 | $this->connect(); 273 | $this->write_cmd("SELECT",$name); 274 | return $this->get_response(); 275 | } 276 | 277 | public function move($name, $db) { 278 | $this->connect(); 279 | $this->write_cmd("MOVE",$name,$db); 280 | return $this->get_response(); 281 | } 282 | 283 | public function info($raw=false) { 284 | $this->connect(); 285 | $this->write_cmd("INFO"); 286 | $info = array(); 287 | $data =& $this->get_response(); 288 | if ($raw) return $data; 289 | foreach (explode("\r\n", $data) as $l) { 290 | if (!$l) 291 | continue; 292 | list($k, $v) = explode(':', $l, 2); 293 | $_v = strpos($v, '.') !== false ? (float)$v : (int)$v; 294 | $info[$k] = (string)$_v == $v ? $_v : $v; 295 | } 296 | return $info; 297 | } 298 | 299 | private function write($s) { 300 | while ($s) { 301 | $i = fwrite($this->_sock, $s); 302 | if ($i == 0) // || $i == strlen($s)) 303 | break; 304 | $s = substr($s, $i); 305 | } 306 | } 307 | 308 | private function write_cmd() { 309 | $args = func_get_args(); 310 | $argv = Array(); 311 | foreach($args as $a) { 312 | if ($a === false) continue; 313 | if (is_array($a)) { 314 | foreach($a as $e) { 315 | if ($e === false) continue; 316 | $argv[] = $e; 317 | } 318 | } else { 319 | $argv[] = $a; 320 | } 321 | } 322 | $query = "*".count($argv)."\r\n"; 323 | foreach($argv as $a) { 324 | $query .= "$".strlen($a)."\r\n".$a."\r\n"; 325 | } 326 | $this->write($query); 327 | } 328 | 329 | private function read($len=1024) { 330 | if ($s = fgets($this->_sock)) 331 | return $s; 332 | $this->disconnect(); 333 | trigger_error("Cannot read from socket.", E_USER_ERROR); 334 | } 335 | 336 | private function get_response() { 337 | $data = trim($this->read()); 338 | $c = $data[0]; 339 | $data = substr($data, 1); 340 | switch ($c) { 341 | case '-': 342 | trigger_error($data, E_USER_ERROR); 343 | break; 344 | case '+': 345 | return $data; 346 | case ':': 347 | $i = strpos($data, '.') !== false ? (int)$data : (float)$data; 348 | if ((string)$i != $data) 349 | trigger_error("Cannot convert data '$c$data' to integer", E_USER_ERROR); 350 | return $i; 351 | case '$': 352 | return $this->get_bulk_reply($c . $data); 353 | case '*': 354 | $num = (int)$data; 355 | if ((string)$num != $data) 356 | trigger_error("Cannot convert multi-response header '$data' to integer", E_USER_ERROR); 357 | $result = array(); 358 | for ($i=0; $i<$num; $i++) 359 | $result[] =& $this->get_response(); 360 | return $result; 361 | default: 362 | trigger_error("Invalid reply type byte: '$c'"); 363 | } 364 | } 365 | 366 | private function get_bulk_reply($data=null) { 367 | if ($data === null) 368 | $data = trim($this->read()); 369 | if ($data == '$-1') 370 | return null; 371 | $c = $data[0]; 372 | $data = substr($data, 1); 373 | $bulklen = (int)$data; 374 | if ((string)$bulklen != $data) 375 | trigger_error("Cannot convert bulk read header '$c$data' to integer", E_USER_ERROR); 376 | if ($c != '$') 377 | trigger_error("Unkown response prefix for '$c$data'", E_USER_ERROR); 378 | $buffer = ''; 379 | while ($bulklen) { 380 | $data = fread($this->_sock,$bulklen); 381 | $bulklen -= strlen($data); 382 | $buffer .= $data; 383 | } 384 | $crlf = fread($this->_sock,2); 385 | return $buffer; 386 | } 387 | } 388 | 389 | ?> 390 | -------------------------------------------------------------------------------- /geoip.inc: -------------------------------------------------------------------------------- 1 | 0, "AP" => 1, "EU" => 2, "AD" => 3, "AE" => 4, "AF" => 5, 66 | "AG" => 6, "AI" => 7, "AL" => 8, "AM" => 9, "AN" => 10, "AO" => 11, 67 | "AQ" => 12, "AR" => 13, "AS" => 14, "AT" => 15, "AU" => 16, "AW" => 17, 68 | "AZ" => 18, "BA" => 19, "BB" => 20, "BD" => 21, "BE" => 22, "BF" => 23, 69 | "BG" => 24, "BH" => 25, "BI" => 26, "BJ" => 27, "BM" => 28, "BN" => 29, 70 | "BO" => 30, "BR" => 31, "BS" => 32, "BT" => 33, "BV" => 34, "BW" => 35, 71 | "BY" => 36, "BZ" => 37, "CA" => 38, "CC" => 39, "CD" => 40, "CF" => 41, 72 | "CG" => 42, "CH" => 43, "CI" => 44, "CK" => 45, "CL" => 46, "CM" => 47, 73 | "CN" => 48, "CO" => 49, "CR" => 50, "CU" => 51, "CV" => 52, "CX" => 53, 74 | "CY" => 54, "CZ" => 55, "DE" => 56, "DJ" => 57, "DK" => 58, "DM" => 59, 75 | "DO" => 60, "DZ" => 61, "EC" => 62, "EE" => 63, "EG" => 64, "EH" => 65, 76 | "ER" => 66, "ES" => 67, "ET" => 68, "FI" => 69, "FJ" => 70, "FK" => 71, 77 | "FM" => 72, "FO" => 73, "FR" => 74, "FX" => 75, "GA" => 76, "GB" => 77, 78 | "GD" => 78, "GE" => 79, "GF" => 80, "GH" => 81, "GI" => 82, "GL" => 83, 79 | "GM" => 84, "GN" => 85, "GP" => 86, "GQ" => 87, "GR" => 88, "GS" => 89, 80 | "GT" => 90, "GU" => 91, "GW" => 92, "GY" => 93, "HK" => 94, "HM" => 95, 81 | "HN" => 96, "HR" => 97, "HT" => 98, "HU" => 99, "ID" => 100, "IE" => 101, 82 | "IL" => 102, "IN" => 103, "IO" => 104, "IQ" => 105, "IR" => 106, "IS" => 107, 83 | "IT" => 108, "JM" => 109, "JO" => 110, "JP" => 111, "KE" => 112, "KG" => 113, 84 | "KH" => 114, "KI" => 115, "KM" => 116, "KN" => 117, "KP" => 118, "KR" => 119, 85 | "KW" => 120, "KY" => 121, "KZ" => 122, "LA" => 123, "LB" => 124, "LC" => 125, 86 | "LI" => 126, "LK" => 127, "LR" => 128, "LS" => 129, "LT" => 130, "LU" => 131, 87 | "LV" => 132, "LY" => 133, "MA" => 134, "MC" => 135, "MD" => 136, "MG" => 137, 88 | "MH" => 138, "MK" => 139, "ML" => 140, "MM" => 141, "MN" => 142, "MO" => 143, 89 | "MP" => 144, "MQ" => 145, "MR" => 146, "MS" => 147, "MT" => 148, "MU" => 149, 90 | "MV" => 150, "MW" => 151, "MX" => 152, "MY" => 153, "MZ" => 154, "NA" => 155, 91 | "NC" => 156, "NE" => 157, "NF" => 158, "NG" => 159, "NI" => 160, "NL" => 161, 92 | "NO" => 162, "NP" => 163, "NR" => 164, "NU" => 165, "NZ" => 166, "OM" => 167, 93 | "PA" => 168, "PE" => 169, "PF" => 170, "PG" => 171, "PH" => 172, "PK" => 173, 94 | "PL" => 174, "PM" => 175, "PN" => 176, "PR" => 177, "PS" => 178, "PT" => 179, 95 | "PW" => 180, "PY" => 181, "QA" => 182, "RE" => 183, "RO" => 184, "RU" => 185, 96 | "RW" => 186, "SA" => 187, "SB" => 188, "SC" => 189, "SD" => 190, "SE" => 191, 97 | "SG" => 192, "SH" => 193, "SI" => 194, "SJ" => 195, "SK" => 196, "SL" => 197, 98 | "SM" => 198, "SN" => 199, "SO" => 200, "SR" => 201, "ST" => 202, "SV" => 203, 99 | "SY" => 204, "SZ" => 205, "TC" => 206, "TD" => 207, "TF" => 208, "TG" => 209, 100 | "TH" => 210, "TJ" => 211, "TK" => 212, "TM" => 213, "TN" => 214, "TO" => 215, 101 | "TL" => 216, "TR" => 217, "TT" => 218, "TV" => 219, "TW" => 220, "TZ" => 221, 102 | "UA" => 222, "UG" => 223, "UM" => 224, "US" => 225, "UY" => 226, "UZ" => 227, 103 | "VA" => 228, "VC" => 229, "VE" => 230, "VG" => 231, "VI" => 232, "VN" => 233, 104 | "VU" => 234, "WF" => 235, "WS" => 236, "YE" => 237, "YT" => 238, "RS" => 239, 105 | "ZA" => 240, "ZM" => 241, "ME" => 242, "ZW" => 243, "A1" => 244, "A2" => 245, 106 | "O1" => 246, "AX" => 247, "GG" => 248, "IM" => 249, "JE" => 250 107 | ); 108 | var $GEOIP_COUNTRY_CODES = array( 109 | "", "AP", "EU", "AD", "AE", "AF", "AG", "AI", "AL", "AM", "AN", "AO", "AQ", 110 | "AR", "AS", "AT", "AU", "AW", "AZ", "BA", "BB", "BD", "BE", "BF", "BG", "BH", 111 | "BI", "BJ", "BM", "BN", "BO", "BR", "BS", "BT", "BV", "BW", "BY", "BZ", "CA", 112 | "CC", "CD", "CF", "CG", "CH", "CI", "CK", "CL", "CM", "CN", "CO", "CR", "CU", 113 | "CV", "CX", "CY", "CZ", "DE", "DJ", "DK", "DM", "DO", "DZ", "EC", "EE", "EG", 114 | "EH", "ER", "ES", "ET", "FI", "FJ", "FK", "FM", "FO", "FR", "FX", "GA", "GB", 115 | "GD", "GE", "GF", "GH", "GI", "GL", "GM", "GN", "GP", "GQ", "GR", "GS", "GT", 116 | "GU", "GW", "GY", "HK", "HM", "HN", "HR", "HT", "HU", "ID", "IE", "IL", "IN", 117 | "IO", "IQ", "IR", "IS", "IT", "JM", "JO", "JP", "KE", "KG", "KH", "KI", "KM", 118 | "KN", "KP", "KR", "KW", "KY", "KZ", "LA", "LB", "LC", "LI", "LK", "LR", "LS", 119 | "LT", "LU", "LV", "LY", "MA", "MC", "MD", "MG", "MH", "MK", "ML", "MM", "MN", 120 | "MO", "MP", "MQ", "MR", "MS", "MT", "MU", "MV", "MW", "MX", "MY", "MZ", "NA", 121 | "NC", "NE", "NF", "NG", "NI", "NL", "NO", "NP", "NR", "NU", "NZ", "OM", "PA", 122 | "PE", "PF", "PG", "PH", "PK", "PL", "PM", "PN", "PR", "PS", "PT", "PW", "PY", 123 | "QA", "RE", "RO", "RU", "RW", "SA", "SB", "SC", "SD", "SE", "SG", "SH", "SI", 124 | "SJ", "SK", "SL", "SM", "SN", "SO", "SR", "ST", "SV", "SY", "SZ", "TC", "TD", 125 | "TF", "TG", "TH", "TJ", "TK", "TM", "TN", "TO", "TL", "TR", "TT", "TV", "TW", 126 | "TZ", "UA", "UG", "UM", "US", "UY", "UZ", "VA", "VC", "VE", "VG", "VI", "VN", 127 | "VU", "WF", "WS", "YE", "YT", "RS", "ZA", "ZM", "ME", "ZW", "A1", "A2", "O1", 128 | "AX", "GG", "IM", "JE" 129 | ); 130 | var $GEOIP_COUNTRY_CODES3 = array( 131 | "","AP","EU","AND","ARE","AFG","ATG","AIA","ALB","ARM","ANT","AGO","AQ","ARG", 132 | "ASM","AUT","AUS","ABW","AZE","BIH","BRB","BGD","BEL","BFA","BGR","BHR","BDI", 133 | "BEN","BMU","BRN","BOL","BRA","BHS","BTN","BV","BWA","BLR","BLZ","CAN","CC", 134 | "COD","CAF","COG","CHE","CIV","COK","CHL","CMR","CHN","COL","CRI","CUB","CPV", 135 | "CX","CYP","CZE","DEU","DJI","DNK","DMA","DOM","DZA","ECU","EST","EGY","ESH", 136 | "ERI","ESP","ETH","FIN","FJI","FLK","FSM","FRO","FRA","FX","GAB","GBR","GRD", 137 | "GEO","GUF","GHA","GIB","GRL","GMB","GIN","GLP","GNQ","GRC","GS","GTM","GUM", 138 | "GNB","GUY","HKG","HM","HND","HRV","HTI","HUN","IDN","IRL","ISR","IND","IO", 139 | "IRQ","IRN","ISL","ITA","JAM","JOR","JPN","KEN","KGZ","KHM","KIR","COM","KNA", 140 | "PRK","KOR","KWT","CYM","KAZ","LAO","LBN","LCA","LIE","LKA","LBR","LSO","LTU", 141 | "LUX","LVA","LBY","MAR","MCO","MDA","MDG","MHL","MKD","MLI","MMR","MNG","MAC", 142 | "MNP","MTQ","MRT","MSR","MLT","MUS","MDV","MWI","MEX","MYS","MOZ","NAM","NCL", 143 | "NER","NFK","NGA","NIC","NLD","NOR","NPL","NRU","NIU","NZL","OMN","PAN","PER", 144 | "PYF","PNG","PHL","PAK","POL","SPM","PCN","PRI","PSE","PRT","PLW","PRY","QAT", 145 | "REU","ROU","RUS","RWA","SAU","SLB","SYC","SDN","SWE","SGP","SHN","SVN","SJM", 146 | "SVK","SLE","SMR","SEN","SOM","SUR","STP","SLV","SYR","SWZ","TCA","TCD","TF", 147 | "TGO","THA","TJK","TKL","TLS","TKM","TUN","TON","TUR","TTO","TUV","TWN","TZA", 148 | "UKR","UGA","UM","USA","URY","UZB","VAT","VCT","VEN","VGB","VIR","VNM","VUT", 149 | "WLF","WSM","YEM","YT","SRB","ZAF","ZMB","MNE","ZWE","A1","A2","O1", 150 | "ALA","GGY","IMN","JEY" 151 | ); 152 | var $GEOIP_COUNTRY_NAMES = array( 153 | "", "Asia/Pacific Region", "Europe", "Andorra", "United Arab Emirates", 154 | "Afghanistan", "Antigua and Barbuda", "Anguilla", "Albania", "Armenia", 155 | "Netherlands Antilles", "Angola", "Antarctica", "Argentina", "American Samoa", 156 | "Austria", "Australia", "Aruba", "Azerbaijan", "Bosnia and Herzegovina", 157 | "Barbados", "Bangladesh", "Belgium", "Burkina Faso", "Bulgaria", "Bahrain", 158 | "Burundi", "Benin", "Bermuda", "Brunei Darussalam", "Bolivia", "Brazil", 159 | "Bahamas", "Bhutan", "Bouvet Island", "Botswana", "Belarus", "Belize", 160 | "Canada", "Cocos (Keeling) Islands", "Congo, The Democratic Republic of the", 161 | "Central African Republic", "Congo", "Switzerland", "Cote D'Ivoire", "Cook 162 | Islands", "Chile", "Cameroon", "China", "Colombia", "Costa Rica", "Cuba", "Cape 163 | Verde", "Christmas Island", "Cyprus", "Czech Republic", "Germany", "Djibouti", 164 | "Denmark", "Dominica", "Dominican Republic", "Algeria", "Ecuador", "Estonia", 165 | "Egypt", "Western Sahara", "Eritrea", "Spain", "Ethiopia", "Finland", "Fiji", 166 | "Falkland Islands (Malvinas)", "Micronesia, Federated States of", "Faroe 167 | Islands", "France", "France, Metropolitan", "Gabon", "United Kingdom", 168 | "Grenada", "Georgia", "French Guiana", "Ghana", "Gibraltar", "Greenland", 169 | "Gambia", "Guinea", "Guadeloupe", "Equatorial Guinea", "Greece", "South Georgia 170 | and the South Sandwich Islands", "Guatemala", "Guam", "Guinea-Bissau", 171 | "Guyana", "Hong Kong", "Heard Island and McDonald Islands", "Honduras", 172 | "Croatia", "Haiti", "Hungary", "Indonesia", "Ireland", "Israel", "India", 173 | "British Indian Ocean Territory", "Iraq", "Iran, Islamic Republic of", 174 | "Iceland", "Italy", "Jamaica", "Jordan", "Japan", "Kenya", "Kyrgyzstan", 175 | "Cambodia", "Kiribati", "Comoros", "Saint Kitts and Nevis", "Korea, Democratic 176 | People's Republic of", "Korea, Republic of", "Kuwait", "Cayman Islands", 177 | "Kazakstan", "Lao People's Democratic Republic", "Lebanon", "Saint Lucia", 178 | "Liechtenstein", "Sri Lanka", "Liberia", "Lesotho", "Lithuania", "Luxembourg", 179 | "Latvia", "Libyan Arab Jamahiriya", "Morocco", "Monaco", "Moldova, Republic 180 | of", "Madagascar", "Marshall Islands", "Macedonia", 181 | "Mali", "Myanmar", "Mongolia", "Macau", "Northern Mariana Islands", 182 | "Martinique", "Mauritania", "Montserrat", "Malta", "Mauritius", "Maldives", 183 | "Malawi", "Mexico", "Malaysia", "Mozambique", "Namibia", "New Caledonia", 184 | "Niger", "Norfolk Island", "Nigeria", "Nicaragua", "Netherlands", "Norway", 185 | "Nepal", "Nauru", "Niue", "New Zealand", "Oman", "Panama", "Peru", "French 186 | Polynesia", "Papua New Guinea", "Philippines", "Pakistan", "Poland", "Saint 187 | Pierre and Miquelon", "Pitcairn Islands", "Puerto Rico", "Palestinian Territory", 188 | "Portugal", "Palau", "Paraguay", "Qatar", "Reunion", "Romania", 189 | "Russian Federation", "Rwanda", "Saudi Arabia", "Solomon Islands", 190 | "Seychelles", "Sudan", "Sweden", "Singapore", "Saint Helena", "Slovenia", 191 | "Svalbard and Jan Mayen", "Slovakia", "Sierra Leone", "San Marino", "Senegal", 192 | "Somalia", "Suriname", "Sao Tome and Principe", "El Salvador", "Syrian Arab 193 | Republic", "Swaziland", "Turks and Caicos Islands", "Chad", "French Southern 194 | Territories", "Togo", "Thailand", "Tajikistan", "Tokelau", "Turkmenistan", 195 | "Tunisia", "Tonga", "Timor-Leste", "Turkey", "Trinidad and Tobago", "Tuvalu", 196 | "Taiwan", "Tanzania, United Republic of", "Ukraine", 197 | "Uganda", "United States Minor Outlying Islands", "United States", "Uruguay", 198 | "Uzbekistan", "Holy See (Vatican City State)", "Saint Vincent and the 199 | Grenadines", "Venezuela", "Virgin Islands, British", "Virgin Islands, U.S.", 200 | "Vietnam", "Vanuatu", "Wallis and Futuna", "Samoa", "Yemen", "Mayotte", 201 | "Serbia", "South Africa", "Zambia", "Montenegro", "Zimbabwe", 202 | "Anonymous Proxy","Satellite Provider","Other", 203 | "Aland Islands","Guernsey","Isle of Man","Jersey" 204 | ); 205 | } 206 | function geoip_load_shared_mem ($file) { 207 | 208 | $fp = fopen($file, "rb"); 209 | if (!$fp) { 210 | print "error opening $file: $php_errormsg\n"; 211 | exit; 212 | } 213 | $s_array = fstat($fp); 214 | $size = $s_array['size']; 215 | if ($shmid = @shmop_open (GEOIP_SHM_KEY, "w", 0, 0)) { 216 | shmop_delete ($shmid); 217 | shmop_close ($shmid); 218 | } 219 | $shmid = shmop_open (GEOIP_SHM_KEY, "c", 0644, $size); 220 | shmop_write ($shmid, fread($fp, $size), 0); 221 | shmop_close ($shmid); 222 | } 223 | 224 | function _setup_segments($gi){ 225 | $gi->databaseType = GEOIP_COUNTRY_EDITION; 226 | $gi->record_length = STANDARD_RECORD_LENGTH; 227 | if ($gi->flags & GEOIP_SHARED_MEMORY) { 228 | $offset = @shmop_size ($gi->shmid) - 3; 229 | for ($i = 0; $i < STRUCTURE_INFO_MAX_SIZE; $i++) { 230 | $delim = @shmop_read ($gi->shmid, $offset, 3); 231 | $offset += 3; 232 | if ($delim == (chr(255).chr(255).chr(255))) { 233 | $gi->databaseType = ord(@shmop_read ($gi->shmid, $offset, 1)); 234 | $offset++; 235 | 236 | if ($gi->databaseType == GEOIP_REGION_EDITION_REV0){ 237 | $gi->databaseSegments = GEOIP_STATE_BEGIN_REV0; 238 | } else if ($gi->databaseType == GEOIP_REGION_EDITION_REV1){ 239 | $gi->databaseSegments = GEOIP_STATE_BEGIN_REV1; 240 | } else if (($gi->databaseType == GEOIP_CITY_EDITION_REV0)|| 241 | ($gi->databaseType == GEOIP_CITY_EDITION_REV1) 242 | || ($gi->databaseType == GEOIP_ORG_EDITION) 243 | || ($gi->databaseType == GEOIP_ISP_EDITION) 244 | || ($gi->databaseType == GEOIP_ASNUM_EDITION)){ 245 | $gi->databaseSegments = 0; 246 | $buf = @shmop_read ($gi->shmid, $offset, SEGMENT_RECORD_LENGTH); 247 | for ($j = 0;$j < SEGMENT_RECORD_LENGTH;$j++){ 248 | $gi->databaseSegments += (ord($buf[$j]) << ($j * 8)); 249 | } 250 | if (($gi->databaseType == GEOIP_ORG_EDITION)|| 251 | ($gi->databaseType == GEOIP_ISP_EDITION)) { 252 | $gi->record_length = ORG_RECORD_LENGTH; 253 | } 254 | } 255 | break; 256 | } else { 257 | $offset -= 4; 258 | } 259 | } 260 | if (($gi->databaseType == GEOIP_COUNTRY_EDITION)|| 261 | ($gi->databaseType == GEOIP_PROXY_EDITION)|| 262 | ($gi->databaseType == GEOIP_NETSPEED_EDITION)){ 263 | $gi->databaseSegments = GEOIP_COUNTRY_BEGIN; 264 | } 265 | } else { 266 | $filepos = ftell($gi->filehandle); 267 | fseek($gi->filehandle, -3, SEEK_END); 268 | for ($i = 0; $i < STRUCTURE_INFO_MAX_SIZE; $i++) { 269 | $delim = fread($gi->filehandle,3); 270 | if ($delim == (chr(255).chr(255).chr(255))){ 271 | $gi->databaseType = ord(fread($gi->filehandle,1)); 272 | if ($gi->databaseType == GEOIP_REGION_EDITION_REV0){ 273 | $gi->databaseSegments = GEOIP_STATE_BEGIN_REV0; 274 | } 275 | else if ($gi->databaseType == GEOIP_REGION_EDITION_REV1){ 276 | $gi->databaseSegments = GEOIP_STATE_BEGIN_REV1; 277 | } else if (($gi->databaseType == GEOIP_CITY_EDITION_REV0) || 278 | ($gi->databaseType == GEOIP_CITY_EDITION_REV1) || 279 | ($gi->databaseType == GEOIP_ORG_EDITION) || 280 | ($gi->databaseType == GEOIP_ISP_EDITION) || 281 | ($gi->databaseType == GEOIP_ASNUM_EDITION)){ 282 | $gi->databaseSegments = 0; 283 | $buf = fread($gi->filehandle,SEGMENT_RECORD_LENGTH); 284 | for ($j = 0;$j < SEGMENT_RECORD_LENGTH;$j++){ 285 | $gi->databaseSegments += (ord($buf[$j]) << ($j * 8)); 286 | } 287 | if ($gi->databaseType == GEOIP_ORG_EDITION) { 288 | $gi->record_length = ORG_RECORD_LENGTH; 289 | } 290 | } 291 | break; 292 | } else { 293 | fseek($gi->filehandle, -4, SEEK_CUR); 294 | } 295 | } 296 | if (($gi->databaseType == GEOIP_COUNTRY_EDITION)|| 297 | ($gi->databaseType == GEOIP_PROXY_EDITION)|| 298 | ($gi->databaseType == GEOIP_NETSPEED_EDITION)){ 299 | $gi->databaseSegments = GEOIP_COUNTRY_BEGIN; 300 | } 301 | fseek($gi->filehandle,$filepos,SEEK_SET); 302 | } 303 | return $gi; 304 | } 305 | 306 | function geoip_open($filename, $flags) { 307 | $gi = new GeoIP; 308 | $gi->flags = $flags; 309 | if ($gi->flags & GEOIP_SHARED_MEMORY) { 310 | $gi->shmid = @shmop_open (GEOIP_SHM_KEY, "a", 0, 0); 311 | } else { 312 | $gi->filehandle = fopen($filename,"rb"); 313 | if ($gi->flags & GEOIP_MEMORY_CACHE) { 314 | $s_array = fstat($gi->filehandle); 315 | $gi->memory_buffer = fread($gi->filehandle, $s_array[size]); 316 | } 317 | } 318 | 319 | $gi = _setup_segments($gi); 320 | return $gi; 321 | } 322 | 323 | function geoip_close($gi) { 324 | if ($gi->flags & GEOIP_SHARED_MEMORY) { 325 | return true; 326 | } 327 | 328 | return fclose($gi->filehandle); 329 | } 330 | 331 | function geoip_country_id_by_name($gi, $name) { 332 | $addr = gethostbyname($name); 333 | if (!$addr || $addr == $name) { 334 | return false; 335 | } 336 | return geoip_country_id_by_addr($gi, $addr); 337 | } 338 | 339 | function geoip_country_code_by_name($gi, $name) { 340 | $country_id = geoip_country_id_by_name($gi,$name); 341 | if ($country_id !== false) { 342 | return $gi->GEOIP_COUNTRY_CODES[$country_id]; 343 | } 344 | return false; 345 | } 346 | 347 | function geoip_country_name_by_name($gi, $name) { 348 | $country_id = geoip_country_id_by_name($gi,$name); 349 | if ($country_id !== false) { 350 | return $gi->GEOIP_COUNTRY_NAMES[$country_id]; 351 | } 352 | return false; 353 | } 354 | 355 | function geoip_country_id_by_addr($gi, $addr) { 356 | $ipnum = ip2long($addr); 357 | return _geoip_seek_country($gi, $ipnum) - GEOIP_COUNTRY_BEGIN; 358 | } 359 | 360 | function geoip_country_code_by_addr($gi, $addr) { 361 | if ($gi->databaseType == GEOIP_CITY_EDITION_REV1) { 362 | $record = geoip_record_by_addr($gi,$addr); 363 | return $record->country_code; 364 | } else { 365 | $country_id = geoip_country_id_by_addr($gi,$addr); 366 | if ($country_id !== false) { 367 | return $gi->GEOIP_COUNTRY_CODES[$country_id]; 368 | } 369 | } 370 | return false; 371 | } 372 | 373 | function geoip_country_name_by_addr($gi, $addr) { 374 | if ($gi->databaseType == GEOIP_CITY_EDITION_REV1) { 375 | $record = geoip_record_by_addr($gi,$addr); 376 | return $record->country_name; 377 | } else { 378 | $country_id = geoip_country_id_by_addr($gi,$addr); 379 | if ($country_id !== false) { 380 | return $gi->GEOIP_COUNTRY_NAMES[$country_id]; 381 | } 382 | } 383 | return false; 384 | } 385 | 386 | function _geoip_seek_country($gi, $ipnum) { 387 | $offset = 0; 388 | for ($depth = 31; $depth >= 0; --$depth) { 389 | if ($gi->flags & GEOIP_MEMORY_CACHE) { 390 | $buf = substr($gi->memory_buffer, 391 | 2 * $gi->record_length * $offset, 392 | 2 * $gi->record_length); 393 | } elseif ($gi->flags & GEOIP_SHARED_MEMORY) { 394 | $buf = @shmop_read ($gi->shmid, 395 | 2 * $gi->record_length * $offset, 396 | 2 * $gi->record_length ); 397 | } else { 398 | fseek($gi->filehandle, 2 * $gi->record_length * $offset, SEEK_SET) == 0 399 | or die("fseek failed"); 400 | $buf = fread($gi->filehandle, 2 * $gi->record_length); 401 | } 402 | $x = array(0,0); 403 | for ($i = 0; $i < 2; ++$i) { 404 | for ($j = 0; $j < $gi->record_length; ++$j) { 405 | $x[$i] += ord($buf[$gi->record_length * $i + $j]) << ($j * 8); 406 | } 407 | } 408 | if ($ipnum & (1 << $depth)) { 409 | if ($x[1] >= $gi->databaseSegments) { 410 | return $x[1]; 411 | } 412 | $offset = $x[1]; 413 | } else { 414 | if ($x[0] >= $gi->databaseSegments) { 415 | return $x[0]; 416 | } 417 | $offset = $x[0]; 418 | } 419 | } 420 | trigger_error("error traversing database - perhaps it is corrupt?", E_USER_ERROR); 421 | return false; 422 | } 423 | 424 | function _get_org($gi,$ipnum){ 425 | $seek_org = _geoip_seek_country($gi,$ipnum); 426 | if ($seek_org == $gi->databaseSegments) { 427 | return NULL; 428 | } 429 | $record_pointer = $seek_org + (2 * $gi->record_length - 1) * $gi->databaseSegments; 430 | if ($gi->flags & GEOIP_SHARED_MEMORY) { 431 | $org_buf = @shmop_read ($gi->shmid, $record_pointer, MAX_ORG_RECORD_LENGTH); 432 | } else { 433 | fseek($gi->filehandle, $record_pointer, SEEK_SET); 434 | $org_buf = fread($gi->filehandle,MAX_ORG_RECORD_LENGTH); 435 | } 436 | $org_buf = substr($org_buf, 0, strpos($org_buf, 0)); 437 | return $org_buf; 438 | } 439 | 440 | function geoip_org_by_addr ($gi,$addr) { 441 | if ($addr == NULL) { 442 | return 0; 443 | } 444 | $ipnum = ip2long($addr); 445 | return _get_org($gi, $ipnum); 446 | } 447 | 448 | function _get_region($gi,$ipnum){ 449 | if ($gi->databaseType == GEOIP_REGION_EDITION_REV0){ 450 | $seek_region = _geoip_seek_country($gi,$ipnum) - GEOIP_STATE_BEGIN_REV0; 451 | if ($seek_region >= 1000){ 452 | $country_code = "US"; 453 | $region = chr(($seek_region - 1000)/26 + 65) . chr(($seek_region - 1000)%26 + 65); 454 | } else { 455 | $country_code = $gi->GEOIP_COUNTRY_CODES[$seek_region]; 456 | $region = ""; 457 | } 458 | return array ($country_code,$region); 459 | } else if ($gi->databaseType == GEOIP_REGION_EDITION_REV1) { 460 | $seek_region = _geoip_seek_country($gi,$ipnum) - GEOIP_STATE_BEGIN_REV1; 461 | //print $seek_region; 462 | if ($seek_region < US_OFFSET){ 463 | $country_code = ""; 464 | $region = ""; 465 | } else if ($seek_region < CANADA_OFFSET) { 466 | $country_code = "US"; 467 | $region = chr(($seek_region - US_OFFSET)/26 + 65) . chr(($seek_region - US_OFFSET)%26 + 65); 468 | } else if ($seek_region < WORLD_OFFSET) { 469 | $country_code = "CA"; 470 | $region = chr(($seek_region - CANADA_OFFSET)/26 + 65) . chr(($seek_region - CANADA_OFFSET)%26 + 65); 471 | } else { 472 | $country_code = $gi->GEOIP_COUNTRY_CODES[($seek_region - WORLD_OFFSET) / FIPS_RANGE]; 473 | $region = ""; 474 | } 475 | return array ($country_code,$region); 476 | } 477 | } 478 | 479 | function geoip_region_by_addr ($gi,$addr) { 480 | if ($addr == NULL) { 481 | return 0; 482 | } 483 | $ipnum = ip2long($addr); 484 | return _get_region($gi, $ipnum); 485 | } 486 | 487 | function getdnsattributes ($l,$ip){ 488 | $r = new Net_DNS_Resolver(); 489 | $r->nameservers = array("ws1.maxmind.com"); 490 | $p = $r->search($l."." . $ip .".s.maxmind.com","TXT","IN"); 491 | $str = is_object($p->answer[0])?$p->answer[0]->string():''; 492 | ereg("\"(.*)\"",$str,$regs); 493 | $str = $regs[1]; 494 | return $str; 495 | } 496 | 497 | ?> 498 | -------------------------------------------------------------------------------- /javascript/mfx.js: -------------------------------------------------------------------------------- 1 | // Javascript library for common web stuff written back in 2007. 2 | // Should be replaced with something mordern ASAP probably... and put on fire. 3 | // 4 | // Copyright (C) 2007 Salvatore Sanfilippo (antirez at gmail dot com) 5 | 6 | /* ============================================================================= 7 | * UTILS 8 | * ========================================================================== */ 9 | 10 | /* Just a less verbose way to getElementById() */ 11 | function $(id) { 12 | if (typeof(id) == 'string') 13 | return document.getElementById(id); 14 | return id; 15 | } 16 | 17 | /* Return the innerHTML of the element with ID 'id' */ 18 | function $html(id) { 19 | id=$(id); 20 | return id.getElementById(id).innerHTML; 21 | } 22 | 23 | /* Set the innerHTML of th element with ID 'id' */ 24 | function $sethtml(id,html) { 25 | id=$(id); 26 | id.innerHTML = html; 27 | } 28 | 29 | /* Append HTML to innerHTML of element with ID 'id' */ 30 | function $apphtml(id,html) { 31 | id=$(id); 32 | id.innerHTML += html; 33 | } 34 | 35 | /* Handy way to test if typeof(o) is 'undefined' */ 36 | function isdef(o) { 37 | return typeof(o) != 'undefined'; 38 | } 39 | 40 | /* encodeURIComponent() working with IE5.0 */ 41 | function mfxEscape(s) { 42 | try { 43 | return encodeURIComponent(s); 44 | } catch(e) { 45 | var e = escape(s); 46 | e = e.replace(/@/g,"%40"); 47 | e = e.replace(/\//g,"%2f"); 48 | e = e.replace(/\+/g,"%2b"); 49 | return e; 50 | } 51 | } 52 | 53 | /* decodeURIComponent() working with IE5.0 */ 54 | function mfxUnescape(s) { 55 | try { 56 | s = s.replace(/\+/g,"%20"); 57 | return decodeURIComponent(s); 58 | } catch(e) { 59 | var s = unescape(s); 60 | s = s.replace(/\+/g," "); 61 | return s; 62 | } 63 | } 64 | 65 | /* mfxGetUrlParam("http://www.google.com?foo=bar","foo") => "bar" */ 66 | function mfxGetUrlParam(url,name) { 67 | var re="(&|\\?)"+name+"=([^&]*)"; 68 | if (m = url.match(re)) return mfxUnescape(m[2]); 69 | return false; 70 | } 71 | 72 | /* Preform an action every N milliseconds */ 73 | function mfxEvery(milliseconds,handler) { 74 | if (handler() !== false) 75 | setTimeout(function() { 76 | mfxEvery(milliseconds,handler); 77 | },milliseconds); 78 | } 79 | 80 | function mfxGetElementsByTagClass(tag,cname) { 81 | if (!document.getElementsByTagName) return []; 82 | var el = document.getElementsByTagName(tag); 83 | var res = []; 84 | for (var i = 0; i < el.length; i++) { 85 | var aux = ' '+el[i].className+' '; 86 | if (!cname || aux.indexOf(cname) != -1) { 87 | res[res.length] = el[i]; 88 | } 89 | } 90 | return res; 91 | } 92 | 93 | function mfxGetElementsByClass(cname) { 94 | return mfxGetElementsByTagClass('*',cname); 95 | } 96 | 97 | function mfxSaveStyle(o,pname,defvalue) { 98 | o = $(o); 99 | if (!isdef(o.mfxSavedStyle)) o.mfxSavedStyle = {}; 100 | var value = o.style[pname]; 101 | if (!isdef(value)) value=defvalue; 102 | o.mfxSavedStyle[pname]=value; 103 | } 104 | 105 | function mfxRestoreStyle(o,pname) { 106 | o = $(o); 107 | if (!isdef(o.mfxSavedStyle) || !isdef(o.mfxSavedStyle[pname])) return; 108 | o.style[pname] = o.mfxSavedStyle[pname]; 109 | delete o.mfxSavedStyle[pname]; 110 | } 111 | 112 | function mfxMap(o,f) { 113 | var res = []; 114 | for(var i = 0; i < o.length; i++) 115 | res[res.length] = f(o[i]); 116 | return res; 117 | } 118 | 119 | /* ============================================================================= 120 | * JSON 121 | * ========================================================================== */ 122 | function mfxJson(o) { 123 | if (typeof(o) == 'boolean') return String(o); 124 | if (typeof(o) == 'number') return String(o); 125 | if (typeof(o) == 'string') return mfxJsonString(o); 126 | if (typeof(o) == 'object') return mfxJsonArray(o); 127 | if (typeof(o) == 'undefined') return "undefined"; 128 | return undefined; 129 | } 130 | 131 | /* The string to json conversion is taken from json.org */ 132 | function mfxJsonString(s) { 133 | if (typeof(s) != 'string') s = String(s); 134 | var m = { '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', 135 | '\r': '\\r', '"' : '\\"', '\\': '\\\\' }; 136 | if (/["\\\x00-\x1f]/.test(s)) { 137 | return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) { 138 | var c = m[b]; 139 | if (c) { 140 | return c; 141 | } 142 | c = b.charCodeAt(); 143 | return '\\u00' + 144 | Math.floor(c / 16).toString(16) + 145 | (c % 16).toString(16); 146 | }) + '"'; 147 | } 148 | return '"'+s+'"'; 149 | } 150 | 151 | function mfxJsonArray(a) { 152 | var s = "["; 153 | for (var j = 0; j < a.length; j++) { 154 | s += mfxJson(a[j]); 155 | if (j != a.length-1) s += ","; 156 | } 157 | s += ']'; 158 | return s; 159 | } 160 | 161 | /* ============================================================================= 162 | * COOKIES 163 | * ========================================================================== */ 164 | function mfxSetCookie(name,value,expires,path,domain,secure) 165 | { 166 | document.cookie= name + "=" + escape(value) + 167 | ((expires) ? "; expires=" + expires.toGMTString() : "") + 168 | ((path) ? "; path=" + path : "") + 169 | ((domain) ? "; domain=" + domain : "") + 170 | ((secure) ? "; secure" : ""); 171 | } 172 | 173 | function mfxGetCookie(name) 174 | { 175 | var dc = document.cookie; 176 | var prefix = name + "="; 177 | var begin = dc.indexOf("; " + prefix); 178 | if (begin == -1) { 179 | begin = dc.indexOf(prefix); 180 | if (begin != 0) return null; 181 | } else { 182 | begin += 2; 183 | } 184 | var end = document.cookie.indexOf(";", begin); 185 | if (end == -1) end = dc.length; 186 | return unescape(dc.substring(begin + prefix.length, end)); 187 | } 188 | 189 | function mfxDelCookie(name,path,domain) 190 | { 191 | if (getCookie(name)) { 192 | document.cookie = name + "=" + 193 | ((path) ? "; path=" + path : "") + 194 | ((domain) ? "; domain=" + domain : "") + 195 | "; expires=Thu, 01-Jan-70 00:00:01 GMT"; 196 | } 197 | } 198 | 199 | /* ============================================================================= 200 | * FORMS 201 | * ========================================================================== */ 202 | 203 | function mfxGetInput(i) { 204 | i = $(i); 205 | if (isdef(i.type)) { 206 | if (i.type == 'text' || i.type == 'password') { 207 | return i.value; 208 | } else if (i.type == 'select-one') { 209 | return String(i.selectedIndex); 210 | } else if (i.type == 'checkbox') { 211 | if (i.checked == true) return "1"; 212 | return "0"; 213 | } 214 | } 215 | } 216 | 217 | function mfxSetInput(i,v) { 218 | i = $(i); 219 | if (isdef(i.type)) { 220 | if (i.type == 'text' || i.type == 'password') { 221 | i.value = v; 222 | if (typeof(i.onchange) == 'function') i.onchange(); 223 | } else if (i.type == 'select-one') { 224 | i.selectedIndex = Number(v); 225 | if (typeof(i.onchange) == 'function') i.onchange(); 226 | } else if (i.type == 'checkbox') { 227 | if ((Number(v) == true && i.checked == false) || 228 | (Number(v) == false && i.checked == true)) 229 | i.click(); 230 | } 231 | } 232 | } 233 | 234 | function mfxSaveInputs(idlist) { 235 | var a = []; 236 | for (var i = 0; i < idlist.length; i++) { 237 | a[a.length] = idlist[i]; 238 | a[a.length] = mfxGetInput(idlist[i]); 239 | } 240 | return a; 241 | } 242 | 243 | function mfxRestoreInputs(a) { 244 | for (var i = 0; i < a.length; i += 2) 245 | mfxSetInput(a[i],a[i+1]); 246 | } 247 | 248 | function mfxSaveInputsInString(idlist) { 249 | return mfxJson(mfxSaveInputs(idlist)); 250 | } 251 | 252 | function mfxRestoreInputsFromString(s) { 253 | mfxRestoreInputs(eval(s)); 254 | } 255 | 256 | function mfxSaveInputsInCookie(cookiename,idlist) { 257 | var s = mfxSaveInputsInString(idlist); 258 | var now = new Date; 259 | t = now.getTime(); 260 | now.setTime(t+(3600*24*1000*1000)); 261 | mfxSetCookie(cookiename,s,now); 262 | } 263 | 264 | function mfxRestoreInputsFromCookie(cookiename) { 265 | var c = mfxGetCookie(cookiename); 266 | if (c == null) return; 267 | mfxRestoreInputsFromString(c); 268 | } 269 | 270 | /* ============================================================================= 271 | * BROWSER detection 272 | * ========================================================================== */ 273 | 274 | /* Browser detection is uncool, but sometimes to test 275 | * for features is impossible */ 276 | function mfxIsGecko() { 277 | if (mfxIsKonqueror()) return false; 278 | return navigator.userAgent.toLowerCase().indexOf("gecko") != -1; 279 | } 280 | 281 | function mfxIsExplorer() { 282 | if (isdef(window.opera)) return false; 283 | return navigator.userAgent.toLowerCase().indexOf("msie") != -1; 284 | } 285 | 286 | function mfxIsOpera() { 287 | return isdef(window.opera); 288 | } 289 | 290 | function mfxIsSafari() { 291 | return isdef(navigator.vendor) && 292 | navigator.vendor.toLowerCase().indexOf("apple") != -1; 293 | } 294 | 295 | function mfxIsKonqueror() { 296 | return isdef(navigator.vendor) && 297 | navigator.vendor.indexOf("KDE") != -1; 298 | } 299 | 300 | function mfxIsIphone() { 301 | return isdef(navigator.vendor) && isdef(navigator.userAgent) && 302 | navigator.vendor.toLowerCase().indexOf("apple") != -1 && 303 | navigator.userAgent.toLowerCase().indexOf("iphone") != -1; 304 | } 305 | 306 | /* ============================================================================= 307 | * AJAX 308 | * ========================================================================== */ 309 | 310 | /* Browser compatibilty. 311 | * Tested with: 312 | * 313 | * Firefox 1.0 to 1.5 314 | * Konqueror 3.4.2 315 | * Internet Explorer 5.0 316 | * internet Explorer 6.0 317 | * internet Explorer 7.0 318 | * 319 | * It should work also in Opera and Safari without troubles. */ 320 | 321 | // Create the XML HTTP request object. We try to be 322 | // more cross-browser as possible. 323 | function mfxCreateXmlHttpReq(handler) { 324 | var xmlhttp = null; 325 | try { 326 | xmlhttp = new XMLHttpRequest(); 327 | } catch(e) { 328 | try { 329 | xmlhttp = new ActiveXObject("Msxml2.XMLHTTP"); 330 | } catch(e) { 331 | xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); 332 | } 333 | } 334 | xmlhttp.onreadystatechange = handler; 335 | return xmlhttp; 336 | } 337 | 338 | // An handler that does nothing, used for AJAX requests that 339 | // don't require a reply and are non-critical about error conditions. 340 | function mfxDummyHandler() { 341 | return true; 342 | } 343 | 344 | // Shortcut for creating a GET request and get the reply 345 | // This few lines of code can make Ajax stuff much more trivial 346 | // to write, and... to avoid patterns in programs is sane! 347 | function mfxGet(url,handler) { 348 | var a = new Array("placeholder"); 349 | for (var j=2; j= corner.x-delta && mpos.x <= corner.x && 583 | mpos.y >= corner.y-delta && mpos.y <= corner.y) { 584 | /* Ok, setup the resize operation */ 585 | o.resizeLastX = mpos.x; 586 | o.resizeLastY = mpos.y; 587 | o.resizeWidth = osize.width; 588 | o.resizeHeight = osize.height; 589 | document.onmousemove = function(e) { 590 | mfxResizeMove(e,o); 591 | }; 592 | document.onmouseup = function(e) { 593 | mfxResizeStop(e,o); 594 | } 595 | if (typeof(o.onresizestart) == 'function') 596 | o.onresizestart(e,o); 597 | mfxSaveStyle(o,'zIndex','0'); 598 | o.style.zIndex = '1000'; 599 | return true; 600 | } else { 601 | return false; 602 | } 603 | } 604 | 605 | function mfxResizeMove(e,o) { 606 | var mpos = mfxGetMousePos(e); 607 | var dx = mpos.x-o.resizeLastX; 608 | var dy = mpos.y-o.resizeLastY; 609 | o.resizeLastX = mpos.x; 610 | o.resizeLastY = mpos.y; 611 | o.resizeWidth += dx; 612 | o.resizeHeight += dy; 613 | o.style.width = (o.resizeWidth)+'px'; 614 | o.style.height = (o.resizeHeight)+'px'; 615 | if (typeof(o.onresize) == 'function') 616 | o.onresize(o); 617 | return false; 618 | } 619 | 620 | function mfxResizeStop(e,o) { 621 | document.onmousemove = null; 622 | document.onmouseup = null; 623 | if (typeof(o.onresizestop) == 'function') 624 | o.onresizestop(e,o); 625 | mfxRestoreStyle(o,'zIndex'); 626 | } 627 | 628 | /* ============================================================================= 629 | * CLICKTIPS - balloon-style helps on click or hover 630 | * ========================================================================== */ 631 | function registerClicktip(e,_text) { 632 | e.clicktipActive = false; 633 | e.onclick = function (event) { 634 | if (!event) var event = window.event; 635 | return handleClicktip(event,_text,e); 636 | }; 637 | } 638 | 639 | function registerClicktipId(id,text) { 640 | var e = document.getElementById(id); 641 | if (e) { 642 | registerClicktip(e,text); 643 | } else { 644 | alert("Clicktip error: no such element ID '"+id+"'"); 645 | } 646 | } 647 | 648 | /* Set a clicktip to all the elements of a given type/class */ 649 | function registerClicktipBulk(type,classname,text) { 650 | var i; 651 | var e = mfxGetElementsByTagClass(type,classname); 652 | for (i = 0; i < e.length; i++) 653 | registerClicktip(e[i], text); 654 | } 655 | 656 | function registerOvertip(e,_text,showdelay,hidedelay) { 657 | e.clicktipActive = false; 658 | e.clicktipTimeout = false; 659 | e.onmouseover = function (event) { 660 | if (!event) var event = window.event; 661 | return handleOvertipOver(e,event,_text,showdelay,hidedelay); 662 | }; 663 | e.onmouseout = function (event) { 664 | if (!event) var event = window.event; 665 | return handleOvertipOut(e,event,_text,showdelay,hidedelay); 666 | }; 667 | } 668 | 669 | function registerOvertipId(id,text,showdelay,hidedelay) { 670 | var e = document.getElementById(id); 671 | if (e) { 672 | registerOvertip(e,text,showdelay,hidedelay); 673 | } else { 674 | alert("Clicktip error: no such element ID '"+id+"'"); 675 | } 676 | } 677 | 678 | /* Set an overtip to all the elements of a given type/class */ 679 | function registerOvertipBulk(type,classname,text,showdelay,hidedelay) { 680 | if (isdef(document.getElementsByTagName)) { 681 | var e = document.getElementsByTagName(type); 682 | var i; 683 | for (i = 0; i < e.length; i++) { 684 | if (!classname || e[i].className == classname) { 685 | registerOvertip(e[i],text,showdelay,hidedelay); 686 | } 687 | } 688 | } 689 | } 690 | 691 | function delTip(div) { 692 | try { 693 | try { clearTimeout(div.clicktipTarget.clicktipTimeout); } catch(e) {}; 694 | try { div.clicktipTarget.clicktipTimeout = false; } catch(e) {}; 695 | try { div.clicktipTarget.clicktipActive = false; } catch(e) {}; 696 | document.body.removeChild(div); 697 | delete(div); 698 | } catch(e) {}; 699 | } 700 | 701 | function delTipOnClick() { 702 | delTip(this); 703 | } 704 | 705 | function createTipDiv(x,y,text,target) { 706 | /* Show a DIV with the right message */ 707 | var div = document.createElement('div'); 708 | div.className = 'clicktip'; 709 | div.style.visibility = 'hidden'; 710 | div.style.position = 'absolute'; 711 | div.style.left = x+"px"; 712 | div.style.top = y+"px"; 713 | /* We set the DIV content usign innerHTML, 714 | If you are a purist append a text node instead ;) */ 715 | div.innerHTML = text; 716 | 717 | /* When the clicktip gets clicked we hide it */ 718 | div.clicktipTarget = target; 719 | div.onclick = delTipOnClick; 720 | document.body.appendChild(div); 721 | 722 | /* Try to fix the 'top' in order to display the div just over the pointer */ 723 | var divsize = mfxGetElementSize(div); 724 | div.clicktipXDelta = 0; 725 | div.clicktipYDelta = 0; 726 | if (divsize) { 727 | /* Check if there is space on top to display the clicktip */ 728 | if (divsize.height < y) { 729 | div.clicktipYDelta = -(divsize.height+2); 730 | div.clicktipXDelta = 2; 731 | } else { 732 | div.clicktipXDelta = 2; 733 | /* No space on top, display the tip on the bottom, i.e. 734 | just don't alter the current position. */ 735 | } 736 | } 737 | if (div.clicktipXDelta || div.clicktipYDelta) { 738 | div.style.top = (y+div.clicktipYDelta)+"px"; 739 | div.style.left = (x+div.clicktipXDelta)+"px"; 740 | } 741 | div.style.visibility = 'visible'; 742 | return div; 743 | } 744 | 745 | function handleClicktip(e,text,target) { 746 | /* A clicktip is already on screen for this object? Return */ 747 | if (target.clicktipActive) return false; 748 | target.clicktipActive = true; 749 | 750 | /* The target object have a tipclick attribute? Use it as text */ 751 | if (target.getAttribute('clicktip')) text=target.getAttribute('clicktip'); 752 | if (!text) return; /* No text attribute nor one specified on registration */ 753 | 754 | /* Get the mouse position */ 755 | var mouse = mfxGetMousePos(e); 756 | 757 | /* Create/show the tip div */ 758 | var div = createTipDiv(mouse.x,mouse.y,text,target); 759 | 760 | /* Compute how long the clicktip should be shown */ 761 | var milliseconds = 2000; /* base time */ 762 | var textlen = text.length; 763 | 764 | /* Add one second for every 50 characters */ 765 | while(textlen > 30) { 766 | milliseconds += 1000; 767 | textlen -= 30; 768 | } 769 | 770 | /* Register a timer to remove the DIV after few seconds */ 771 | setTimeout(function() { 772 | try { 773 | target.clicktipActive = false; 774 | document.body.removeChild(div); 775 | delete(div); 776 | } catch(e) {}; 777 | }, milliseconds); 778 | return false; 779 | } 780 | 781 | function handleOvertipOver(target,e,text,showdelay,hidedelay) 782 | { 783 | var mouse = mfxGetMousePos(e); 784 | 785 | target.clicktipX = mouse.x; 786 | target.clicktipY = mouse.y; 787 | 788 | /* An overtip is already scheduled or shown for this object? Return */ 789 | if (target.clicktipTimeout !== false || target.clicktipActive) return false; 790 | 791 | /* Otherwise start the timer that will display the TIP */ 792 | target.clicktipTimeout = setTimeout(function() { 793 | showAfterDelay(target,text,showdelay,hidedelay); 794 | }, showdelay); 795 | } 796 | 797 | function handleOvertipOut(target,e,text,showdelay,hidedelay) 798 | { 799 | /* Clicktip scheduled but not yet shown, delete the timer */ 800 | if (target.clicktipTimeout !== false && !target.clicktipActive) { 801 | try { 802 | clearTimeout(target.clicktipTimeout); 803 | } catch(e) {}; 804 | target.clicktipTimeout = false; 805 | return; 806 | } 807 | 808 | /* Tip shown, register a timer to remove it */ 809 | if (target.clicktipActive) { 810 | target.clicktipTimeout = setTimeout(function() { 811 | hideAfterDelay(target); 812 | }, hidedelay); 813 | } 814 | } 815 | 816 | function showAfterDelay(target,text,showdelay,hidedelay) 817 | { 818 | var div = createTipDiv(target.clicktipX,target.clicktipY,text,target); 819 | target.clicktipActive = true; 820 | target.clicktipDiv = div; 821 | if (showdelay == 0) { 822 | target.onmousemove = function(event) { 823 | if (!event) var event = window.event; 824 | var mouse = mfxGetMousePos(event); 825 | tipFollowMouse(mouse,this); 826 | }; 827 | } 828 | } 829 | 830 | function hideAfterDelay(target) 831 | { 832 | delTip(target.clicktipDiv); 833 | } 834 | 835 | function tipFollowMouse(mouse,target) { 836 | var div = target.clicktipDiv; 837 | div.style.top = mouse.y + div.clicktipYDelta; 838 | div.style.left = mouse.x + div.clicktipXDelta; 839 | } 840 | 841 | /* ============================================================================= 842 | * EFFECTS 843 | * ========================================================================== */ 844 | 845 | // Set object opacity in a cross-browser fashion 846 | function mfxSetOpacity(o,val) { 847 | if (val == 1) val= mfxIsGecko() ? '' : 0.9999; 848 | o.style.opacity = val; 849 | try { 850 | o.style.filter = 'alpha(opacity='+Math.floor(val*100)+')'; 851 | } catch(e) {}; 852 | } 853 | 854 | // Fade the object 'o' from sval opacity to tval opacity 855 | // i.e. mfxFade(o,0,1) will fade in 856 | // mfxFade(o,1,0) will fade out 857 | function mfxFade(o,sval,tval,steps,delay) { 858 | o.style.zoom = '1'; // IE requires this to be set to 1 to set opacity 859 | if (isdef(o.fade)) { 860 | try {clearTimeout(o.fade.timeout);} catch(e) {} 861 | current = o.fade.current; 862 | } else { 863 | mfxSetOpacity(o,sval); 864 | current = sval; 865 | } 866 | o.fade = {}; 867 | o.fade.steps = isdef(steps) ? steps : 20; 868 | o.fade.delay = isdef(delay) ? delay : 50; 869 | o.fade.sval = sval; 870 | o.fade.tval = tval; 871 | o.fade.incr = (tval-sval)/o.fade.steps; 872 | o.fade.current = current; 873 | mfxFadeTimeout(o); 874 | } 875 | 876 | function mfxFadeTimeout(o) { 877 | o.fade.current += o.fade.incr; 878 | if(o.fade.current < 0) o.fade.current = 0; 879 | else if(o.fade.current > 1) o.fade.current = 1; 880 | mfxSetOpacity(o,o.fade.current); 881 | if ((o.fade.incr > 0 && o.fade.current < o.fade.tval) || 882 | (o.fade.incr < 0 && o.fade.current > o.fade.tval)) { 883 | o.fade.timeout = 884 | setTimeout(function() {mfxFadeTimeout(o);}, o.fade.delay); 885 | } else { 886 | if (isdef(o.onfadedone)) o.onfadedone(o); 887 | o.fade = undefined; 888 | } 889 | } 890 | 891 | function mfxSpydivPush(div,classname,html) { 892 | var fade = isdef(div.spyNoFade) ? 0 : 1; 893 | var ele = document.createElement('div'); 894 | var maxlen = isdef(div.spyMaxLen) ? div.spyMaxLen : 10; 895 | ele.className = classname; 896 | ele.innerHTML = html; 897 | if (fade) mfxSetOpacity(ele,0); 898 | if (!isdef(div.spyLastEle)) { 899 | div.appendChild(ele); 900 | div.spyLen = 1; 901 | } else { 902 | div.insertBefore(ele,div.spyLastEle); 903 | div.spyLen++; 904 | } 905 | div.spyLastEle = ele; 906 | if (fade) mfxFade(ele,0,1,3,50); 907 | while (div.spyLen > maxlen) { 908 | var nodes = div.childNodes; 909 | var last, i=0; 910 | while(1) { 911 | i++; 912 | last = nodes[nodes.length-i]; 913 | if (!isdef(last.spyRemoved)) break; 914 | } 915 | if (fade && !mfxIsIphone()) { 916 | last.onfadedone = function(e) { 917 | div.removeChild(e); 918 | } 919 | last.spyRemoved = true; 920 | mfxFade(last,1,0,5,50); 921 | } else { 922 | div.removeChild(last); 923 | } 924 | div.spyLen--; 925 | } 926 | return ele; 927 | } 928 | 929 | function mfxSpydivClear(div,toleave) { 930 | toleave = isdef(toleave) ? toleave : 0; 931 | while (div.spyLen > toleave) { 932 | var nodes = div.childNodes; 933 | var last, i=0; 934 | while(1) { 935 | i++; 936 | if (i > nodes.length) return; 937 | last = nodes[nodes.length-i]; 938 | if (!isdef(last.spyRemoved)) break; 939 | } 940 | if (div.spyLastEle == last) div.spyLastEle = undefined; 941 | div.removeChild(last); 942 | div.spyLen--; 943 | } 944 | } 945 | 946 | function mfxToggle(o) { 947 | o = $(o); 948 | if(!isdef(o.style.visibility) || 949 | o.style.visibility=='' || 950 | o.style.visibility=='visible') { 951 | mfxHide(o); 952 | return "hidden"; 953 | } else { 954 | mfxShow(o); 955 | return "visible"; 956 | } 957 | } 958 | 959 | function mfxShow(o) { 960 | o = $(o); 961 | o.style.visibility = 'visible'; 962 | o.style.display = 'block'; 963 | } 964 | 965 | function mfxHide(o) { 966 | o = $(o); 967 | o.style.visibility = 'hidden'; 968 | o.style.display = 'none'; 969 | } 970 | -------------------------------------------------------------------------------- /json.php: -------------------------------------------------------------------------------- 1 | 51 | * @author Matt Knapp 52 | * @author Brett Stimmerman 53 | * @copyright 2005 Michal Migurski 54 | * @version CVS: $Id: JSON.php,v 1.31 2006/06/28 05:54:17 migurski Exp $ 55 | * @license http://www.opensource.org/licenses/bsd-license.php 56 | * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198 57 | */ 58 | 59 | /** 60 | * Marker constant for Services_JSON::decode(), used to flag stack state 61 | */ 62 | define('SERVICES_JSON_SLICE', 1); 63 | 64 | /** 65 | * Marker constant for Services_JSON::decode(), used to flag stack state 66 | */ 67 | define('SERVICES_JSON_IN_STR', 2); 68 | 69 | /** 70 | * Marker constant for Services_JSON::decode(), used to flag stack state 71 | */ 72 | define('SERVICES_JSON_IN_ARR', 3); 73 | 74 | /** 75 | * Marker constant for Services_JSON::decode(), used to flag stack state 76 | */ 77 | define('SERVICES_JSON_IN_OBJ', 4); 78 | 79 | /** 80 | * Marker constant for Services_JSON::decode(), used to flag stack state 81 | */ 82 | define('SERVICES_JSON_IN_CMT', 5); 83 | 84 | /** 85 | * Behavior switch for Services_JSON::decode() 86 | */ 87 | define('SERVICES_JSON_LOOSE_TYPE', 16); 88 | 89 | /** 90 | * Behavior switch for Services_JSON::decode() 91 | */ 92 | define('SERVICES_JSON_SUPPRESS_ERRORS', 32); 93 | 94 | /** 95 | * Converts to and from JSON format. 96 | * 97 | * Brief example of use: 98 | * 99 | * 100 | * // create a new instance of Services_JSON 101 | * $json = new Services_JSON(); 102 | * 103 | * // convert a complexe value to JSON notation, and send it to the browser 104 | * $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4))); 105 | * $output = $json->encode($value); 106 | * 107 | * print($output); 108 | * // prints: ["foo","bar",[1,2,"baz"],[3,[4]]] 109 | * 110 | * // accept incoming POST data, assumed to be in JSON notation 111 | * $input = file_get_contents('php://input', 1000000); 112 | * $value = $json->decode($input); 113 | * 114 | */ 115 | class Services_JSON 116 | { 117 | /** 118 | * constructs a new JSON instance 119 | * 120 | * @param int $use object behavior flags; combine with boolean-OR 121 | * 122 | * possible values: 123 | * - SERVICES_JSON_LOOSE_TYPE: loose typing. 124 | * "{...}" syntax creates associative arrays 125 | * instead of objects in decode(). 126 | * - SERVICES_JSON_SUPPRESS_ERRORS: error suppression. 127 | * Values which can't be encoded (e.g. resources) 128 | * appear as NULL instead of throwing errors. 129 | * By default, a deeply-nested resource will 130 | * bubble up with an error, so all return values 131 | * from encode() should be checked with isError() 132 | */ 133 | function Services_JSON($use = 0) 134 | { 135 | $this->use = $use; 136 | } 137 | 138 | /** 139 | * convert a string from one UTF-16 char to one UTF-8 char 140 | * 141 | * Normally should be handled by mb_convert_encoding, but 142 | * provides a slower PHP-only method for installations 143 | * that lack the multibye string extension. 144 | * 145 | * @param string $utf16 UTF-16 character 146 | * @return string UTF-8 character 147 | * @access private 148 | */ 149 | function utf162utf8($utf16) 150 | { 151 | // oh please oh please oh please oh please oh please 152 | if(function_exists('mb_convert_encoding')) { 153 | return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16'); 154 | } 155 | 156 | $bytes = (ord($utf16{0}) << 8) | ord($utf16{1}); 157 | 158 | switch(true) { 159 | case ((0x7F & $bytes) == $bytes): 160 | // this case should never be reached, because we are in ASCII range 161 | // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 162 | return chr(0x7F & $bytes); 163 | 164 | case (0x07FF & $bytes) == $bytes: 165 | // return a 2-byte UTF-8 character 166 | // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 167 | return chr(0xC0 | (($bytes >> 6) & 0x1F)) 168 | . chr(0x80 | ($bytes & 0x3F)); 169 | 170 | case (0xFFFF & $bytes) == $bytes: 171 | // return a 3-byte UTF-8 character 172 | // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 173 | return chr(0xE0 | (($bytes >> 12) & 0x0F)) 174 | . chr(0x80 | (($bytes >> 6) & 0x3F)) 175 | . chr(0x80 | ($bytes & 0x3F)); 176 | } 177 | 178 | // ignoring UTF-32 for now, sorry 179 | return ''; 180 | } 181 | 182 | /** 183 | * convert a string from one UTF-8 char to one UTF-16 char 184 | * 185 | * Normally should be handled by mb_convert_encoding, but 186 | * provides a slower PHP-only method for installations 187 | * that lack the multibye string extension. 188 | * 189 | * @param string $utf8 UTF-8 character 190 | * @return string UTF-16 character 191 | * @access private 192 | */ 193 | function utf82utf16($utf8) 194 | { 195 | // oh please oh please oh please oh please oh please 196 | if(function_exists('mb_convert_encoding')) { 197 | return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); 198 | } 199 | 200 | switch(strlen($utf8)) { 201 | case 1: 202 | // this case should never be reached, because we are in ASCII range 203 | // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 204 | return $utf8; 205 | 206 | case 2: 207 | // return a UTF-16 character from a 2-byte UTF-8 char 208 | // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 209 | return chr(0x07 & (ord($utf8{0}) >> 2)) 210 | . chr((0xC0 & (ord($utf8{0}) << 6)) 211 | | (0x3F & ord($utf8{1}))); 212 | 213 | case 3: 214 | // return a UTF-16 character from a 3-byte UTF-8 char 215 | // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 216 | return chr((0xF0 & (ord($utf8{0}) << 4)) 217 | | (0x0F & (ord($utf8{1}) >> 2))) 218 | . chr((0xC0 & (ord($utf8{1}) << 6)) 219 | | (0x7F & ord($utf8{2}))); 220 | } 221 | 222 | // ignoring UTF-32 for now, sorry 223 | return ''; 224 | } 225 | 226 | /** 227 | * encodes an arbitrary variable into JSON format 228 | * 229 | * @param mixed $var any number, boolean, string, array, or object to be encoded. 230 | * see argument 1 to Services_JSON() above for array-parsing behavior. 231 | * if var is a strng, note that encode() always expects it 232 | * to be in ASCII or UTF-8 format! 233 | * 234 | * @return mixed JSON string representation of input var or an error if a problem occurs 235 | * @access public 236 | */ 237 | function encode($var) 238 | { 239 | switch (gettype($var)) { 240 | case 'boolean': 241 | return $var ? 'true' : 'false'; 242 | 243 | case 'NULL': 244 | return 'null'; 245 | 246 | case 'integer': 247 | return (int) $var; 248 | 249 | case 'double': 250 | case 'float': 251 | return (float) $var; 252 | 253 | case 'string': 254 | // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT 255 | $ascii = ''; 256 | $strlen_var = strlen($var); 257 | 258 | /* 259 | * Iterate over every character in the string, 260 | * escaping with a slash or encoding to UTF-8 where necessary 261 | */ 262 | for ($c = 0; $c < $strlen_var; ++$c) { 263 | 264 | $ord_var_c = ord($var{$c}); 265 | 266 | switch (true) { 267 | case $ord_var_c == 0x08: 268 | $ascii .= '\b'; 269 | break; 270 | case $ord_var_c == 0x09: 271 | $ascii .= '\t'; 272 | break; 273 | case $ord_var_c == 0x0A: 274 | $ascii .= '\n'; 275 | break; 276 | case $ord_var_c == 0x0C: 277 | $ascii .= '\f'; 278 | break; 279 | case $ord_var_c == 0x0D: 280 | $ascii .= '\r'; 281 | break; 282 | 283 | case $ord_var_c == 0x22: 284 | case $ord_var_c == 0x2F: 285 | case $ord_var_c == 0x5C: 286 | // double quote, slash, slosh 287 | $ascii .= '\\'.$var{$c}; 288 | break; 289 | 290 | case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)): 291 | // characters U-00000000 - U-0000007F (same as ASCII) 292 | $ascii .= $var{$c}; 293 | break; 294 | 295 | case (($ord_var_c & 0xE0) == 0xC0): 296 | // characters U-00000080 - U-000007FF, mask 110XXXXX 297 | // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 298 | $char = pack('C*', $ord_var_c, ord($var{$c + 1})); 299 | $c += 1; 300 | $utf16 = $this->utf82utf16($char); 301 | $ascii .= sprintf('\u%04s', bin2hex($utf16)); 302 | break; 303 | 304 | case (($ord_var_c & 0xF0) == 0xE0): 305 | // characters U-00000800 - U-0000FFFF, mask 1110XXXX 306 | // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 307 | $char = pack('C*', $ord_var_c, 308 | ord($var{$c + 1}), 309 | ord($var{$c + 2})); 310 | $c += 2; 311 | $utf16 = $this->utf82utf16($char); 312 | $ascii .= sprintf('\u%04s', bin2hex($utf16)); 313 | break; 314 | 315 | case (($ord_var_c & 0xF8) == 0xF0): 316 | // characters U-00010000 - U-001FFFFF, mask 11110XXX 317 | // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 318 | $char = pack('C*', $ord_var_c, 319 | ord($var{$c + 1}), 320 | ord($var{$c + 2}), 321 | ord($var{$c + 3})); 322 | $c += 3; 323 | $utf16 = $this->utf82utf16($char); 324 | $ascii .= sprintf('\u%04s', bin2hex($utf16)); 325 | break; 326 | 327 | case (($ord_var_c & 0xFC) == 0xF8): 328 | // characters U-00200000 - U-03FFFFFF, mask 111110XX 329 | // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 330 | $char = pack('C*', $ord_var_c, 331 | ord($var{$c + 1}), 332 | ord($var{$c + 2}), 333 | ord($var{$c + 3}), 334 | ord($var{$c + 4})); 335 | $c += 4; 336 | $utf16 = $this->utf82utf16($char); 337 | $ascii .= sprintf('\u%04s', bin2hex($utf16)); 338 | break; 339 | 340 | case (($ord_var_c & 0xFE) == 0xFC): 341 | // characters U-04000000 - U-7FFFFFFF, mask 1111110X 342 | // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 343 | $char = pack('C*', $ord_var_c, 344 | ord($var{$c + 1}), 345 | ord($var{$c + 2}), 346 | ord($var{$c + 3}), 347 | ord($var{$c + 4}), 348 | ord($var{$c + 5})); 349 | $c += 5; 350 | $utf16 = $this->utf82utf16($char); 351 | $ascii .= sprintf('\u%04s', bin2hex($utf16)); 352 | break; 353 | } 354 | } 355 | 356 | return '"'.$ascii.'"'; 357 | 358 | case 'array': 359 | /* 360 | * As per JSON spec if any array key is not an integer 361 | * we must treat the the whole array as an object. We 362 | * also try to catch a sparsely populated associative 363 | * array with numeric keys here because some JS engines 364 | * will create an array with empty indexes up to 365 | * max_index which can cause memory issues and because 366 | * the keys, which may be relevant, will be remapped 367 | * otherwise. 368 | * 369 | * As per the ECMA and JSON specification an object may 370 | * have any string as a property. Unfortunately due to 371 | * a hole in the ECMA specification if the key is a 372 | * ECMA reserved word or starts with a digit the 373 | * parameter is only accessible using ECMAScript's 374 | * bracket notation. 375 | */ 376 | 377 | // treat as a JSON object 378 | if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) { 379 | $properties = array_map(array($this, 'name_value'), 380 | array_keys($var), 381 | array_values($var)); 382 | 383 | foreach($properties as $property) { 384 | if(Services_JSON::isError($property)) { 385 | return $property; 386 | } 387 | } 388 | 389 | return '{' . join(',', $properties) . '}'; 390 | } 391 | 392 | // treat it like a regular array 393 | $elements = array_map(array($this, 'encode'), $var); 394 | 395 | foreach($elements as $element) { 396 | if(Services_JSON::isError($element)) { 397 | return $element; 398 | } 399 | } 400 | 401 | return '[' . join(',', $elements) . ']'; 402 | 403 | case 'object': 404 | $vars = get_object_vars($var); 405 | 406 | $properties = array_map(array($this, 'name_value'), 407 | array_keys($vars), 408 | array_values($vars)); 409 | 410 | foreach($properties as $property) { 411 | if(Services_JSON::isError($property)) { 412 | return $property; 413 | } 414 | } 415 | 416 | return '{' . join(',', $properties) . '}'; 417 | 418 | default: 419 | return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS) 420 | ? 'null' 421 | : new Services_JSON_Error(gettype($var)." can not be encoded as JSON string"); 422 | } 423 | } 424 | 425 | /** 426 | * array-walking function for use in generating JSON-formatted name-value pairs 427 | * 428 | * @param string $name name of key to use 429 | * @param mixed $value reference to an array element to be encoded 430 | * 431 | * @return string JSON-formatted name-value pair, like '"name":value' 432 | * @access private 433 | */ 434 | function name_value($name, $value) 435 | { 436 | $encoded_value = $this->encode($value); 437 | 438 | if(Services_JSON::isError($encoded_value)) { 439 | return $encoded_value; 440 | } 441 | 442 | return $this->encode(strval($name)) . ':' . $encoded_value; 443 | } 444 | 445 | /** 446 | * reduce a string by removing leading and trailing comments and whitespace 447 | * 448 | * @param $str string string value to strip of comments and whitespace 449 | * 450 | * @return string string value stripped of comments and whitespace 451 | * @access private 452 | */ 453 | function reduce_string($str) 454 | { 455 | $str = preg_replace(array( 456 | 457 | // eliminate single line comments in '// ...' form 458 | '#^\s*//(.+)$#m', 459 | 460 | // eliminate multi-line comments in '/* ... */' form, at start of string 461 | '#^\s*/\*(.+)\*/#Us', 462 | 463 | // eliminate multi-line comments in '/* ... */' form, at end of string 464 | '#/\*(.+)\*/\s*$#Us' 465 | 466 | ), '', $str); 467 | 468 | // eliminate extraneous space 469 | return trim($str); 470 | } 471 | 472 | /** 473 | * decodes a JSON string into appropriate variable 474 | * 475 | * @param string $str JSON-formatted string 476 | * 477 | * @return mixed number, boolean, string, array, or object 478 | * corresponding to given JSON input string. 479 | * See argument 1 to Services_JSON() above for object-output behavior. 480 | * Note that decode() always returns strings 481 | * in ASCII or UTF-8 format! 482 | * @access public 483 | */ 484 | function decode($str) 485 | { 486 | $str = $this->reduce_string($str); 487 | 488 | switch (strtolower($str)) { 489 | case 'true': 490 | return true; 491 | 492 | case 'false': 493 | return false; 494 | 495 | case 'null': 496 | return null; 497 | 498 | default: 499 | $m = array(); 500 | 501 | if (is_numeric($str)) { 502 | // Lookie-loo, it's a number 503 | 504 | // This would work on its own, but I'm trying to be 505 | // good about returning integers where appropriate: 506 | // return (float)$str; 507 | 508 | // Return float or int, as appropriate 509 | return ((float)$str == (integer)$str) 510 | ? (integer)$str 511 | : (float)$str; 512 | 513 | } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) { 514 | // STRINGS RETURNED IN UTF-8 FORMAT 515 | $delim = substr($str, 0, 1); 516 | $chrs = substr($str, 1, -1); 517 | $utf8 = ''; 518 | $strlen_chrs = strlen($chrs); 519 | 520 | for ($c = 0; $c < $strlen_chrs; ++$c) { 521 | 522 | $substr_chrs_c_2 = substr($chrs, $c, 2); 523 | $ord_chrs_c = ord($chrs{$c}); 524 | 525 | switch (true) { 526 | case $substr_chrs_c_2 == '\b': 527 | $utf8 .= chr(0x08); 528 | ++$c; 529 | break; 530 | case $substr_chrs_c_2 == '\t': 531 | $utf8 .= chr(0x09); 532 | ++$c; 533 | break; 534 | case $substr_chrs_c_2 == '\n': 535 | $utf8 .= chr(0x0A); 536 | ++$c; 537 | break; 538 | case $substr_chrs_c_2 == '\f': 539 | $utf8 .= chr(0x0C); 540 | ++$c; 541 | break; 542 | case $substr_chrs_c_2 == '\r': 543 | $utf8 .= chr(0x0D); 544 | ++$c; 545 | break; 546 | 547 | case $substr_chrs_c_2 == '\\"': 548 | case $substr_chrs_c_2 == '\\\'': 549 | case $substr_chrs_c_2 == '\\\\': 550 | case $substr_chrs_c_2 == '\\/': 551 | if (($delim == '"' && $substr_chrs_c_2 != '\\\'') || 552 | ($delim == "'" && $substr_chrs_c_2 != '\\"')) { 553 | $utf8 .= $chrs{++$c}; 554 | } 555 | break; 556 | 557 | case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)): 558 | // single, escaped unicode character 559 | $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2))) 560 | . chr(hexdec(substr($chrs, ($c + 4), 2))); 561 | $utf8 .= $this->utf162utf8($utf16); 562 | $c += 5; 563 | break; 564 | 565 | case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F): 566 | $utf8 .= $chrs{$c}; 567 | break; 568 | 569 | case ($ord_chrs_c & 0xE0) == 0xC0: 570 | // characters U-00000080 - U-000007FF, mask 110XXXXX 571 | //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 572 | $utf8 .= substr($chrs, $c, 2); 573 | ++$c; 574 | break; 575 | 576 | case ($ord_chrs_c & 0xF0) == 0xE0: 577 | // characters U-00000800 - U-0000FFFF, mask 1110XXXX 578 | // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 579 | $utf8 .= substr($chrs, $c, 3); 580 | $c += 2; 581 | break; 582 | 583 | case ($ord_chrs_c & 0xF8) == 0xF0: 584 | // characters U-00010000 - U-001FFFFF, mask 11110XXX 585 | // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 586 | $utf8 .= substr($chrs, $c, 4); 587 | $c += 3; 588 | break; 589 | 590 | case ($ord_chrs_c & 0xFC) == 0xF8: 591 | // characters U-00200000 - U-03FFFFFF, mask 111110XX 592 | // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 593 | $utf8 .= substr($chrs, $c, 5); 594 | $c += 4; 595 | break; 596 | 597 | case ($ord_chrs_c & 0xFE) == 0xFC: 598 | // characters U-04000000 - U-7FFFFFFF, mask 1111110X 599 | // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 600 | $utf8 .= substr($chrs, $c, 6); 601 | $c += 5; 602 | break; 603 | 604 | } 605 | 606 | } 607 | 608 | return $utf8; 609 | 610 | } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) { 611 | // array, or object notation 612 | 613 | if ($str{0} == '[') { 614 | $stk = array(SERVICES_JSON_IN_ARR); 615 | $arr = array(); 616 | } else { 617 | if ($this->use & SERVICES_JSON_LOOSE_TYPE) { 618 | $stk = array(SERVICES_JSON_IN_OBJ); 619 | $obj = array(); 620 | } else { 621 | $stk = array(SERVICES_JSON_IN_OBJ); 622 | $obj = new stdClass(); 623 | } 624 | } 625 | 626 | array_push($stk, array('what' => SERVICES_JSON_SLICE, 627 | 'where' => 0, 628 | 'delim' => false)); 629 | 630 | $chrs = substr($str, 1, -1); 631 | $chrs = $this->reduce_string($chrs); 632 | 633 | if ($chrs == '') { 634 | if (reset($stk) == SERVICES_JSON_IN_ARR) { 635 | return $arr; 636 | 637 | } else { 638 | return $obj; 639 | 640 | } 641 | } 642 | 643 | //print("\nparsing {$chrs}\n"); 644 | 645 | $strlen_chrs = strlen($chrs); 646 | 647 | for ($c = 0; $c <= $strlen_chrs; ++$c) { 648 | 649 | $top = end($stk); 650 | $substr_chrs_c_2 = substr($chrs, $c, 2); 651 | 652 | if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) { 653 | // found a comma that is not inside a string, array, etc., 654 | // OR we've reached the end of the character list 655 | $slice = substr($chrs, $top['where'], ($c - $top['where'])); 656 | array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false)); 657 | //print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); 658 | 659 | if (reset($stk) == SERVICES_JSON_IN_ARR) { 660 | // we are in an array, so just push an element onto the stack 661 | array_push($arr, $this->decode($slice)); 662 | 663 | } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { 664 | // we are in an object, so figure 665 | // out the property name and set an 666 | // element in an associative array, 667 | // for now 668 | $parts = array(); 669 | 670 | if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { 671 | // "name":value pair 672 | $key = $this->decode($parts[1]); 673 | $val = $this->decode($parts[2]); 674 | 675 | if ($this->use & SERVICES_JSON_LOOSE_TYPE) { 676 | $obj[$key] = $val; 677 | } else { 678 | $obj->$key = $val; 679 | } 680 | } elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { 681 | // name:value pair, where name is unquoted 682 | $key = $parts[1]; 683 | $val = $this->decode($parts[2]); 684 | 685 | if ($this->use & SERVICES_JSON_LOOSE_TYPE) { 686 | $obj[$key] = $val; 687 | } else { 688 | $obj->$key = $val; 689 | } 690 | } 691 | 692 | } 693 | 694 | } elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) { 695 | // found a quote, and we are not inside a string 696 | array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c})); 697 | //print("Found start of string at {$c}\n"); 698 | 699 | } elseif (($chrs{$c} == $top['delim']) && 700 | ($top['what'] == SERVICES_JSON_IN_STR) && 701 | ((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) { 702 | // found a quote, we're in a string, and it's not escaped 703 | // we know that it's not escaped becase there is _not_ an 704 | // odd number of backslashes at the end of the string so far 705 | array_pop($stk); 706 | //print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n"); 707 | 708 | } elseif (($chrs{$c} == '[') && 709 | in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { 710 | // found a left-bracket, and we are in an array, object, or slice 711 | array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false)); 712 | //print("Found start of array at {$c}\n"); 713 | 714 | } elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) { 715 | // found a right-bracket, and we're in an array 716 | array_pop($stk); 717 | //print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); 718 | 719 | } elseif (($chrs{$c} == '{') && 720 | in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { 721 | // found a left-brace, and we are in an array, object, or slice 722 | array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false)); 723 | //print("Found start of object at {$c}\n"); 724 | 725 | } elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) { 726 | // found a right-brace, and we're in an object 727 | array_pop($stk); 728 | //print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); 729 | 730 | } elseif (($substr_chrs_c_2 == '/*') && 731 | in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { 732 | // found a comment start, and we are in an array, object, or slice 733 | array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false)); 734 | $c++; 735 | //print("Found start of comment at {$c}\n"); 736 | 737 | } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) { 738 | // found a comment end, and we're in one now 739 | array_pop($stk); 740 | $c++; 741 | 742 | for ($i = $top['where']; $i <= $c; ++$i) 743 | $chrs = substr_replace($chrs, ' ', $i, 1); 744 | 745 | //print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); 746 | 747 | } 748 | 749 | } 750 | 751 | if (reset($stk) == SERVICES_JSON_IN_ARR) { 752 | return $arr; 753 | 754 | } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { 755 | return $obj; 756 | 757 | } 758 | 759 | } 760 | } 761 | } 762 | 763 | /** 764 | * @todo Ultimately, this should just call PEAR::isError() 765 | */ 766 | function isError($data, $code = null) 767 | { 768 | if (class_exists('pear')) { 769 | return PEAR::isError($data, $code); 770 | } elseif (is_object($data) && (get_class($data) == 'services_json_error' || 771 | is_subclass_of($data, 'services_json_error'))) { 772 | return true; 773 | } 774 | 775 | return false; 776 | } 777 | } 778 | 779 | if (class_exists('PEAR_Error')) { 780 | 781 | class Services_JSON_Error extends PEAR_Error 782 | { 783 | function Services_JSON_Error($message = 'unknown error', $code = null, 784 | $mode = null, $options = null, $userinfo = null) 785 | { 786 | parent::PEAR_Error($message, $code, $mode, $options, $userinfo); 787 | } 788 | } 789 | 790 | } else { 791 | 792 | /** 793 | * @todo Ultimately, this class shall be descended from PEAR_Error 794 | */ 795 | class Services_JSON_Error 796 | { 797 | function Services_JSON_Error($message = 'unknown error', $code = null, 798 | $mode = null, $options = null, $userinfo = null) 799 | { 800 | 801 | } 802 | } 803 | 804 | } 805 | 806 | ?> 807 | --------------------------------------------------------------------------------