Added version control.

master
Neil 2 years ago
commit 44ffa35ce2
  1. 15
      copying.txt
  2. 465
      fb/facebook.php
  3. 104
      fb/facebook_desktop.php
  4. 2082
      fb/facebookapi_php5_restlib.php
  5. 806
      fb/jsonwrapper/JSON/JSON.php
  6. 21
      fb/jsonwrapper/JSON/LICENSE
  7. 6
      fb/jsonwrapper/jsonwrapper.php
  8. 23
      fb/jsonwrapper/jsonwrapper_inner.php
  9. 6
      fb/readme-fb.txt
  10. 3
      gifbar/Makefile
  11. 313
      gifbar/gifbar.c
  12. 619
      gpl.txt
  13. 25
      help.html
  14. 56
      index.php
  15. BIN
      media/2f-daisy.jpg
  16. BIN
      media/2f-daisy.piv
  17. BIN
      media/2f-daisy0.bmp
  18. BIN
      media/3f-threesome.jpg
  19. BIN
      media/3f-threesome.piv
  20. BIN
      media/3f-threesome0.bmp
  21. BIN
      media/68.jpg
  22. BIN
      media/68.piv
  23. BIN
      media/680.bmp
  24. BIN
      media/69.jpg
  25. BIN
      media/69.piv
  26. BIN
      media/690.bmp
  27. BIN
      media/acrobat.jpg
  28. BIN
      media/acrobat.piv
  29. BIN
      media/acrobat0.bmp
  30. BIN
      media/bukkake.jpg
  31. BIN
      media/bukkake.piv
  32. BIN
      media/bukkake0.bmp
  33. BIN
      media/butterfly.jpg
  34. BIN
      media/butterfly.piv
  35. BIN
      media/butterfly0.bmp
  36. BIN
      media/cat.jpg
  37. BIN
      media/cat.piv
  38. BIN
      media/cat0.bmp
  39. BIN
      media/circle-jeck.jpg
  40. BIN
      media/circle-jeck0.bmp
  41. BIN
      media/circle-jerk.piv
  42. BIN
      media/cowboy.jpg
  43. BIN
      media/cowboy.piv
  44. BIN
      media/cowboy0.bmp
  45. BIN
      media/cowgirl.jpg
  46. BIN
      media/cowgirl.piv
  47. BIN
      media/cowgirl0.bmp
  48. BIN
      media/daisy-chain.jpg
  49. BIN
      media/daisy-chain.piv
  50. BIN
      media/daisy-chain0.bmp
  51. BIN
      media/dance.jpg
  52. BIN
      media/dance.piv
  53. BIN
      media/dance0.bmp
  54. BIN
      media/dance2.gif
  55. BIN
      media/dance2.piv
  56. BIN
      media/dance20.bmp
  57. BIN
      media/doggy-style.jpg
  58. BIN
      media/doggy-style.piv
  59. BIN
      media/doggy-style0.bmp
  60. BIN
      media/double-penetration.jpg
  61. BIN
      media/double-penetration.piv
  62. BIN
      media/double-penetration0.bmp
  63. BIN
      media/elephant.jpg
  64. BIN
      media/elephant.piv
  65. BIN
      media/elephant0.bmp
  66. BIN
      media/ffm-threesome.piv
  67. BIN
      media/ffm-threesome0.bmp
  68. BIN
      media/frot.jpg
  69. BIN
      media/frot.piv
  70. BIN
      media/frot0.bmp
  71. BIN
      media/horse-icon.gif
  72. BIN
      media/horse.jpg
  73. BIN
      media/horse.piv
  74. BIN
      media/horse0.bmp
  75. BIN
      media/icon.piv
  76. BIN
      media/icon0.bmp
  77. BIN
      media/kiss-logo.gif
  78. BIN
      media/kiss.piv
  79. BIN
      media/kiss0.bmp
  80. BIN
      media/kneeling.jpg
  81. BIN
      media/kneeling.piv
  82. BIN
      media/kneeling0.bmp
  83. BIN
      media/lateral.jpg
  84. BIN
      media/lateral.piv
  85. BIN
      media/lateral0.bmp
  86. BIN
      media/lipstick-director.jpg
  87. BIN
      media/lipstick-director.piv
  88. BIN
      media/lipstick-director0.bmp
  89. BIN
      media/lotus.jpg
  90. BIN
      media/lotus.piv
  91. BIN
      media/lotus0.bmp
  92. BIN
      media/missionary.jpg
  93. BIN
      media/missionary.piv
  94. BIN
      media/missionary0.bmp
  95. BIN
      media/mmf-threesome.piv
  96. BIN
      media/mmf-threesome0.bmp
  97. BIN
      media/oyster.jpg
  98. BIN
      media/oyster.piv
  99. BIN
      media/oyster0.bmp
  100. BIN
      media/reverse-cowgirl.jpg
  101. Some files were not shown because too many files have changed in this diff Show More

@ -0,0 +1,15 @@
Copyright (C) 2008 Neil Edelman
Sex is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Sex (see gnu.txt.) If not, see
<http://www.gnu.org/licenses/>.

@ -0,0 +1,465 @@
<?php
// Copyright 2004-2008 Facebook. All Rights Reserved.
//
// +---------------------------------------------------------------------------+
// | Facebook Platform PHP5 client |
// +---------------------------------------------------------------------------+
// | Copyright (c) 2007 Facebook, Inc. |
// | All rights reserved. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | 1. Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | 2. Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
// | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
// | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
// | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
// | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
// | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
// +---------------------------------------------------------------------------+
// | For help with this library, contact developers-help@facebook.com |
// +---------------------------------------------------------------------------+
//
include_once 'facebookapi_php5_restlib.php';
define('FACEBOOK_API_VALIDATION_ERROR', 1);
class Facebook {
public $api_client;
public $api_key;
public $secret;
public $generate_session_secret;
public $session_expires;
public $fb_params;
public $user;
public $profile_user;
public $canvas_user;
/*
* Create a Facebook client like this:
*
* $fb = new Facebook(API_KEY, SECRET);
*
* This will automatically pull in any parameters, validate them against the
* session signature, and chuck them in the public $fb_params member variable.
*
* @param api_key your Developer API key
* @param secret your Developer API secret
* @param generate_session_secret whether to automatically generate a session
* if the user doesn't have one, but
* there is an auth token present in the url,
*/
public function __construct($api_key, $secret, $generate_session_secret=false) {
$this->api_key = $api_key;
$this->secret = $secret;
$this->generate_session_secret = $generate_session_secret;
$this->api_client = new FacebookRestClient($api_key, $secret);
$this->validate_fb_params();
if (isset($this->fb_params['friends'])) {
$this->api_client->friends_list = explode(',', $this->fb_params['friends']);
}
if (isset($this->fb_params['added'])) {
$this->api_client->added = $this->fb_params['added'];
}
if (isset($this->fb_params['canvas_user'])) {
$this->api_client->canvas_user = $this->fb_params['canvas_user'];
}
}
/*
* Validates that the parameters passed in were sent from Facebook. It does so
* by validating that the signature matches one that could only be generated
* by using your application's secret key.
*
* Facebook-provided parameters will come from $_POST, $_GET, or $_COOKIE,
* in that order. $_POST and $_GET are always more up-to-date than cookies,
* so we prefer those if they are available.
*
* For nitty-gritty details of when each of these is used, check out
* http://wiki.developers.facebook.com/index.php/Verifying_The_Signature
*
* @param bool resolve_auth_token convert an auth token into a session
*/
public function validate_fb_params($resolve_auth_token=true) {
$this->fb_params = $this->get_valid_fb_params($_POST, 48*3600, 'fb_sig');
// note that with preload FQL, it's possible to receive POST params in
// addition to GET, so use a different prefix to differentiate them
if (!$this->fb_params) {
$fb_params = $this->get_valid_fb_params($_GET, 48*3600, 'fb_sig');
$fb_post_params = $this->get_valid_fb_params($_POST, 48*3600, 'fb_post_sig');
$this->fb_params = array_merge($fb_params, $fb_post_params);
}
// Okay, something came in via POST or GET
if ($this->fb_params) {
$user = isset($this->fb_params['user']) ?
$this->fb_params['user'] : null;
$this->profile_user = isset($this->fb_params['profile_user']) ?
$this->fb_params['profile_user'] : null;
$this->canvas_user = isset($this->fb_params['canvas_user']) ?
$this->fb_params['canvas_user'] : null;
if (isset($this->fb_params['session_key'])) {
$session_key = $this->fb_params['session_key'];
} else if (isset($this->fb_params['profile_session_key'])) {
$session_key = $this->fb_params['profile_session_key'];
} else {
$session_key = null;
}
$expires = isset($this->fb_params['expires']) ?
$this->fb_params['expires'] : null;
$this->set_user($user,
$session_key,
$expires);
}
// if no Facebook parameters were found in the GET or POST variables,
// then fall back to cookies, which may have cached user information
// Cookies are also used to receive session data via the Javascript API
else if ($cookies =
$this->get_valid_fb_params($_COOKIE, null, $this->api_key)) {
// use $api_key . '_' as a prefix for the cookies in case there are
// multiple facebook clients on the same domain.
$expires = isset($cookies['expires']) ? $cookies['expires'] : null;
$this->set_user($cookies['user'],
$cookies['session_key'],
$expires);
}
// finally, if we received no parameters, but the 'auth_token' GET var
// is present, then we are in the middle of auth handshake,
// so go ahead and create the session
else if ($resolve_auth_token && isset($_GET['auth_token']) &&
$session = $this->do_get_session($_GET['auth_token'])) {
if ($this->generate_session_secret &&
!empty($session['secret'])) {
$session_secret = $session['secret'];
}
$this->set_user($session['uid'],
$session['session_key'],
$session['expires'],
$session_secret ? $session_secret : null);
}
return !empty($this->fb_params);
}
// Store a temporary session secret for the current session
// for use with the JS client library
public function promote_session() {
try {
$session_secret = $this->api_client->auth_promoteSession();
if (!$this->in_fb_canvas()) {
$this->set_cookies($this->user, $this->api_client->session_key, $this->session_expires, $session_secret);
}
return $session_secret;
} catch (FacebookRestClientException $e) {
// API_EC_PARAM means we don't have a logged in user, otherwise who
// knows what it means, so just throw it.
if ($e->getCode() != FacebookAPIErrorCodes::API_EC_PARAM) {
throw $e;
}
}
}
public function do_get_session($auth_token) {
try {
return $this->api_client->auth_getSession($auth_token, $this->generate_session_secret);
} catch (FacebookRestClientException $e) {
// API_EC_PARAM means we don't have a logged in user, otherwise who
// knows what it means, so just throw it.
if ($e->getCode() != FacebookAPIErrorCodes::API_EC_PARAM) {
throw $e;
}
}
}
// Invalidate the session currently being used, and clear any state associated with it
public function expire_session() {
if ($this->api_client->auth_expireSession()) {
if (!$this->in_fb_canvas() && isset($_COOKIE[$this->api_key . '_user'])) {
$cookies = array('user', 'session_key', 'expires', 'ss');
foreach ($cookies as $name) {
setcookie($this->api_key . '_' . $name, false, time() - 3600);
unset($_COOKIE[$this->api_key . '_' . $name]);
}
setcookie($this->api_key, false, time() - 3600);
unset($_COOKIE[$this->api_key]);
}
// now, clear the rest of the stored state
$this->user = 0;
$this->api_client->session_key = 0;
return true;
} else {
return false;
}
}
public function redirect($url) {
if ($this->in_fb_canvas()) {
echo '<fb:redirect url="' . $url . '"/>';
} else if (preg_match('/^https?:\/\/([^\/]*\.)?facebook\.com(:\d+)?/i', $url)) {
// make sure facebook.com url's load in the full frame so that we don't
// get a frame within a frame.
echo "<script type=\"text/javascript\">\ntop.location.href = \"$url\";\n</script>";
} else {
header('Location: ' . $url);
}
exit;
}
public function in_frame() {
return isset($this->fb_params['in_canvas']) || isset($this->fb_params['in_iframe']);
}
public function in_fb_canvas() {
return isset($this->fb_params['in_canvas']);
}
public function get_loggedin_user() {
return $this->user;
}
public function get_canvas_user() {
return $this->canvas_user;
}
public function get_profile_user() {
return $this->profile_user;
}
public static function current_url() {
return 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
}
// require_add and require_install have been removed.
// see http://developer.facebook.com/news.php?blog=1&story=116 for more details
public function require_login() {
if ($user = $this->get_loggedin_user()) {
return $user;
}
$this->redirect($this->get_login_url(self::current_url(), $this->in_frame()));
}
public function require_frame() {
if (!$this->in_frame()) {
$this->redirect($this->get_login_url(self::current_url(), true));
}
}
public static function get_facebook_url($subdomain='www') {
return 'http://' . $subdomain . '.new.facebook.com';
}
public function get_install_url($next=null) {
// this was renamed, keeping for compatibility's sake
return $this->get_add_url($next);
}
public function get_add_url($next=null) {
return self::get_facebook_url().'/add.php?api_key='.$this->api_key .
($next ? '&next=' . urlencode($next) : '');
}
public function get_login_url($next, $canvas) {
return self::get_facebook_url().'/login.php?v=1.0&api_key=' . $this->api_key .
($next ? '&next=' . urlencode($next) : '') .
($canvas ? '&canvas' : '');
}
public function set_user($user, $session_key, $expires=null, $session_secret=null) {
if (!$this->in_fb_canvas() && (!isset($_COOKIE[$this->api_key . '_user'])
|| $_COOKIE[$this->api_key . '_user'] != $user)) {
$this->set_cookies($user, $session_key, $expires, $session_secret);
}
$this->user = $user;
$this->api_client->session_key = $session_key;
$this->session_expires = $expires;
}
public function set_cookies($user, $session_key, $expires=null, $session_secret=null) {
$cookies = array();
$cookies['user'] = $user;
$cookies['session_key'] = $session_key;
if ($expires != null) {
$cookies['expires'] = $expires;
}
if ($session_secret != null) {
$cookies['ss'] = $session_secret;
}
foreach ($cookies as $name => $val) {
setcookie($this->api_key . '_' . $name, $val, (int)$expires);
$_COOKIE[$this->api_key . '_' . $name] = $val;
}
$sig = self::generate_sig($cookies, $this->secret);
setcookie($this->api_key, $sig, (int)$expires);
$_COOKIE[$this->api_key] = $sig;
}
/**
* Tries to undo the badness of magic quotes as best we can
* @param string $val Should come directly from $_GET, $_POST, etc.
* @return string val without added slashes
*/
public static function no_magic_quotes($val) {
if (get_magic_quotes_gpc()) {
return stripslashes($val);
} else {
return $val;
}
}
/*
* Get the signed parameters that were sent from Facebook. Validates the set
* of parameters against the included signature.
*
* Since Facebook sends data to your callback URL via unsecured means, the
* signature is the only way to make sure that the data actually came from
* Facebook. So if an app receives a request at the callback URL, it should
* always verify the signature that comes with against your own secret key.
* Otherwise, it's possible for someone to spoof a request by
* pretending to be someone else, i.e.:
* www.your-callback-url.com/?fb_user=10101
*
* This is done automatically by verify_fb_params.
*
* @param assoc $params a full array of external parameters.
* presumed $_GET, $_POST, or $_COOKIE
* @param int $timeout number of seconds that the args are good for.
* Specifically good for forcing cookies to expire.
* @param string $namespace prefix string for the set of parameters we want
* to verify. i.e., fb_sig or fb_post_sig
*
* @return assoc the subset of parameters containing the given prefix,
* and also matching the signature associated with them.
* OR an empty array if the params do not validate
*/
public function get_valid_fb_params($params, $timeout=null, $namespace='fb_sig') {
$prefix = $namespace . '_';
$prefix_len = strlen($prefix);
$fb_params = array();
if (empty($params)) {
return array();
}
foreach ($params as $name => $val) {
// pull out only those parameters that match the prefix
// note that the signature itself ($params[$namespace]) is not in the list
if (strpos($name, $prefix) === 0) {
$fb_params[substr($name, $prefix_len)] = self::no_magic_quotes($val);
}
}
// validate that the request hasn't expired. this is most likely
// for params that come from $_COOKIE
if ($timeout && (!isset($fb_params['time']) || time() - $fb_params['time'] > $timeout)) {
return array();
}
// validate that the params match the signature
$signature = isset($params[$namespace]) ? $params[$namespace] : null;
if (!$signature || (!$this->verify_signature($fb_params, $signature))) {
return array();
}
return $fb_params;
}
/*
* Validates that a given set of parameters match their signature.
* Parameters all match a given input prefix, such as "fb_sig".
*
* @param $fb_params an array of all Facebook-sent parameters,
* not including the signature itself
* @param $expected_sig the expected result to check against
*/
public function verify_signature($fb_params, $expected_sig) {
return self::generate_sig($fb_params, $this->secret) == $expected_sig;
}
/*
* Generate a signature using the application secret key.
*
* The only two entities that know your secret key are you and Facebook,
* according to the Terms of Service. Since nobody else can generate
* the signature, you can rely on it to verify that the information
* came from Facebook.
*
* @param $params_array an array of all Facebook-sent parameters,
* NOT INCLUDING the signature itself
* @param $secret your app's secret key
*
* @return a hash to be checked against the signature provided by Facebook
*/
public static function generate_sig($params_array, $secret) {
$str = '';
ksort($params_array);
// Note: make sure that the signature parameter is not already included in
// $params_array.
foreach ($params_array as $k=>$v) {
$str .= "$k=$v";
}
$str .= $secret;
return md5($str);
}
public function encode_validationError($summary, $message) {
return json_encode(
array('errorCode' => FACEBOOK_API_VALIDATION_ERROR,
'errorTitle' => $summary,
'errorMessage' => $message));
}
public function encode_multiFeedStory($feed, $next) {
return json_encode(
array('method' => 'multiFeedStory',
'content' =>
array('next' => $next,
'feed' => $feed)));
}
public function encode_feedStory($feed, $next) {
return json_encode(
array('method' => 'feedStory',
'content' =>
array('next' => $next,
'feed' => $feed)));
}
public function create_templatizedFeedStory($title_template, $title_data=array(),
$body_template='', $body_data = array(), $body_general=null,
$image_1=null, $image_1_link=null,
$image_2=null, $image_2_link=null,
$image_3=null, $image_3_link=null,
$image_4=null, $image_4_link=null) {
return array('title_template'=> $title_template,
'title_data' => $title_data,
'body_template'=> $body_template,
'body_data' => $body_data,
'body_general' => $body_general,
'image_1' => $image_1,
'image_1_link' => $image_1_link,
'image_2' => $image_2,
'image_2_link' => $image_2_link,
'image_3' => $image_3,
'image_3_link' => $image_3_link,
'image_4' => $image_4,
'image_4_link' => $image_4_link);
}
}

@ -0,0 +1,104 @@
<?php
// Copyright 2004-2008 Facebook. All Rights Reserved.
//
// +---------------------------------------------------------------------------+
// | Facebook Platform PHP5 client |
// +---------------------------------------------------------------------------+
// | Copyright (c) 2007 Facebook, Inc. |
// | All rights reserved. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | 1. Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | 2. Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
// | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
// | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
// | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
// | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
// | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
// +---------------------------------------------------------------------------+
// | For help with this library, contact developers-help@facebook.com |
// +---------------------------------------------------------------------------+
//
/**
* This class extends and modifies the "Facebook" class to better
* suit desktop apps.
*/
class FacebookDesktop extends Facebook {
// the application secret, which differs from the session secret
public $app_secret;
public $verify_sig;
public function __construct($api_key, $secret) {
$this->app_secret = $secret;
$this->verify_sig = false;
parent::__construct($api_key, $secret);
}
public function do_get_session($auth_token) {
$this->api_client->secret = $this->app_secret;
$this->api_client->session_key = null;
$session_info = parent::do_get_session($auth_token);
if (!empty($session_info['secret'])) {
// store the session secret
$this->set_session_secret($session_info['secret']);
}
return $session_info;
}
public function set_session_secret($session_secret) {
$this->secret = $session_secret;
$this->api_client->secret = $session_secret;
}
public function require_login() {
if ($this->get_loggedin_user()) {
try {
// try a session-based API call to ensure that we have the correct
// session secret
$user = $this->api_client->users_getLoggedInUser();
// now that we have a valid session secret, verify the signature
$this->verify_sig = true;
if ($this->validate_fb_params(false)) {
return $user;
} else {
// validation failed
return null;
}
} catch (FacebookRestClientException $ex) {
if (isset($_GET['auth_token'])) {
// if we have an auth_token, use it to establish a session
$session_info = $this->do_get_session($_GET['auth_token']);
if ($session_info) {
return $session_info['uid'];
}
}
}
}
// if we get here, we need to redirect the user to log in
$this->redirect($this->get_login_url(self::current_url(), $this->in_fb_canvas()));
}
public function verify_signature($fb_params, $expected_sig) {
// we don't want to verify the signature until we have a valid
// session secret
if ($this->verify_sig) {
return parent::verify_signature($fb_params, $expected_sig);
} else {
return true;
}
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,806 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* Converts to and from JSON format.
*
* JSON (JavaScript Object Notation) is a lightweight data-interchange
* format. It is easy for humans to read and write. It is easy for machines
* to parse and generate. It is based on a subset of the JavaScript
* Programming Language, Standard ECMA-262 3rd Edition - December 1999.
* This feature can also be found in Python. JSON is a text format that is
* completely language independent but uses conventions that are familiar
* to programmers of the C-family of languages, including C, C++, C#, Java,
* JavaScript, Perl, TCL, and many others. These properties make JSON an
* ideal data-interchange language.
*
* This package provides a simple encoder and decoder for JSON notation. It
* is intended for use with client-side Javascript applications that make
* use of HTTPRequest to perform server communication functions - data can
* be encoded into JSON notation for use in a client-side javascript, or
* decoded from incoming Javascript requests. JSON format is native to
* Javascript, and can be directly eval()'ed with no further parsing
* overhead
*
* All strings should be in ASCII or UTF-8 format!
*
* LICENSE: Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met: Redistributions of source code must retain the
* above copyright notice, this list of conditions and the following
* disclaimer. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* @category
* @package Services_JSON
* @author Michal Migurski <mike-json@teczno.com>
* @author Matt Knapp <mdknapp[at]gmail[dot]com>
* @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
* @copyright 2005 Michal Migurski
* @version CVS: $Id: JSON.php,v 1.31 2006/06/28 05:54:17 migurski Exp $
* @license http://www.opensource.org/licenses/bsd-license.php
* @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198
*/
/**
* Marker constant for Services_JSON::decode(), used to flag stack state
*/
define('SERVICES_JSON_SLICE', 1);
/**
* Marker constant for Services_JSON::decode(), used to flag stack state
*/
define('SERVICES_JSON_IN_STR', 2);
/**
* Marker constant for Services_JSON::decode(), used to flag stack state
*/
define('SERVICES_JSON_IN_ARR', 3);
/**
* Marker constant for Services_JSON::decode(), used to flag stack state
*/
define('SERVICES_JSON_IN_OBJ', 4);
/**
* Marker constant for Services_JSON::decode(), used to flag stack state
*/
define('SERVICES_JSON_IN_CMT', 5);
/**
* Behavior switch for Services_JSON::decode()
*/
define('SERVICES_JSON_LOOSE_TYPE', 16);
/**
* Behavior switch for Services_JSON::decode()
*/
define('SERVICES_JSON_SUPPRESS_ERRORS', 32);
/**
* Converts to and from JSON format.
*
* Brief example of use:
*
* <code>
* // create a new instance of Services_JSON
* $json = new Services_JSON();
*
* // convert a complexe value to JSON notation, and send it to the browser
* $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4)));
* $output = $json->encode($value);
*
* print($output);
* // prints: ["foo","bar",[1,2,"baz"],[3,[4]]]
*
* // accept incoming POST data, assumed to be in JSON notation
* $input = file_get_contents('php://input', 1000000);
* $value = $json->decode($input);
* </code>
*/
class Services_JSON
{
/**
* constructs a new JSON instance
*
* @param int $use object behavior flags; combine with boolean-OR
*
* possible values:
* - SERVICES_JSON_LOOSE_TYPE: loose typing.
* "{...}" syntax creates associative arrays
* instead of objects in decode().
* - SERVICES_JSON_SUPPRESS_ERRORS: error suppression.
* Values which can't be encoded (e.g. resources)
* appear as NULL instead of throwing errors.
* By default, a deeply-nested resource will
* bubble up with an error, so all return values
* from encode() should be checked with isError()
*/
function Services_JSON($use = 0)
{
$this->use = $use;
}
/**
* convert a string from one UTF-16 char to one UTF-8 char
*
* Normally should be handled by mb_convert_encoding, but
* provides a slower PHP-only method for installations
* that lack the multibye string extension.
*
* @param string $utf16 UTF-16 character
* @return string UTF-8 character
* @access private
*/
function utf162utf8($utf16)
{
// oh please oh please oh please oh please oh please
if(function_exists('mb_convert_encoding')) {
return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
}
$bytes = (ord($utf16{0}) << 8) | ord($utf16{1});
switch(true) {
case ((0x7F & $bytes) == $bytes):
// this case should never be reached, because we are in ASCII range
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return chr(0x7F & $bytes);
case (0x07FF & $bytes) == $bytes:
// return a 2-byte UTF-8 character
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return chr(0xC0 | (($bytes >> 6) & 0x1F))
. chr(0x80 | ($bytes & 0x3F));
case (0xFFFF & $bytes) == $bytes:
// return a 3-byte UTF-8 character
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return chr(0xE0 | (($bytes >> 12) & 0x0F))
. chr(0x80 | (($bytes >> 6) & 0x3F))
. chr(0x80 | ($bytes & 0x3F));
}
// ignoring UTF-32 for now, sorry
return '';
}
/**
* convert a string from one UTF-8 char to one UTF-16 char
*
* Normally should be handled by mb_convert_encoding, but
* provides a slower PHP-only method for installations
* that lack the multibye string extension.
*
* @param string $utf8 UTF-8 character
* @return string UTF-16 character
* @access private
*/
function utf82utf16($utf8)
{
// oh please oh please oh please oh please oh please
if(function_exists('mb_convert_encoding')) {
return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
}
switch(strlen($utf8)) {
case 1:
// this case should never be reached, because we are in ASCII range
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return $utf8;
case 2:
// return a UTF-16 character from a 2-byte UTF-8 char
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return chr(0x07 & (ord($utf8{0}) >> 2))
. chr((0xC0 & (ord($utf8{0}) << 6))
| (0x3F & ord($utf8{1})));
case 3:
// return a UTF-16 character from a 3-byte UTF-8 char
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return chr((0xF0 & (ord($utf8{0}) << 4))
| (0x0F & (ord($utf8{1}) >> 2)))
. chr((0xC0 & (ord($utf8{1}) << 6))
| (0x7F & ord($utf8{2})));
}
// ignoring UTF-32 for now, sorry
return '';
}
/**
* encodes an arbitrary variable into JSON format
*
* @param mixed $var any number, boolean, string, array, or object to be encoded.
* see argument 1 to Services_JSON() above for array-parsing behavior.
* if var is a strng, note that encode() always expects it
* to be in ASCII or UTF-8 format!
*
* @return mixed JSON string representation of input var or an error if a problem occurs
* @access public
*/
function encode($var)
{
switch (gettype($var)) {
case 'boolean':
return $var ? 'true' : 'false';
case 'NULL':
return 'null';
case 'integer':
return (int) $var;
case 'double':
case 'float':
return (float) $var;
case 'string':
// STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
$ascii = '';
$strlen_var = strlen($var);
/*
* Iterate over every character in the string,
* escaping with a slash or encoding to UTF-8 where necessary
*/
for ($c = 0; $c < $strlen_var; ++$c) {
$ord_var_c = ord($var{$c});
switch (true) {
case $ord_var_c == 0x08:
$ascii .= '\b';
break;
case $ord_var_c == 0x09:
$ascii .= '\t';
break;
case $ord_var_c == 0x0A:
$ascii .= '\n';
break;
case $ord_var_c == 0x0C:
$ascii .= '\f';
break;
case $ord_var_c == 0x0D:
$ascii .= '\r';
break;
case $ord_var_c == 0x22:
case $ord_var_c == 0x2F:
case $ord_var_c == 0x5C:
// double quote, slash, slosh
$ascii .= '\\'.$var{$c};
break;
case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
// characters U-00000000 - U-0000007F (same as ASCII)
$ascii .= $var{$c};
break;
case (($ord_var_c & 0xE0) == 0xC0):
// characters U-00000080 - U-000007FF, mask 110XXXXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$char = pack('C*', $ord_var_c, ord($var{$c + 1}));
$c += 1;
$utf16 = $this->utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16));
break;
case (($ord_var_c & 0xF0) == 0xE0):
// characters U-00000800 - U-0000FFFF, mask 1110XXXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$char = pack('C*', $ord_var_c,
ord($var{$c + 1}),
ord($var{$c + 2}));
$c += 2;
$utf16 = $this->utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16));
break;
case (($ord_var_c & 0xF8) == 0xF0):
// characters U-00010000 - U-001FFFFF, mask 11110XXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$char = pack('C*', $ord_var_c,
ord($var{$c + 1}),
ord($var{$c + 2}),
ord($var{$c + 3}));
$c += 3;
$utf16 = $this->utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16));
break;
case (($ord_var_c & 0xFC) == 0xF8):
// characters U-00200000 - U-03FFFFFF, mask 111110XX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$char = pack('C*', $ord_var_c,
ord($var{$c + 1}),
ord($var{$c + 2}),
ord($var{$c + 3}),
ord($var{$c + 4}));
$c += 4;
$utf16 = $this->utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16));
break;
case (($ord_var_c & 0xFE) == 0xFC):
// characters U-04000000 - U-7FFFFFFF, mask 1111110X
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$char = pack('C*', $ord_var_c,
ord($var{$c + 1}),
ord($var{$c + 2}),
ord($var{$c + 3}),
ord($var{$c + 4}),
ord($var{$c + 5}));
$c += 5;
$utf16 = $this->utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16));
break;
}
}
return '"'.$ascii.'"';
case 'array':
/*
* As per JSON spec if any array key is not an integer
* we must treat the the whole array as an object. We
* also try to catch a sparsely populated associative
* array with numeric keys here because some JS engines
* will create an array with empty indexes up to
* max_index which can cause memory issues and because
* the keys, which may be relevant, will be remapped
* otherwise.
*
* As per the ECMA and JSON specification an object may
* have any string as a property. Unfortunately due to
* a hole in the ECMA specification if the key is a
* ECMA reserved word or starts with a digit the
* parameter is only accessible using ECMAScript's
* bracket notation.
*/
// treat as a JSON object
if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
$properties = array_map(array($this, 'name_value'),
array_keys($var),
array_values($var));
foreach($properties as $property) {
if(Services_JSON::isError($property)) {
return $property;
}
}
return '{' . join(',', $properties) . '}';
}
// treat it like a regular array
$elements = array_map(array($this, 'encode'), $var);
foreach($elements as $element) {
if(Services_JSON::isError($element)) {
return $element;
}
}
return '[' . join(',', $elements) . ']';
case 'object':
$vars = get_object_vars($var);
$properties = array_map(array($this, 'name_value'),
array_keys($vars),
array_values($vars));
foreach($properties as $property) {
if(Services_JSON::isError($property)) {
return $property;
}
}
return '{' . join(',', $properties) . '}';
default:
return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS)
? 'null'
: new Services_JSON_Error(gettype($var)." can not be encoded as JSON string");
}
}
/**
* array-walking function for use in generating JSON-formatted name-value pairs
*
* @param string $name name of key to use
* @param mixed $value reference to an array element to be encoded
*
* @return string JSON-formatted name-value pair, like '"name":value'
* @access private
*/
function name_value($name, $value)
{
$encoded_value = $this->encode($value);
if(Services_JSON::isError($encoded_value)) {
return $encoded_value;
}
return $this->encode(strval($name)) . ':' . $encoded_value;
}
/**
* reduce a string by removing leading and trailing comments and whitespace
*
* @param $str string string value to strip of comments and whitespace
*
* @return string string value stripped of comments and whitespace
* @access private
*/
function reduce_string($str)
{
$str = preg_replace(array(
// eliminate single line comments in '// ...' form
'#^\s*//(.+)$#m',
// eliminate multi-line comments in '/* ... */' form, at start of string
'#^\s*/\*(.+)\*/#Us',
// eliminate multi-line comments in '/* ... */' form, at end of string
'#/\*(.+)\*/\s*$#Us'
), '', $str);
// eliminate extraneous space
return trim($str);
}
/**
* decodes a JSON string into appropriate variable
*
* @param string $str JSON-formatted string
*
* @return mixed number, boolean, string, array, or object
* corresponding to given JSON input string.
* See argument 1 to Services_JSON() above for object-output behavior.
* Note that decode() always returns strings
* in ASCII or UTF-8 format!
* @access public
*/
function decode($str)
{
$str = $this->reduce_string($str);
switch (strtolower($str)) {
case 'true':
return true;
case 'false':
return false;
case 'null':
return null;
default:
$m = array();
if (is_numeric($str)) {
// Lookie-loo, it's a number
// This would work on its own, but I'm trying to be
// good about returning integers where appropriate:
// return (float)$str;
// Return float or int, as appropriate
return ((float)$str == (integer)$str)
? (integer)$str
: (float)$str;
} elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) {
// STRINGS RETURNED IN UTF-8 FORMAT
$delim = substr($str, 0, 1);
$chrs = substr($str, 1, -1);
$utf8 = '';
$strlen_chrs = strlen($chrs);
for ($c = 0; $c < $strlen_chrs; ++$c) {
$substr_chrs_c_2 = substr($chrs, $c, 2);
$ord_chrs_c = ord($chrs{$c});
switch (true) {
case $substr_chrs_c_2 == '\b':
$utf8 .= chr(0x08);
++$c;
break;
case $substr_chrs_c_2 == '\t':
$utf8 .= chr(0x09);
++$c;
break;
case $substr_chrs_c_2 == '\n':
$utf8 .= chr(0x0A);
++$c;
break;
case $substr_chrs_c_2 == '\f':
$utf8 .= chr(0x0C);
++$c;
break;
case $substr_chrs_c_2 == '\r':
$utf8 .= chr(0x0D);
++$c;
break;
case $substr_chrs_c_2 == '\\"':
case $substr_chrs_c_2 == '\\\'':
case $substr_chrs_c_2 == '\\\\':
case $substr_chrs_c_2 == '\\/':
if (($delim == '"' && $substr_chrs_c_2 != '\\\'') ||
($delim == "'" && $substr_chrs_c_2 != '\\"')) {
$utf8 .= $chrs{++$c};
}
break;
case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)):
// single, escaped unicode character
$utf16 = chr(hexdec(substr($chrs, ($c + 2), 2)))
. chr(hexdec(substr($chrs, ($c + 4), 2)));
$utf8 .= $this->utf162utf8($utf16);
$c += 5;
break;
case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):
$utf8 .= $chrs{$c};
break;
case ($ord_chrs_c & 0xE0) == 0xC0:
// characters U-00000080 - U-000007FF, mask 110XXXXX
//see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $c, 2);
++$c;
break;
case ($ord_chrs_c & 0xF0) == 0xE0:
// characters U-00000800 - U-0000FFFF, mask 1110XXXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $c, 3);
$c += 2;
break;
case ($ord_chrs_c & 0xF8) == 0xF0:
// characters U-00010000 - U-001FFFFF, mask 11110XXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $c, 4);
$c += 3;
break;
case ($ord_chrs_c & 0xFC) == 0xF8:
// characters U-00200000 - U-03FFFFFF, mask 111110XX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $c, 5);
$c += 4;
break;
case ($ord_chrs_c & 0xFE) == 0xFC:
// characters U-04000000 - U-7FFFFFFF, mask 1111110X
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $c, 6);
$c += 5;
break;
}
}
return $utf8;
} elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) {
// array, or object notation
if ($str{0} == '[') {
$stk = array(SERVICES_JSON_IN_ARR);
$arr = array();
} else {
if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
$stk = array(SERVICES_JSON_IN_OBJ);
$obj = array();
} else {
$stk = array(SERVICES_JSON_IN_OBJ);
$obj = new stdClass();
}
}
array_push($stk, array('what' => SERVICES_JSON_SLICE,
'where' => 0,
'delim' => false));
$chrs = substr($str, 1, -1);
$chrs = $this->reduce_string($chrs);
if ($chrs == '') {
if (reset($stk) == SERVICES_JSON_IN_ARR) {
return $arr;
} else {
return $obj;
}
}
//print("\nparsing {$chrs}\n");
$strlen_chrs = strlen($chrs);
for ($c = 0; $c <= $strlen_chrs; ++$c) {
$top = end($stk);
$substr_chrs_c_2 = substr($chrs, $c, 2);
if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) {
// found a comma that is not inside a string, array, etc.,
// OR we've reached the end of the character list
$slice = substr($chrs, $top['where'], ($c - $top['where']));
array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false));
//print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
if (reset($stk) == SERVICES_JSON_IN_ARR) {
// we are in an array, so just push an element onto the stack
array_push($arr, $this->decode($slice));
} elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
// we are in an object, so figure
// out the property name and set an
// element in an associative array,
// for now
$parts = array();
if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
// "name":value pair
$key = $this->decode($parts[1]);
$val = $this->decode($parts[2]);
if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
$obj[$key] = $val;
} else {
$obj->$key = $val;
}
} elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
// name:value pair, where name is unquoted
$key = $parts[1];
$val = $this->decode($parts[2]);
if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
$obj[$key] = $val;
} else {
$obj->$key = $val;
}
}
}
} elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) {
// found a quote, and we are not inside a string
array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c}));
//print("Found start of string at {$c}\n");
} elseif (($chrs{$c} == $top['delim']) &&
($top['what'] == SERVICES_JSON_IN_STR) &&
((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) {
// found a quote, we're in a string, and it's not escaped
// we know that it's not escaped becase there is _not_ an
// odd number of backslashes at the end of the string so far
array_pop($stk);
//print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n");
} elseif (($chrs{$c} == '[') &&
in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
// found a left-bracket, and we are in an array, object, or slice
array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false));
//print("Found start of array at {$c}\n");
} elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) {
// found a right-bracket, and we're in an array
array_pop($stk);
//print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
} elseif (($chrs{$c} == '{') &&
in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
// found a left-brace, and we are in an array, object, or slice
array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false));
//print("Found start of object at {$c}\n");
} elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) {
// found a right-brace, and we're in an object
array_pop($stk);
//print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
} elseif (($substr_chrs_c_2 == '/*') &&
in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
// found a comment start, and we are in an array, object, or slice
array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false));
$c++;
//print("Found start of comment at {$c}\n");
} elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) {
// found a comment end, and we're in one now
array_pop($stk);
$c++;
for ($i = $top['where']; $i <= $c; ++$i)
$chrs = substr_replace($chrs, ' ', $i, 1);
//print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
}
}
if (reset($stk) == SERVICES_JSON_IN_ARR) {
return $arr;
} elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
return $obj;
}
}
}
}
/**
* @todo Ultimately, this should just call PEAR::isError()
*/
function isError($data, $code = null)
{
if (class_exists('pear')) {
return PEAR::isError($data, $code);
} elseif (is_object($data) && (get_class($data) == 'services_json_error' ||
is_subclass_of($data, 'services_json_error'))) {
return true;
}
return false;
}
}
if (class_exists('PEAR_Error')) {
class Services_JSON_Error extends PEAR_Error
{
function Services_JSON_Error($message = 'unknown error', $code = null,
$mode = null, $options = null, $userinfo = null)
{
parent::PEAR_Error($message, $code, $mode, $options, $userinfo);
}
}
} else {
/**
* @todo Ultimately, this class shall be descended from PEAR_Error
*/
class Services_JSON_Error
{
function Services_JSON_Error($message = 'unknown error', $code = null,
$mode = null, $options = null, $userinfo = null)
{
}
}
}
?>

@ -0,0 +1,21 @@
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

@ -0,0 +1,6 @@
<?php
# In PHP 5.2 or higher we don't need to bring this in
if (!function_exists('json_encode')) {
require_once 'jsonwrapper_inner.php';
}
?>

@ -0,0 +1,23 @@
<?php
require_once 'JSON/JSON.php';
function json_encode($arg)
{
global $services_json;
if (!isset($services_json)) {
$services_json = new Services_JSON();
}
return $services_json->encode($arg);
}
function json_decode($arg)
{
global $services_json;
if (!isset($services_json)) {
$services_json = new Services_JSON();
}
return $services_json->decode($arg);
}
?>

@ -0,0 +1,6 @@
The contents of fb/ are copyright (c) 2004-2008 Facebook (All Rights
Reserved.) They are NOT part of Sex, but an integral part of the
communication with Facebook, and have been supplied by Facebook.
I have left them in because Sex doesn't work without them; I have no
connection to Facebook and they are part of Facebook's licence; see
individual files for license.

@ -0,0 +1,3 @@
default:
gcc -Wall -O3 -pedantic -ansi -fasm -fomit-frame-pointer -ffast-math -funroll-loops -o gifbar.cgi gifbar.c
chmod 755 gifbar.cgi

@ -0,0 +1,313 @@
/* I was looking though old code, and the one is genius, if a do say so myself.
I cleaned it up.
-Neil
Multi-byte fields are LSB first
header: 6 bytes (one header per data stream)
Signature: 3 bytes, 'GIF'
Version: 3 bytes, '87a' or '89a'
logical screen descriptor (one per data stream, follow header)
Logical screen width: 2 bytes
Logical screen height: 2 bytes
Packed Data: 1 byte
Global colour table flag: 1 bit (global table follows?)
Colour resolution - 1: 3 bits
Sort flag: 1 bit (important colours first? 0 in 87a)
Size of global colour table: 3 bits (size is 2^(x+1))
Background colour: 1 byte
Pixel aspect ratio: 1 byte (0 = ignore 0 in 87a)
global colour table: 3 * 2 ^ (size of table + 1) bytes if table flag
RGB: 3 bytes
[
image descriptor: (one per image)
Image separator: 1 byte 0x2C
Image left position: 2 bytes
Image top position: 2 bytes
Image width: 2 bytes
Image height: 2 bytes
Packed data:
Local colour table?: 1 bit
Interlace?: 1 bit
Sorted palette?: 1 bit
Reserved = 0: 2 bits
Local colour table size: 3 bits
local colour table: 3 * 2 ^ (size of table + 1) bytes if table flag
RGB: 3 bytes
code size: 1 byte
repeated data sub-blocks: terminated by zero-size sub-block
Size: 1 byte
Data: Size bytes
]
trailer: (one per data stream)
GIF trailer: 1 byte, 0x3B ';'
*/
#include <stdlib.h> /* getenv(), atoi() */
#include <stdio.h> /* printf(), putchar(), fwrite() */
#include <string.h> /* strcmp() */
typedef struct tagCode Code;
struct tagCode {
Code *child;
Code *next;
unsigned char suffix;
unsigned long code;
unsigned int code_size;
};
static void print_gif(unsigned char parm);
int main(int argc, char **argv) {
char *env;
if(!(env = getenv("REQUEST_METHOD"))
|| (strcmp(env, "get") && strcmp(env, "GET"))
|| !(env = getenv("QUERY_STRING"))) {
printf("Status: 400 Missing required argument.\n\n");
return EXIT_FAILURE;
}
printf("Content-type: image/gif\n\n");
/* may need to set STDOUT to binary mode to avoid line feed translation
if your OS sucks (you know who you are!)
if(setmode(stdout, O_BINARY) == -1) return 1; */
print_gif(atoi(env));
return EXIT_SUCCESS;
}
static void print_gif(unsigned char parm) {
/* this is the gif binary output; mine, you could use whatever and paste it
here from the hex editor, just make it 00-FF as it goes across the image */
static unsigned char image_data[] = {
0x08, 0xFE, 0x00, 0x5B, 0x38, 0x18, 0x80, 0xC0, 0x02, 0x8A, 0x18, 0x39,
0x8A, 0x20, 0xA9, 0xB1, 0xA1, 0xC2, 0x05, 0x0C, 0x16, 0x24, 0x38, 0x60,
0xD0, 0xE0, 0x41, 0x84, 0x09, 0x15, 0x34, 0x98, 0xE8, 0xC1, 0xA4, 0x09,
0x13, 0x23, 0x3A, 0x5E, 0x98, 0xF8, 0xD0, 0x01, 0x04, 0x8B, 0x24, 0x56,
0x9C, 0x10, 0x21, 0x72, 0x85, 0x4C, 0x19, 0x30, 0x54, 0x96, 0xF0, 0xA0,
0x81, 0x83, 0xC7, 0x8E, 0x21, 0x59, 0xB4, 0x14, 0xD9, 0x81, 0xE3, 0xC6,
0x0D, 0x1C, 0x39, 0x72, 0xE8, 0xD8, 0xD1, 0x83, 0x67, 0x8E, 0x1E, 0x3D,
0x74, 0x00, 0xE5, 0x11, 0x44, 0x88, 0x10, 0x22, 0x47, 0x8E, 0x00, 0xE1,
0xC1, 0xA3, 0xC7, 0x8F, 0x1F, 0x42, 0x9E, 0xB8, 0xC9, 0xA3, 0xA6, 0x4B,
0x95, 0x27, 0x4B, 0x9A, 0x40, 0xB9, 0xD2, 0x65, 0xCC, 0x18, 0x2E, 0x5F,
0x3B, 0x42, 0xB1, 0xD2, 0x45, 0x0C, 0x1A, 0x39, 0x7F, 0x18, 0x05, 0x22,
0xB3, 0xA5, 0x0B, 0x98, 0x32, 0x79, 0x22, 0x59, 0x42, 0x14, 0x67, 0x4D,
0x9B, 0x3C, 0x89, 0x1A, 0xED, 0x49, 0x43, 0x86, 0x4D, 0x20, 0x49, 0x96,
0x3A, 0x95, 0x2A, 0x95, 0x09, 0x50, 0x1C, 0x37, 0x6E, 0xDE, 0xC0, 0x89,
0x03, 0xE7, 0x8D, 0x65, 0x38, 0x74, 0xF2, 0x08, 0x7A, 0xE4, 0x89, 0x95,
0x2A, 0x4A, 0x83, 0x0C, 0x85, 0x8A, 0xC5, 0x8A, 0xD4, 0xA9, 0x59, 0xB6,
0x50, 0x71, 0x62, 0x65, 0xAB, 0x15, 0xAA, 0x55, 0xB2, 0x62, 0xB5, 0x5A,
0xC5, 0x2A, 0x96, 0xAE, 0x61, 0xC6, 0x90, 0x25, 0x43, 0x76, 0x2C, 0x19<