commit 44ffa35ce2b3946a0a5e8a8d7eda81173f82c62c Author: Neil Date: Fri Jun 11 15:46:44 2021 -0700 Added version control. diff --git a/copying.txt b/copying.txt new file mode 100644 index 0000000..c802cd5 --- /dev/null +++ b/copying.txt @@ -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 +. diff --git a/fb/facebook.php b/fb/facebook.php new file mode 100644 index 0000000..e014c33 --- /dev/null +++ b/fb/facebook.php @@ -0,0 +1,465 @@ +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 ''; + } 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 ""; + } 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); + } + + +} + diff --git a/fb/facebook_desktop.php b/fb/facebook_desktop.php new file mode 100644 index 0000000..90cdf66 --- /dev/null +++ b/fb/facebook_desktop.php @@ -0,0 +1,104 @@ +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; + } + } +} diff --git a/fb/facebookapi_php5_restlib.php b/fb/facebookapi_php5_restlib.php new file mode 100644 index 0000000..9decc6a --- /dev/null +++ b/fb/facebookapi_php5_restlib.php @@ -0,0 +1,2082 @@ +secret = $secret; + $this->session_key = $session_key; + $this->api_key = $api_key; + $this->batch_mode = FacebookRestClient::BATCH_MODE_DEFAULT; + $this->last_call_id = 0; + $this->call_as_apikey = ''; + $this->server_addr = Facebook::get_facebook_url('api') . '/restserver.php'; + if (!empty($GLOBALS['facebook_config']['debug'])) { + $this->cur_id = 0; + ?> + +batch_queue !== null) + { + throw new FacebookRestClientException(FacebookAPIErrorCodes::API_EC_BATCH_ALREADY_STARTED, + FacebookAPIErrorCodes::$api_error_descriptions[FacebookAPIErrorCodes::API_EC_BATCH_ALREADY_STARTED]); + } + + $this->batch_queue = array(); + } + + /* + * End current batch operation + */ + public function end_batch() { + if($this->batch_queue === null) { + throw new FacebookRestClientException(FacebookAPIErrorCodes::API_EC_BATCH_NOT_STARTED, + FacebookAPIErrorCodes::$api_error_descriptions[FacebookAPIErrorCodes::API_EC_BATCH_NOT_STARTED]); + } + + $this->execute_server_side_batch(); + + $this->batch_queue = null; + } + + + private function execute_server_side_batch() { + + + $item_count = count($this->batch_queue); + $method_feed = array(); + foreach($this->batch_queue as $batch_item) { + $method_feed[] = $this->create_post_string($batch_item['m'], $batch_item['p']); + } + + $method_feed_json = json_encode($method_feed); + + $serial_only = $this->batch_mode == FacebookRestClient::BATCH_MODE_SERIAL_ONLY ; + $params = array('method_feed' => $method_feed_json, 'serial_only' => $serial_only); + if ($this->call_as_apikey) { + $params['call_as_apikey'] = $this->call_as_apikey; + } + + $xml = $this->post_request('batch.run', $params); + + $result = $this->convert_xml_to_result($xml, 'batch.run', $params); + + + if (is_array($result) && isset($result['error_code'])) { + throw new FacebookRestClientException($result['error_msg'], $result['error_code']); + } + + for($i = 0; $i < $item_count; $i++) { + $batch_item = $this->batch_queue[$i]; + $batch_item_result_xml = $result[$i]; + $batch_item_result = $this->convert_xml_to_result($batch_item_result_xml, $batch_item['m'], $batch_item['p']); + + if (is_array($batch_item_result) && isset($batch_item_result['error_code'])) { + throw new FacebookRestClientException($batch_item_result['error_msg'], $batch_item_result['error_code']); + } + $batch_item['r'] = $batch_item_result; + } + } + + public function begin_permissions_mode($permissions_apikey) { + $this->call_as_apikey = $permissions_apikey; + } + + public function end_permissions_mode() { + $this->call_as_apikey = ''; + } + + /** + * Returns the session information available after current user logs in. + * @param string $auth_token the token returned by auth_createToken or + * passed back to your callback_url. + * @param bool $generate_session_secret whether the session returned should include a session secret + * + * @return assoc array containing session_key, uid + */ + public function auth_getSession($auth_token, $generate_session_secret=false) { + //Check if we are in batch mode + if($this->batch_queue === null) { + $result = $this->call_method('facebook.auth.getSession', + array('auth_token' => $auth_token, 'generate_session_secret' => $generate_session_secret)); + $this->session_key = $result['session_key']; + + if (!empty($result['secret']) && !$generate_session_secret) { + // desktop apps have a special secret + $this->secret = $result['secret']; + } + return $result; + } + } + + /** + * Generates a session specific secret. This is for integration with client-side API calls, such as the + * JS library. + * @error API_EC_PARAM_SESSION_KEY + * API_EC_PARAM_UNKNOWN + * @return session secret for the current promoted session + */ + public function auth_promoteSession() { + return $this->call_method('facebook.auth.promoteSession', array()); + } + + /** + * Expires the session that is currently being used. If this call is successful, no further calls to the + * API (which require a session) can be made until a valid session is created. + * + * @return bool true if session expiration was successful, false otherwise + */ + public function auth_expireSession() { + return $this->call_method('facebook.auth.expireSession', array()); + } + + /** + * Revokes the user's agreement to the Facebook Terms of Service for your application. If you call this + * method for one of your users, you will no longer be able to make API requests on their behalf until + * they again authorize your application. Use with care. + * + * @return bool true if revocation succeeds, false otherwise + */ + public function auth_revokeAuthorization() { + return $this->call_method('facebook.auth.revokeAuthorization', array()); + } + + /** + * Returns events according to the filters specified. + * @param int $uid Optional: User associated with events. + * A null parameter will default to the session user. + * @param array $eids Optional: Filter by these event ids. + * A null parameter will get all events for the user. + * @param int $start_time Optional: Filter with this unix time as lower bound. + * A null or zero parameter indicates no lower bound. + * @param int $end_time Optional: Filter with this UTC as upper bound. + * A null or zero parameter indicates no upper bound. + * @param string $rsvp_status Optional: Only show events where the given uid + * has this rsvp status. This only works if you have specified a value for + * $uid. Values are as in events.getMembers. Null indicates to ignore + * rsvp status when filtering. + * @return array of events + */ + public function &events_get($uid=null, $eids=null, $start_time=null, $end_time=null, $rsvp_status=null) { + return $this->call_method('facebook.events.get', + array( + 'uid' => $uid, + 'eids' => $eids, + 'start_time' => $start_time, + 'end_time' => $end_time, + 'rsvp_status' => $rsvp_status)); + } + + /** + * Returns membership list data associated with an event + * @param int $eid : event id + * @return assoc array of four membership lists, with keys 'attending', + * 'unsure', 'declined', and 'not_replied' + */ + public function &events_getMembers($eid) { + return $this->call_method('facebook.events.getMembers', + array('eid' => $eid)); + } + + /** + * RSVPs the current user + * @param int $eid : event id + * @param string $rsvp_status : 'attending', 'unsure', or 'declined' + * @return bool true if successful + */ + public function &events_rsvp($eid, $rsvp_status) { + return $this->call_method('facebook.events.rsvp', + array( + 'eid' => $eid, + 'rsvp_status' => $rsvp_status)); + } + + /** + * Cancels an event. Only works for events application is admin of. + * @param int $eid : event id + * @param string $cancel_message : (optional) message to send to members of the event about why it is cancelled + * @return bool true if successful + */ + public function &events_cancel($eid, $cancel_message) { + return $this->call_method('facebook.events.cancel', + array( + 'eid' => $eid, + 'cancel_message' => $cancel_message)); + } + + /** + * Creates an event on behalf of the user is there is a session, otherwise on behalf of app. + * Successful creation guarantees app will be admin. + * @param assoc array $event_info : json encoded event information + * @return int event id + */ + public function &events_create($event_info) { + return $this->call_method('facebook.events.create', + array('event_info' => $event_info)); + } + + /** + * Edits an existing event. Only works for events application is admin of. + * @param int $eid : event id + * @param assoc array $event_info : json encoded event information + * @return bool true if successful + */ + public function &events_edit($eid, $event_info) { + return $this->call_method('facebook.events.edit', + array( + 'eid' => $eid, + 'event_info' => $event_info)); + } + + /** + * Makes an FQL query. This is a generalized way of accessing all the data + * in the API, as an alternative to most of the other method calls. More + * info at http://developers.facebook.com/documentation.php?v=1.0&doc=fql + * @param string $query the query to evaluate + * @return generalized array representing the results + */ + public function &fql_query($query) { + return $this->call_method('facebook.fql.query', + array('query' => $query)); + } + + public function &feed_publishStoryToUser($title, $body, + $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 $this->call_method('facebook.feed.publishStoryToUser', + array('title' => $title, + 'body' => $body, + '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)); + } + + public function &feed_publishActionOfUser($title, $body, + $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 $this->call_method('facebook.feed.publishActionOfUser', + array('title' => $title, + 'body' => $body, + '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)); + } + + public function &feed_publishTemplatizedAction($title_template, $title_data, + $body_template, $body_data, $body_general, + $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, + $target_ids='', $page_actor_id=null) { + return $this->call_method('facebook.feed.publishTemplatizedAction', + 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, + 'target_ids' => $target_ids, + 'page_actor_id' => $page_actor_id)); + } + + public function &feed_registerTemplateBundle($one_line_story_templates, + $short_story_templates = array(), + $full_story_template = null) + { + $one_line_story_templates = json_encode($one_line_story_templates); + + if (!empty($short_story_templates)) { + $short_story_templates = json_encode($short_story_templates); + } + + if (isset($full_story_template)) { + $full_story_template = json_encode($full_story_template); + } + + return $this->call_method('facebook.feed.registerTemplateBundle', + array('one_line_story_templates' => $one_line_story_templates, + 'short_story_templates' => $short_story_templates, + 'full_story_template' => $full_story_template)); + } + + public function &feed_getRegisteredTemplateBundles() + { + return $this->call_method('facebook.feed.getRegisteredTemplateBundles', array()); + } + + public function &feed_getRegisteredTemplateBundleByID($template_bundle_id) + { + return $this->call_method('facebook.feed.getRegisteredTemplateBundleByID', + array('template_bundle_id' => $template_bundle_id)); + } + + public function &feed_deactivateTemplateBundleByID($template_bundle_id) + { + return $this->call_method('facebook.feed.deactivateTemplateBundleByID', + array('template_bundle_id' => $template_bundle_id)); + } + + public function &feed_publishUserAction($template_bundle_id, $template_data, + $target_ids='', $body_general='') + { + if (is_array($template_data)) { + $template_data = json_encode($template_data); + } // allow client to either pass in JSON or an assoc that we JSON for them + + if (is_array($target_ids)) { + $target_ids = json_encode($target_ids); + $target_ids = trim($target_ids, "[]"); // we don't want the square bracket delims + } + + return $this->call_method('facebook.feed.publishUserAction', + array('template_bundle_id' => $template_bundle_id, + 'template_data' => $template_data, + 'target_ids' => $target_ids, + 'body_general' => $body_general)); + } + + /** + * Returns whether or not pairs of users are friends. + * Note that the Facebook friend relationship is symmetric. + * @param array $uids1: array of ids (id_1, id_2,...) of some length X + * @param array $uids2: array of ids (id_A, id_B,...) of SAME length X + * @return array of uid pairs with bool, true if pair are friends, e.g. + * array( 0 => array('uid1' => id_1, 'uid2' => id_A, 'are_friends' => 1), + * 1 => array('uid1' => id_2, 'uid2' => id_B, 'are_friends' => 0) + * ...) + */ + public function &friends_areFriends($uids1, $uids2) { + return $this->call_method('facebook.friends.areFriends', + array('uids1'=>$uids1, 'uids2'=>$uids2)); + } + + /** + * Returns the friends of the current session user. + * @return array of friends + */ + public function &friends_get() { + if (isset($this->friends_list)) { + return $this->friends_list; + } + $params = array(); + if (isset($this->canvas_user)) { + $params['uid'] = $this->canvas_user; + } + return $this->call_method('facebook.friends.get', $params); + + } + + /** + * Returns the friends of the session user, who are also users + * of the calling application. + * @return array of friends + */ + public function &friends_getAppUsers() { + return $this->call_method('facebook.friends.getAppUsers', array()); + } + + /** + * Returns groups according to the filters specified. + * @param int $uid Optional: User associated with groups. + * A null parameter will default to the session user. + * @param array $gids Optional: group ids to query. + * A null parameter will get all groups for the user. + * @return array of groups + */ + public function &groups_get($uid, $gids) { + return $this->call_method('facebook.groups.get', + array( + 'uid' => $uid, + 'gids' => $gids)); + } + + /** + * Returns the membership list of a group + * @param int $gid : Group id + * @return assoc array of four membership lists, with keys + * 'members', 'admins', 'officers', and 'not_replied' + */ + public function &groups_getMembers($gid) { + return $this->call_method('facebook.groups.getMembers', + array('gid' => $gid)); + } + + /** + * Returns cookies according to the filters specified. + * @param int $uid Required: User for which the cookies are needed. + * @param string $name Optional: + * A null parameter will get all cookies for the user. + * @return array of cookies + */ + public function data_getCookies($uid, $name) { + return $this->call_method('facebook.data.getCookies', + array( + 'uid' => $uid, + 'name' => $name)); + } + + /** + * Sets cookies according to the params specified. + * @param int $uid Required: User for which the cookies are needed. + * @param string $name Required: name of the cookie + * @param string $value Optional if expires specified and is in the past + * @param int$expires Optional + * @param string $path Optional + * + * @return bool + */ + public function data_setCookie($uid, $name, $value, $expires, $path) { + return $this->call_method('facebook.data.setCookie', + array( + 'uid' => $uid, + 'name' => $name, + 'value' => $value, + 'expires' => $expires, + 'path' => $path)); + } + + /** + * Permissions API + */ + + /** + * Checks API-access granted by self to the specified application + * @param string $permissions_apikey: Required + * + * @return array: API methods/namespaces which are allowed access + */ + public function permissions_checkGrantedApiAccess($permissions_apikey) { + return $this->call_method('facebook.permissions.checkGrantedApiAccess', + array( + 'permissions_apikey' => $permissions_apikey)); + } + + /** + * Checks API-access granted to self by the specified application + * @param string $permissions_apikey: Required + * + * @return array: API methods/namespaces which are allowed access + */ + public function permissions_checkAvailableApiAccess($permissions_apikey) { + return $this->call_method('facebook.permissions.checkAvailableApiAccess', + array( + 'permissions_apikey' => $permissions_apikey)); + } + + /** + * Grant API-access to the specified methods/namespaces to the specified application + * @param string $permissions_apikey: Required + * @param array(string) : Optional: API methods/namespaces to be allowed + * + * @return array: API methods/namespaces which are allowed access + */ + public function permissions_grantApiAccess($permissions_apikey, $method_arr) { + return $this->call_method('facebook.permissions.grantApiAccess', + array( + 'permissions_apikey' => $permissions_apikey, + 'method_arr' => $method_arr)); + } + + /** + * Revoke API-access granted to the specified application + * @param string $permissions_apikey: Required + * + * @return bool + */ + public function permissions_revokeApiAccess($permissions_apikey) { + return $this->call_method('facebook.permissions.revokeApiAccess', + array( + 'permissions_apikey' => $permissions_apikey)); + } + + /** + * Returns the outstanding notifications for the session user. + * @return assoc array of + * notification count objects for 'messages', 'pokes' and 'shares', + * a uid list of 'friend_requests', a gid list of 'group_invites', + * and an eid list of 'event_invites' + */ + public function ¬ifications_get() { + return $this->call_method('facebook.notifications.get', array()); + } + + /** + * Sends a notification to the specified users. + * @return (nothing) + */ + public function ¬ifications_send($to_ids, $notification, $type) { + return $this->call_method('facebook.notifications.send', + array('to_ids' => $to_ids, 'notification' => $notification, 'type' => $type)); + } + + /** + * Sends an email to the specified user of the application. + * @param array $recipients : id of the recipients + * @param string $subject : subject of the email + * @param string $text : (plain text) body of the email + * @param string $fbml : fbml markup if you want an html version of the email + * @return comma separated list of successful recipients + */ + public function ¬ifications_sendEmail($recipients, $subject, $text, $fbml) { + return $this->call_method('facebook.notifications.sendEmail', + array('recipients' => $recipients, + 'subject' => $subject, + 'text' => $text, + 'fbml' => $fbml)); + } + + /** + * Returns the requested info fields for the requested set of pages + * @param array $page_ids an array of page ids + * @param array $fields an array of strings describing the info fields desired + * @param int $uid Optionally, limit results to pages of which this user is a fan. + * @param string type limits results to a particular type of page. + * @return array of pages + */ + public function &pages_getInfo($page_ids, $fields, $uid, $type) { + return $this->call_method('facebook.pages.getInfo', array('page_ids' => $page_ids, 'fields' => $fields, 'uid' => $uid, 'type' => $type)); + } + + /** + * Returns true if logged in user is an admin for the passed page + * @param int $page_id target page id + * @return boolean + */ + public function &pages_isAdmin($page_id) { + return $this->call_method('facebook.pages.isAdmin', array('page_id' => $page_id)); + } + + /** + * Returns whether or not the page corresponding to the current session object has the app installed + * @return boolean + */ + public function &pages_isAppAdded() { + if (isset($this->added)) { + return $this->added; + } + return $this->call_method('facebook.pages.isAppAdded', array()); + } + + /** + * Returns true if logged in user is a fan for the passed page + * @param int $page_id target page id + * @param int $uid user to compare. If empty, the logged in user. + * @return bool + */ + public function &pages_isFan($page_id, $uid) { + return $this->call_method('facebook.pages.isFan', array('page_id' => $page_id, 'uid' => $uid)); + } + + /** + * Returns photos according to the filters specified. + * @param int $subj_id Optional: Filter by uid of user tagged in the photos. + * @param int $aid Optional: Filter by an album, as returned by + * photos_getAlbums. + * @param array $pids Optional: Restrict to a list of pids + * Note that at least one of these parameters needs to be specified, or an + * error is returned. + * @return array of photo objects. + */ + public function &photos_get($subj_id, $aid, $pids) { + return $this->call_method('facebook.photos.get', + array('subj_id' => $subj_id, 'aid' => $aid, 'pids' => $pids)); + } + + /** + * Returns the albums created by the given user. + * @param int $uid Optional: the uid of the user whose albums you want. + * A null value will return the albums of the session user. + * @param array $aids Optional: a list of aids to restrict the query. + * Note that at least one of the (uid, aids) parameters must be specified. + * @returns an array of album objects. + */ + public function &photos_getAlbums($uid, $aids) { + return $this->call_method('facebook.photos.getAlbums', + array('uid' => $uid, + 'aids' => $aids)); + } + + /** + * Returns the tags on all photos specified. + * @param string $pids : a list of pids to query + * @return array of photo tag objects, with include pid, subject uid, + * and two floating-point numbers (xcoord, ycoord) for tag pixel location + */ + public function &photos_getTags($pids) { + return $this->call_method('facebook.photos.getTags', + array('pids' => $pids)); + } + + + /** + * Returns the requested info fields for the requested set of users + * @param array $uids an array of user ids + * @param array $fields an array of strings describing the info fields desired + * @return array of users + */ + public function &users_getInfo($uids, $fields) { + return $this->call_method('facebook.users.getInfo', array('uids' => $uids, 'fields' => $fields)); + } + + /** + * Returns the requested info fields for the requested set of users. No + * session key is required. Only data about users that have authorized + * your application will be returned. + * + * Check the wiki for fields that can be queried through this API call. + * Data returned from here should not be used for rendering to application + * users, use users.getInfo instead, so that proper privacy rules will be + * applied. + * @param array $uids an array of user ids + * @param array $fields an array of strings describing the info fields desired + * @return array of users + */ + public function &users_getStandardInfo($uids, $fields) { + return $this->call_method('facebook.users.getStandardInfo', array('uids' => $uids, 'fields' => $fields)); + } + + /** + * Returns the user corresponding to the current session object. + * @return integer uid + */ + public function &users_getLoggedInUser() { + return $this->call_method('facebook.users.getLoggedInUser', array()); + } + + /** + * Returns whether or not the user corresponding to the current session object has the give the app basic + * authorization. + * @return boolean + */ + public function &users_isAppUser($uid=null) { + if (isset($this->is_user)) { + return $this->is_user; + } + + return $this->call_method('facebook.users.isAppUser', array('uid' => $uid)); + } + + /** + * Returns whether or not the user corresponding to the current session object has the app installed + * @return boolean + */ + public function &users_isAppAdded($uid=null) { + if (isset($this->added)) { + return $this->added; + } + return $this->call_method('facebook.users.isAppAdded', array('uid' => $uid)); + } + + /** + * Sets the FBML for the profile of the user attached to this session + * @param string $markup The FBML that describes the profile presence of this app for the user + * @param int $uid The user + * @param string $profile Profile FBML + * @param string $profile_action Profile action FBML (deprecated) + * @param string $mobile_profile Mobile profile FBML + * @param string $profile_main Main Tab profile FBML + * @return array A list of strings describing any compile errors for the submitted FBML + */ + function profile_setFBML($markup, $uid = null, $profile='', $profile_action='', $mobile_profile='', $profile_main='') { + return $this->call_method('facebook.profile.setFBML', array('markup' => $markup, + 'uid' => $uid, + 'profile' => $profile, + 'profile_action' => $profile_action, + 'mobile_profile' => $mobile_profile, + 'profile_main' => $profile_main)); + } + + public function &profile_getFBML($uid, $type=null) { + return $this->call_method('facebook.profile.getFBML', array('uid' => $uid, + 'type' => $type)); + } + + public function &profile_getInfo($uid=null) { + return $this->call_method('facebook.profile.getInfo', array('uid' => $uid)); + } + + public function &profile_getInfoOptions($field) { + return $this->call_method('facebook.profile.getInfoOptions', + array('field' => $field)); + } + + public function profile_setInfoOptions($options, $field) { + return $this->call_method('facebook.profile.setInfoOptions', + array('options' => json_encode($options), + 'field' => $field)); + } + + public function &profile_setInfo($title, $type, $info_fields, $uid=null) { + return $this->call_method('facebook.profile.setInfo', + array('uid' => $uid, + 'type' => $type, + 'title' => $title, + 'info_fields' => json_encode($info_fields))); + } + + public function &profile_addInfoItems($info_items, $uid=null) { + return $this->call_method('facebook.profile.addInfoItems', + array('uid' => $uid, + 'info_items' => json_encode($info_items))); + } + + public function &profile_removeInfoItems($info_labels, $uid=null) { + return $this->call_method('facebook.profile.removeInfoItems', + array('uid' => $uid, + 'info_labels' => json_encode($info_labels))); + } + + + public function &fbml_refreshImgSrc($url) { + return $this->call_method('facebook.fbml.refreshImgSrc', array('url' => $url)); + } + + public function &fbml_refreshRefUrl($url) { + return $this->call_method('facebook.fbml.refreshRefUrl', array('url' => $url)); + } + + public function &fbml_setRefHandle($handle, $fbml) { + return $this->call_method('facebook.fbml.setRefHandle', array('handle' => $handle, 'fbml' => $fbml)); + } + + /** + * Get all the marketplace categories + * + * @return array A list of category names + */ + function marketplace_getCategories() { + return $this->call_method('facebook.marketplace.getCategories', array()); + } + + /** + * Get all the marketplace subcategories for a particular category + * + * @param category The category for which we are pulling subcategories + * @return array A list of subcategory names + */ + function marketplace_getSubCategories($category) { + return $this->call_method('facebook.marketplace.getSubCategories', array('category' => $category)); + } + + /** + * Get listings by either listing_id or user + * + * @param listing_ids An array of listing_ids (optional) + * @param uids An array of user ids (optional) + * @return array The data for matched listings + */ + function marketplace_getListings($listing_ids, $uids) { + return $this->call_method('facebook.marketplace.getListings', array('listing_ids' => $listing_ids, 'uids' => $uids)); + } + + /** + * Search for Marketplace listings. All arguments are optional, though at least + * one must be filled out to retrieve results. + * + * @param category The category in which to search (optional) + * @param subcategory The subcategory in which to search (optional) + * @param query A query string (optional) + * @return array The data for matched listings + */ + function marketplace_search($category, $subcategory, $query) { + return $this->call_method('facebook.marketplace.search', array('category' => $category, 'subcategory' => $subcategory, 'query' => $query)); + } + + /** + * Remove a listing from Marketplace + * + * @param listing_id The id of the listing to be removed + * @param status 'SUCCESS', 'NOT_SUCCESS', or 'DEFAULT' + * @return bool True on success + */ + function marketplace_removeListing($listing_id, $status='DEFAULT', $uid=null) { + return $this->call_method('facebook.marketplace.removeListing', + array('listing_id'=>$listing_id, + 'status'=>$status, + 'uid' => $uid)); + } + + /** + * Create/modify a Marketplace listing for the loggedinuser + * + * @param int listing_id The id of a listing to be modified, 0 for a new listing. + * @param show_on_profile bool Should we show this listing on the user's profile + * @param listing_attrs array An array of the listing data + * @return int The listing_id (unchanged if modifying an existing listing) + */ + function marketplace_createListing($listing_id, $show_on_profile, $attrs, $uid=null) { + return $this->call_method('facebook.marketplace.createListing', + array('listing_id'=>$listing_id, + 'show_on_profile'=>$show_on_profile, + 'listing_attrs'=>json_encode($attrs), + 'uid' => $uid)); + } + + + ///////////////////////////////////////////////////////////////////////////// + // Data Store API + + /** + * Set a user preference. + * + * @param pref_id preference identifier (0-200) + * @param value preferece's value + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_PARAM + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_setUserPreference($pref_id, $value) { + return $this->call_method + ('facebook.data.setUserPreference', + array('pref_id' => $pref_id, + 'value' => $value)); + } + + /** + * Set a user's all preferences for this application. + * + * @param values preferece values in an associative arrays + * @param replace whether to replace all existing preferences or + * merge into them. + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_PARAM + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_setUserPreferences($values, $replace = false) { + return $this->call_method + ('facebook.data.setUserPreferences', + array('values' => json_encode($values), + 'replace' => $replace)); + } + + /** + * Get a user preference. + * + * @param pref_id preference identifier (0-200) + * @return preference's value + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_PARAM + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_getUserPreference($pref_id) { + return $this->call_method + ('facebook.data.getUserPreference', + array('pref_id' => $pref_id)); + } + + /** + * Get a user preference. + * + * @return preference values + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_getUserPreferences() { + return $this->call_method + ('facebook.data.getUserPreferences', + array()); + } + + /** + * Create a new object type. + * + * @param name object type's name + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_DATA_OBJECT_ALREADY_EXISTS + * API_EC_PARAM + * API_EC_PERMISSION + * API_EC_DATA_INVALID_OPERATION + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_createObjectType($name) { + return $this->call_method + ('facebook.data.createObjectType', + array('name' => $name)); + } + + /** + * Delete an object type. + * + * @param obj_type object type's name + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_DATA_OBJECT_NOT_FOUND + * API_EC_PARAM + * API_EC_PERMISSION + * API_EC_DATA_INVALID_OPERATION + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_dropObjectType($obj_type) { + return $this->call_method + ('facebook.data.dropObjectType', + array('obj_type' => $obj_type)); + } + + /** + * Rename an object type. + * + * @param obj_type object type's name + * @param new_name new object type's name + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_DATA_OBJECT_NOT_FOUND + * API_EC_DATA_OBJECT_ALREADY_EXISTS + * API_EC_PARAM + * API_EC_PERMISSION + * API_EC_DATA_INVALID_OPERATION + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_renameObjectType($obj_type, $new_name) { + return $this->call_method + ('facebook.data.renameObjectType', + array('obj_type' => $obj_type, + 'new_name' => $new_name)); + } + + /** + * Add a new property to an object type. + * + * @param obj_type object type's name + * @param prop_name name of the property to add + * @param prop_type 1: integer; 2: string; 3: text blob + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_DATA_OBJECT_ALREADY_EXISTS + * API_EC_PARAM + * API_EC_PERMISSION + * API_EC_DATA_INVALID_OPERATION + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_defineObjectProperty($obj_type, $prop_name, $prop_type) { + return $this->call_method + ('facebook.data.defineObjectProperty', + array('obj_type' => $obj_type, + 'prop_name' => $prop_name, + 'prop_type' => $prop_type)); + } + + /** + * Remove a previously defined property from an object type. + * + * @param obj_type object type's name + * @param prop_name name of the property to remove + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_DATA_OBJECT_NOT_FOUND + * API_EC_PARAM + * API_EC_PERMISSION + * API_EC_DATA_INVALID_OPERATION + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_undefineObjectProperty($obj_type, $prop_name) { + return $this->call_method + ('facebook.data.undefineObjectProperty', + array('obj_type' => $obj_type, + 'prop_name' => $prop_name)); + } + + /** + * Rename a previously defined property of an object type. + * + * @param obj_type object type's name + * @param prop_name name of the property to rename + * @param new_name new name to use + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_DATA_OBJECT_NOT_FOUND + * API_EC_DATA_OBJECT_ALREADY_EXISTS + * API_EC_PARAM + * API_EC_PERMISSION + * API_EC_DATA_INVALID_OPERATION + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_renameObjectProperty($obj_type, $prop_name, + $new_name) { + return $this->call_method + ('facebook.data.renameObjectProperty', + array('obj_type' => $obj_type, + 'prop_name' => $prop_name, + 'new_name' => $new_name)); + } + + /** + * Retrieve a list of all object types that have defined for the application. + * + * @return a list of object type names + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_PERMISSION + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_getObjectTypes() { + return $this->call_method + ('facebook.data.getObjectTypes', + array()); + } + + /** + * Get definitions of all properties of an object type. + * + * @param obj_type object type's name + * @return pairs of property name and property types + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_PARAM + * API_EC_PERMISSION + * API_EC_DATA_OBJECT_NOT_FOUND + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_getObjectType($obj_type) { + return $this->call_method + ('facebook.data.getObjectType', + array('obj_type' => $obj_type)); + } + + /** + * Create a new object. + * + * @param obj_type object type's name + * @param properties (optional) properties to set initially + * @return newly created object's id + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_PARAM + * API_EC_PERMISSION + * API_EC_DATA_INVALID_OPERATION + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_createObject($obj_type, $properties = null) { + return $this->call_method + ('facebook.data.createObject', + array('obj_type' => $obj_type, + 'properties' => json_encode($properties))); + } + + /** + * Update an existing object. + * + * @param obj_id object's id + * @param properties new properties + * @param replace true for replacing existing properties; false for merging + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_DATA_OBJECT_NOT_FOUND + * API_EC_PARAM + * API_EC_PERMISSION + * API_EC_DATA_INVALID_OPERATION + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_updateObject($obj_id, $properties, $replace = false) { + return $this->call_method + ('facebook.data.updateObject', + array('obj_id' => $obj_id, + 'properties' => json_encode($properties), + 'replace' => $replace)); + } + + /** + * Delete an existing object. + * + * @param obj_id object's id + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_DATA_OBJECT_NOT_FOUND + * API_EC_PARAM + * API_EC_PERMISSION + * API_EC_DATA_INVALID_OPERATION + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_deleteObject($obj_id) { + return $this->call_method + ('facebook.data.deleteObject', + array('obj_id' => $obj_id)); + } + + /** + * Delete a list of objects. + * + * @param obj_ids objects to delete + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_PARAM + * API_EC_PERMISSION + * API_EC_DATA_INVALID_OPERATION + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_deleteObjects($obj_ids) { + return $this->call_method + ('facebook.data.deleteObjects', + array('obj_ids' => json_encode($obj_ids))); + } + + /** + * Get a single property value of an object. + * + * @param obj_id object's id + * @param prop_name individual property's name + * @return individual property's value + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_DATA_OBJECT_NOT_FOUND + * API_EC_PARAM + * API_EC_PERMISSION + * API_EC_DATA_INVALID_OPERATION + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_getObjectProperty($obj_id, $prop_name) { + return $this->call_method + ('facebook.data.getObjectProperty', + array('obj_id' => $obj_id, + 'prop_name' => $prop_name)); + } + + /** + * Get properties of an object. + * + * @param obj_id object's id + * @param prop_names (optional) properties to return; null for all. + * @return specified properties of an object + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_DATA_OBJECT_NOT_FOUND + * API_EC_PARAM + * API_EC_PERMISSION + * API_EC_DATA_INVALID_OPERATION + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_getObject($obj_id, $prop_names = null) { + return $this->call_method + ('facebook.data.getObject', + array('obj_id' => $obj_id, + 'prop_names' => json_encode($prop_names))); + } + + /** + * Get properties of a list of objects. + * + * @param obj_ids object ids + * @param prop_names (optional) properties to return; null for all. + * @return specified properties of an object + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_DATA_OBJECT_NOT_FOUND + * API_EC_PARAM + * API_EC_PERMISSION + * API_EC_DATA_INVALID_OPERATION + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_getObjects($obj_ids, $prop_names = null) { + return $this->call_method + ('facebook.data.getObjects', + array('obj_ids' => json_encode($obj_ids), + 'prop_names' => json_encode($prop_names))); + } + + /** + * Set a single property value of an object. + * + * @param obj_id object's id + * @param prop_name individual property's name + * @param prop_value new value to set + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_DATA_OBJECT_NOT_FOUND + * API_EC_PARAM + * API_EC_PERMISSION + * API_EC_DATA_INVALID_OPERATION + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_setObjectProperty($obj_id, $prop_name, + $prop_value) { + return $this->call_method + ('facebook.data.setObjectProperty', + array('obj_id' => $obj_id, + 'prop_name' => $prop_name, + 'prop_value' => $prop_value)); + } + + /** + * Read hash value by key. + * + * @param obj_type object type's name + * @param key hash key + * @param prop_name (optional) individual property's name + * @return hash value + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_PARAM + * API_EC_PERMISSION + * API_EC_DATA_INVALID_OPERATION + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_getHashValue($obj_type, $key, $prop_name = null) { + return $this->call_method + ('facebook.data.getHashValue', + array('obj_type' => $obj_type, + 'key' => $key, + 'prop_name' => $prop_name)); + } + + /** + * Write hash value by key. + * + * @param obj_type object type's name + * @param key hash key + * @param value hash value + * @param prop_name (optional) individual property's name + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_PARAM + * API_EC_PERMISSION + * API_EC_DATA_INVALID_OPERATION + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_setHashValue($obj_type, $key, $value, $prop_name = null) { + return $this->call_method + ('facebook.data.setHashValue', + array('obj_type' => $obj_type, + 'key' => $key, + 'value' => $value, + 'prop_name' => $prop_name)); + } + + /** + * Increase a hash value by specified increment atomically. + * + * @param obj_type object type's name + * @param key hash key + * @param prop_name individual property's name + * @param increment (optional) default is 1 + * @return incremented hash value + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_PARAM + * API_EC_PERMISSION + * API_EC_DATA_INVALID_OPERATION + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_incHashValue($obj_type, $key, $prop_name, $increment = 1) { + return $this->call_method + ('facebook.data.incHashValue', + array('obj_type' => $obj_type, + 'key' => $key, + 'prop_name' => $prop_name, + 'increment' => $increment)); + } + + /** + * Remove a hash key and its values. + * + * @param obj_type object type's name + * @param key hash key + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_PARAM + * API_EC_PERMISSION + * API_EC_DATA_INVALID_OPERATION + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_removeHashKey($obj_type, $key) { + return $this->call_method + ('facebook.data.removeHashKey', + array('obj_type' => $obj_type, + 'key' => $key)); + } + + /** + * Remove hash keys and their values. + * + * @param obj_type object type's name + * @param keys hash keys + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_PARAM + * API_EC_PERMISSION + * API_EC_DATA_INVALID_OPERATION + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_removeHashKeys($obj_type, $keys) { + return $this->call_method + ('facebook.data.removeHashKeys', + array('obj_type' => $obj_type, + 'keys' => json_encode($keys))); + } + + + /** + * Define an object association. + * + * @param name name of this association + * @param assoc_type 1: one-way 2: two-way symmetric 3: two-way asymmetric + * @param assoc_info1 needed info about first object type + * @param assoc_info2 needed info about second object type + * @param inverse (optional) name of reverse association + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_DATA_OBJECT_ALREADY_EXISTS + * API_EC_PARAM + * API_EC_PERMISSION + * API_EC_DATA_INVALID_OPERATION + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_defineAssociation($name, $assoc_type, $assoc_info1, + $assoc_info2, $inverse = null) { + return $this->call_method + ('facebook.data.defineAssociation', + array('name' => $name, + 'assoc_type' => $assoc_type, + 'assoc_info1' => json_encode($assoc_info1), + 'assoc_info2' => json_encode($assoc_info2), + 'inverse' => $inverse)); + } + + /** + * Undefine an object association. + * + * @param name name of this association + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_DATA_OBJECT_NOT_FOUND + * API_EC_PARAM + * API_EC_PERMISSION + * API_EC_DATA_INVALID_OPERATION + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_undefineAssociation($name) { + return $this->call_method + ('facebook.data.undefineAssociation', + array('name' => $name)); + } + + /** + * Rename an object association or aliases. + * + * @param name name of this association + * @param new_name (optional) new name of this association + * @param new_alias1 (optional) new alias for object type 1 + * @param new_alias2 (optional) new alias for object type 2 + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_DATA_OBJECT_ALREADY_EXISTS + * API_EC_DATA_OBJECT_NOT_FOUND + * API_EC_PARAM + * API_EC_PERMISSION + * API_EC_DATA_INVALID_OPERATION + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_renameAssociation($name, $new_name, $new_alias1 = null, + $new_alias2 = null) { + return $this->call_method + ('facebook.data.renameAssociation', + array('name' => $name, + 'new_name' => $new_name, + 'new_alias1' => $new_alias1, + 'new_alias2' => $new_alias2)); + } + + /** + * Get definition of an object association. + * + * @param name name of this association + * @return specified association + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_DATA_OBJECT_NOT_FOUND + * API_EC_PARAM + * API_EC_PERMISSION + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_getAssociationDefinition($name) { + return $this->call_method + ('facebook.data.getAssociationDefinition', + array('name' => $name)); + } + + /** + * Get definition of all associations. + * + * @return all defined associations + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_PERMISSION + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_getAssociationDefinitions() { + return $this->call_method + ('facebook.data.getAssociationDefinitions', + array()); + } + + /** + * Create or modify an association between two objects. + * + * @param name name of association + * @param obj_id1 id of first object + * @param obj_id2 id of second object + * @param data (optional) extra string data to store + * @param assoc_time (optional) extra time data; default to creation time + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_PARAM + * API_EC_PERMISSION + * API_EC_DATA_INVALID_OPERATION + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_setAssociation($name, $obj_id1, $obj_id2, $data = null, + $assoc_time = null) { + return $this->call_method + ('facebook.data.setAssociation', + array('name' => $name, + 'obj_id1' => $obj_id1, + 'obj_id2' => $obj_id2, + 'data' => $data, + 'assoc_time' => $assoc_time)); + } + + /** + * Create or modify associations between objects. + * + * @param assocs associations to set + * @param name (optional) name of association + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_PARAM + * API_EC_PERMISSION + * API_EC_DATA_INVALID_OPERATION + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_setAssociations($assocs, $name = null) { + return $this->call_method + ('facebook.data.setAssociations', + array('assocs' => json_encode($assocs), + 'name' => $name)); + } + + /** + * Remove an association between two objects. + * + * @param name name of association + * @param obj_id1 id of first object + * @param obj_id2 id of second object + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_DATA_OBJECT_NOT_FOUND + * API_EC_PARAM + * API_EC_PERMISSION + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_removeAssociation($name, $obj_id1, $obj_id2) { + return $this->call_method + ('facebook.data.removeAssociation', + array('name' => $name, + 'obj_id1' => $obj_id1, + 'obj_id2' => $obj_id2)); + } + + /** + * Remove associations between objects by specifying pairs of object ids. + * + * @param assocs associations to remove + * @param name (optional) name of association + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_DATA_OBJECT_NOT_FOUND + * API_EC_PARAM + * API_EC_PERMISSION + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_removeAssociations($assocs, $name = null) { + return $this->call_method + ('facebook.data.removeAssociations', + array('assocs' => json_encode($assocs), + 'name' => $name)); + } + + /** + * Remove associations between objects by specifying one object id. + * + * @param name name of association + * @param obj_id who's association to remove + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_DATA_OBJECT_NOT_FOUND + * API_EC_PARAM + * API_EC_PERMISSION + * API_EC_DATA_INVALID_OPERATION + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_removeAssociatedObjects($name, $obj_id) { + return $this->call_method + ('facebook.data.removeAssociatedObjects', + array('name' => $name, + 'obj_id' => $obj_id)); + } + + /** + * Retrieve a list of associated objects. + * + * @param name name of association + * @param obj_id who's association to retrieve + * @param no_data only return object ids + * @return associated objects + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_DATA_OBJECT_NOT_FOUND + * API_EC_PARAM + * API_EC_PERMISSION + * API_EC_DATA_INVALID_OPERATION + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_getAssociatedObjects($name, $obj_id, $no_data = true) { + return $this->call_method + ('facebook.data.getAssociatedObjects', + array('name' => $name, + 'obj_id' => $obj_id, + 'no_data' => $no_data)); + } + + /** + * Count associated objects. + * + * @param name name of association + * @param obj_id who's association to retrieve + * @return associated object's count + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_DATA_OBJECT_NOT_FOUND + * API_EC_PARAM + * API_EC_PERMISSION + * API_EC_DATA_INVALID_OPERATION + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_getAssociatedObjectCount($name, $obj_id) { + return $this->call_method + ('facebook.data.getAssociatedObjectCount', + array('name' => $name, + 'obj_id' => $obj_id)); + } + + /** + * Get a list of associated object counts. + * + * @param name name of association + * @param obj_ids whose association to retrieve + * @return associated object counts + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_DATA_OBJECT_NOT_FOUND + * API_EC_PARAM + * API_EC_PERMISSION + * API_EC_DATA_INVALID_OPERATION + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_getAssociatedObjectCounts($name, $obj_ids) { + return $this->call_method + ('facebook.data.getAssociatedObjectCounts', + array('name' => $name, + 'obj_ids' => json_encode($obj_ids))); + } + + /** + * Find all associations between two objects. + * + * @param obj_id1 id of first object + * @param obj_id2 id of second object + * @param no_data only return association names without data + * @return all associations between objects + * @error + * API_EC_DATA_DATABASE_ERROR + * API_EC_PARAM + * API_EC_PERMISSION + * API_EC_DATA_QUOTA_EXCEEDED + * API_EC_DATA_UNKNOWN_ERROR + */ + public function &data_getAssociations($obj_id1, $obj_id2, $no_data = true) { + return $this->call_method + ('facebook.data.getAssociations', + array('obj_id1' => $obj_id1, + 'obj_id2' => $obj_id2, + 'no_data' => $no_data)); + } + + /** + * Get the properties that you have set for an app. + * + * @param properties list of properties names to fetch + * @return a map from property name to value + */ + public function admin_getAppProperties($properties) { + return json_decode($this->call_method + ('facebook.admin.getAppProperties', + array('properties' => json_encode($properties))), true); + } + + /** + * Set properties for an app. + * + * @param properties a map from property names to values + * @return true on success + */ + public function admin_setAppProperties($properties) { + return $this->call_method + ('facebook.admin.setAppProperties', + array('properties' => json_encode($properties))); + } + + /** + * Returns the allocation limit value for a specified integration point name + * Integration point names are defined in lib/api/karma/constants.php in the limit_map + * @param string $integration_point_name + * @return integration point allocation value + */ + public function &admin_getAllocation($integration_point_name) { + return $this->call_method('facebook.admin.getAllocation', array('integration_point_name' => $integration_point_name)); + } + + /** + * Returns values for the specified metrics for the current + * application, in the given time range. The metrics are collected + * for fixed-length periods, and the times represent midnight at + * the end of each period. + * + * @param start_time unix time for the start of the range + * @param end_time unix time for the end of the range + * @param period number of seconds in the desired period + * @param metrics list of metrics to look up + * @return a list of the values for those metrics + */ + public function &admin_getMetrics($start_time, $end_time, $period, $metrics) { + return $this->call_method('admin.getMetrics', + array('start_time' => $start_time, + 'end_time' => $end_time, + 'period' => $period, + 'metrics' => json_encode($metrics))); + } + + /** + * Sets application restriction info + * Applications can restrict themselves to only a limted demography of users based on users' age and/or location + * or based on static predefined types specified by facebook for specifying diff age restriction for diff locations + * + * @param restriction_info + * @return bool + */ + public function admin_setRestrictionInfo($restriction_info = null) { + $restriction_str = null; + if (!empty($restriction_info)) { + $restriction_str = json_encode($restriction_info); + } + return $this->call_method('admin.setRestrictionInfo', + array('restriction_str' => $restriction_str)); + } + + /** + * Gets application restriction info + * Applications can restrict themselves to only a limted demography of users based on users' age and/or location + * or based on static predefined types specified by facebook for specifying diff age restriction for diff locations + * + * @return bool + */ + public function admin_getRestrictionInfo() { + return json_decode($this->call_method( + 'admin.getRestrictionInfo', + array()), + true); + } + + /* UTILITY FUNCTIONS */ + + public function & call_method($method, $params) { + + //Check if we are in batch mode + if($this->batch_queue === null) { + if ($this->call_as_apikey) { + $params['call_as_apikey'] = $this->call_as_apikey; + } + $xml = $this->post_request($method, $params); + $result = $this->convert_xml_to_result($xml, $method, $params); + if (is_array($result) && isset($result['error_code'])) { + throw new FacebookRestClientException($result['error_msg'], $result['error_code']); + } + } + else { + $result = null; + $batch_item = array('m' => $method, 'p' => $params, 'r' => & $result); + $this->batch_queue[] = $batch_item; + } + + return $result; + } + + private function convert_xml_to_result($xml, $method, $params) { + $sxml = simplexml_load_string($xml); + $result = self::convert_simplexml_to_array($sxml); + + + if (!empty($GLOBALS['facebook_config']['debug'])) { + // output the raw xml and its corresponding php object, for debugging: + print '
'; + $this->cur_id++; + print $this->cur_id . ': Called ' . $method . ', show ' . + 'Params | '. + 'XML | '. + 'SXML | '. + 'PHP'; + print ''; + print ''; + print ''; + print ''; + print '
'; + } + return $result; + } + + + + private function create_post_string($method, $params) { + $params['method'] = $method; + $params['session_key'] = $this->session_key; + $params['api_key'] = $this->api_key; + $params['call_id'] = microtime(true); + if ($params['call_id'] <= $this->last_call_id) { + $params['call_id'] = $this->last_call_id + 0.001; + } + $this->last_call_id = $params['call_id']; + if (!isset($params['v'])) { + $params['v'] = '1.0'; + } + $post_params = array(); + foreach ($params as $key => &$val) { + if (is_array($val)) $val = implode(',', $val); + $post_params[] = $key.'='.urlencode($val); + } + $secret = $this->secret; + $post_params[] = 'sig='.Facebook::generate_sig($params, $secret); + return implode('&', $post_params); + } + + public function post_request($method, $params) { + + $post_string = $this->create_post_string($method, $params); + + if (function_exists('curl_init')) { + // Use CURL if installed... + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $this->server_addr); + curl_setopt($ch, CURLOPT_POSTFIELDS, $post_string); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_USERAGENT, 'Facebook API PHP5 Client 1.1 (curl) ' . phpversion()); + $result = curl_exec($ch); + curl_close($ch); + } else { + // Non-CURL based version... + $context = + array('http' => + array('method' => 'POST', + 'header' => 'Content-type: application/x-www-form-urlencoded'."\r\n". + 'User-Agent: Facebook API PHP5 Client 1.1 (non-curl) '.phpversion()."\r\n". + 'Content-length: ' . strlen($post_string), + 'content' => $post_string)); + $contextid=stream_context_create($context); + $sock=fopen($this->server_addr, 'r', false, $contextid); + if ($sock) { + $result=''; + while (!feof($sock)) + $result.=fgets($sock, 4096); + + fclose($sock); + } + } + return $result; + } + + public static function convert_simplexml_to_array($sxml) { + $arr = array(); + if ($sxml) { + foreach ($sxml as $k => $v) { + if ($sxml['list']) { + $arr[] = self::convert_simplexml_to_array($v); + } else { + $arr[$k] = self::convert_simplexml_to_array($v); + } + } + } + if (sizeof($arr) > 0) { + return $arr; + } else { + return (string)$sxml; + } + } + +} + + +class FacebookRestClientException extends Exception { +} + +// Supporting methods and values------ + +/** + * Error codes and descriptions for the Facebook API. + */ + +class FacebookAPIErrorCodes { + + const API_EC_SUCCESS = 0; + + /* + * GENERAL ERRORS + */ + const API_EC_UNKNOWN = 1; + const API_EC_SERVICE = 2; + const API_EC_METHOD = 3; + const API_EC_TOO_MANY_CALLS = 4; + const API_EC_BAD_IP = 5; + + /* + * PARAMETER ERRORS + */ + const API_EC_PARAM = 100; + const API_EC_PARAM_API_KEY = 101; + const API_EC_PARAM_SESSION_KEY = 102; + const API_EC_PARAM_CALL_ID = 103; + const API_EC_PARAM_SIGNATURE = 104; + const API_EC_PARAM_USER_ID = 110; + const API_EC_PARAM_USER_FIELD = 111; + const API_EC_PARAM_SOCIAL_FIELD = 112; + const API_EC_PARAM_ALBUM_ID = 120; + const API_EC_PARAM_BAD_EID = 150; + const API_EC_PARAM_UNKNOWN_CITY = 151; + + /* + * USER PERMISSIONS ERRORS + */ + const API_EC_PERMISSION = 200; + const API_EC_PERMISSION_USER = 210; + const API_EC_PERMISSION_ALBUM = 220; + const API_EC_PERMISSION_PHOTO = 221; + const API_EC_PERMISSION_EVENT = 290; + const API_EC_PERMISSION_RSVP_EVENT = 299; + + const FQL_EC_PARSER = 601; + const FQL_EC_UNKNOWN_FIELD = 602; + const FQL_EC_UNKNOWN_TABLE = 603; + const FQL_EC_NOT_INDEXABLE = 604; + + /** + * DATA STORE API ERRORS + */ + const API_EC_DATA_UNKNOWN_ERROR = 800; + const API_EC_DATA_INVALID_OPERATION = 801; + const API_EC_DATA_QUOTA_EXCEEDED = 802; + const API_EC_DATA_OBJECT_NOT_FOUND = 803; + const API_EC_DATA_OBJECT_ALREADY_EXISTS = 804; + const API_EC_DATA_DATABASE_ERROR = 805; + + + /* + * Batch ERROR + */ + const API_EC_BATCH_ALREADY_STARTED = 900; + const API_EC_BATCH_NOT_STARTED = 901; + const API_EC_BATCH_METHOD_NOT_ALLOWED_IN_BATCH_MODE = 902; + + public static $api_error_descriptions = array( + API_EC_SUCCESS => 'Success', + API_EC_UNKNOWN => 'An unknown error occurred', + API_EC_SERVICE => 'Service temporarily unavailable', + API_EC_METHOD => 'Unknown method', + API_EC_TOO_MANY_CALLS => 'Application request limit reached', + API_EC_BAD_IP => 'Unauthorized source IP address', + API_EC_PARAM => 'Invalid parameter', + API_EC_PARAM_API_KEY => 'Invalid API key', + API_EC_PARAM_SESSION_KEY => 'Session key invalid or no longer valid', + API_EC_PARAM_CALL_ID => 'Call_id must be greater than previous', + API_EC_PARAM_SIGNATURE => 'Incorrect signature', + API_EC_PARAM_USER_ID => 'Invalid user id', + API_EC_PARAM_USER_FIELD => 'Invalid user info field', + API_EC_PARAM_SOCIAL_FIELD => 'Invalid user field', + API_EC_PARAM_ALBUM_ID => 'Invalid album id', + API_EC_PARAM_BAD_EID => 'Invalid eid', + API_EC_PARAM_UNKNOWN_CITY => 'Unknown city', + API_EC_PERMISSION => 'Permissions error', + API_EC_PERMISSION_USER => 'User not visible', + API_EC_PERMISSION_ALBUM => 'Album not visible', + API_EC_PERMISSION_PHOTO => 'Photo not visible', + API_EC_PERMISSION_EVENT => 'Creating and modifying events required the extended permission create_event', + API_EC_PERMISSION_RSVP_EVENT => 'RSVPing to events required the extended permission rsvp_event', + FQL_EC_PARSER => 'FQL: Parser Error', + FQL_EC_UNKNOWN_FIELD => 'FQL: Unknown Field', + FQL_EC_UNKNOWN_TABLE => 'FQL: Unknown Table', + FQL_EC_NOT_INDEXABLE => 'FQL: Statement not indexable', + FQL_EC_UNKNOWN_FUNCTION => 'FQL: Attempted to call unknown function', + FQL_EC_INVALID_PARAM => 'FQL: Invalid parameter passed in', + API_EC_DATA_UNKNOWN_ERROR => 'Unknown data store API error', + API_EC_DATA_INVALID_OPERATION => 'Invalid operation', + API_EC_DATA_QUOTA_EXCEEDED => 'Data store allowable quota was exceeded', + API_EC_DATA_OBJECT_NOT_FOUND => 'Specified object cannot be found', + API_EC_DATA_OBJECT_ALREADY_EXISTS => 'Specified object already exists', + API_EC_DATA_DATABASE_ERROR => 'A database error occurred. Please try again', + API_EC_BATCH_ALREADY_STARTED => 'begin_batch already called, please make sure to call end_batch first', + API_EC_BATCH_NOT_STARTED => 'end_batch called before start_batch', + API_EC_BATCH_METHOD_NOT_ALLOWED_IN_BATCH_MODE => 'this method is not allowed in batch mode', + ); +} diff --git a/fb/jsonwrapper/JSON/JSON.php b/fb/jsonwrapper/JSON/JSON.php new file mode 100644 index 0000000..0cddbdd --- /dev/null +++ b/fb/jsonwrapper/JSON/JSON.php @@ -0,0 +1,806 @@ + + * @author Matt Knapp + * @author Brett Stimmerman + * @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: + * + * + * // 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); + * + */ +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) + { + + } + } + +} + +?> diff --git a/fb/jsonwrapper/JSON/LICENSE b/fb/jsonwrapper/JSON/LICENSE new file mode 100644 index 0000000..4ae6bef --- /dev/null +++ b/fb/jsonwrapper/JSON/LICENSE @@ -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. diff --git a/fb/jsonwrapper/jsonwrapper.php b/fb/jsonwrapper/jsonwrapper.php new file mode 100644 index 0000000..29509de --- /dev/null +++ b/fb/jsonwrapper/jsonwrapper.php @@ -0,0 +1,6 @@ + diff --git a/fb/jsonwrapper/jsonwrapper_inner.php b/fb/jsonwrapper/jsonwrapper_inner.php new file mode 100644 index 0000000..36a3f28 --- /dev/null +++ b/fb/jsonwrapper/jsonwrapper_inner.php @@ -0,0 +1,23 @@ +encode($arg); +} + +function json_decode($arg) +{ + global $services_json; + if (!isset($services_json)) { + $services_json = new Services_JSON(); + } + return $services_json->decode($arg); +} + +?> diff --git a/fb/readme-fb.txt b/fb/readme-fb.txt new file mode 100644 index 0000000..46dd758 --- /dev/null +++ b/fb/readme-fb.txt @@ -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. diff --git a/gifbar/Makefile b/gifbar/Makefile new file mode 100644 index 0000000..ed03365 --- /dev/null +++ b/gifbar/Makefile @@ -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 diff --git a/gifbar/gifbar.c b/gifbar/gifbar.c new file mode 100644 index 0000000..5e8ef7c --- /dev/null +++ b/gifbar/gifbar.c @@ -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 /* getenv(), atoi() */ +#include /* printf(), putchar(), fwrite() */ +#include /* 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, + 0xB3, 0x66, 0xCE, 0x9C, 0x3D, 0x73, 0x86, 0xCC, 0xD7, 0xAF, 0x65, 0xD2, + 0xA4, 0x2D, 0xFB, 0xB5, 0x8B, 0x17, 0xB1, 0x67, 0xD2, 0xA0, 0x45, 0xB3, + 0x96, 0x2D, 0x5B, 0x35, 0x64, 0xBD, 0x7A, 0x0D, 0x4B, 0xD6, 0xAC, 0xDA, + 0xB6, 0x66, 0xB1, 0x64, 0xFE, 0x39, 0xCB, 0x16, 0x2C, 0x15, 0xA8, 0x4F, + 0xA0, 0x42, 0x89, 0x2A, 0xC5, 0x8A, 0x56, 0x2E, 0x5C, 0xB4, 0x66, 0xAB, + 0x52, 0xC5, 0xEA, 0x95, 0x2D, 0x5D, 0xC2, 0xB2, 0xB5, 0x13, 0x38, 0x80, + 0xC0, 0x02, 0x08, 0x14, 0x78, 0xA0, 0x03, 0x10, 0x27, 0x50, 0x70, 0xD1, + 0x04, 0x12, 0x3C, 0xC0, 0xC0, 0x82, 0x0E, 0x3C, 0x20, 0x01, 0x05, 0x17, + 0x70, 0x80, 0x02, 0x11, 0x4D, 0x10, 0xB1, 0x43, 0x0D, 0x31, 0xA0, 0xE0, + 0x81, 0x06, 0x1C, 0x9C, 0x10, 0x84, 0x11, 0x31, 0xA8, 0x10, 0x03, 0x14, + 0x62, 0x8C, 0xA1, 0x85, 0x12, 0x3F, 0xDC, 0x20, 0x03, 0x0D, 0x37, 0xD4, + 0x90, 0x03, 0x14, 0x53, 0xFC, 0x80, 0x03, 0x0D, 0x31, 0xC8, 0x50, 0x03, + 0x0E, 0x40, 0xED, 0xC0, 0x83, 0x0E, 0x3E, 0xED, 0xB0, 0x43, 0x0E, 0x3D, + 0x62, 0x25, 0xC4, 0x10, 0x47, 0x20, 0x31, 0x84, 0x4D, 0x3C, 0xF8, 0xE0, + 0x03, 0x10, 0x4D, 0xAC, 0xF1, 0x46, 0x15, 0x4C, 0x24, 0x81, 0x44, 0x12, + 0x4A, 0x28, 0xC1, 0xC4, 0x58, 0x5E, 0x78, 0xE5, 0x44, 0x47, 0x4F, 0x44, + 0x31, 0x45, 0x15, 0x56, 0x68, 0x51, 0x46, 0x20, 0x7C, 0x7C, 0x51, 0x45, + 0x15, 0x59, 0x8C, 0xE1, 0x47, 0x24, 0x8A, 0xC8, 0x61, 0x06, 0x1A, 0x6E, + 0xD4, 0xF1, 0x47, 0x22, 0x7C, 0xA4, 0x31, 0x86, 0x1A, 0x7A, 0x0C, 0x02, + 0x48, 0x24, 0xA2, 0x84, 0xE2, 0x88, 0x1D, 0x6E, 0xB0, 0xB1, 0x86, 0x1A, + 0x6A, 0xAC, 0x31, 0x68, 0xA1, 0x6D, 0xC8, 0x71, 0x47, 0x1F, 0x88, 0x68, + 0xA2, 0xCA, 0x2A, 0x9B, 0x28, 0x92, 0x48, 0x27, 0xA8, 0x64, 0x72, 0x88, + 0x22, 0xA4, 0xC0, 0x02, 0xCA, 0x24, 0xA7, 0xA4, 0xF2, 0x48, 0x22, 0x8E, + 0x68, 0xA2, 0x89, 0x24, 0x8F, 0x48, 0xD2, 0xC9, 0x2B, 0xBE, 0x10, 0x53, + 0xCC, 0xAA, 0xC4, 0xF4, 0xA6, 0x8C, 0x32, 0xFE, 0xCB, 0x38, 0xC3, 0x0C, + 0x2F, 0xA1, 0x84, 0xE2, 0xCB, 0x70, 0xC3, 0xC8, 0x02, 0xCB, 0x2D, 0xC3, + 0x2C, 0x43, 0x4C, 0x2F, 0xC2, 0x30, 0x13, 0x4D, 0x35, 0xD5, 0x28, 0xF3, + 0x0B, 0x7C, 0xB1, 0xD8, 0x92, 0x0C, 0x36, 0xC8, 0xB0, 0xD2, 0x8A, 0x32, + 0xCE, 0xC8, 0x42, 0xCA, 0x27, 0x9E, 0xA0, 0x17, 0x0A, 0x29, 0xAA, 0x24, + 0x5B, 0x0B, 0x69, 0xF3, 0x3D, 0xEA, 0xCA, 0x2C, 0xB7, 0xF0, 0xB2, 0xCC, + 0x39, 0xFC, 0x0D, 0x50, 0x00, 0x02, 0x0D, 0x68, 0x80, 0x83, 0x0D, 0x1A, + 0x3C, 0x60, 0x51, 0x04, 0x10, 0x34, 0xB0, 0x20, 0x45, 0x16, 0x4D, 0x40, + 0x41, 0x05, 0x20, 0xF8, 0x50, 0xC4, 0x0A, 0x23, 0x94, 0xC0, 0x42, 0x0A, + 0x1E, 0x60, 0xA0, 0x01, 0x09, 0x03, 0xAE, 0x50, 0xC2, 0x0B, 0x50, 0x7C, + 0xC1, 0x45, 0x4C, 0x3F, 0xD0, 0xF0, 0x02, 0x0C, 0x34, 0xB8, 0x98, 0x04, + 0x13, 0x3C, 0xD8, 0x30, 0x83, 0x0C, 0x33, 0xD8, 0xE0, 0x13, 0x50, 0x4A, + 0xFD, 0xA4, 0x83, 0x52, 0x36, 0xD8, 0xA0, 0x03, 0x56, 0x41, 0x00, 0x51, + 0x44, 0x12, 0x45, 0xF8, 0xA0, 0x63, 0x0F, 0x3E, 0x04, 0xE1, 0x04, 0x1B, + 0x6E, 0x48, 0x71, 0x04, 0x11, 0x0A, 0x49, 0xA9, 0x04, 0x14, 0x59, 0x84, + 0xC1, 0x85, 0x14, 0x1E, 0x35, 0xF1, 0xC4, 0xCF, 0x4E, 0x3C, 0x41, 0x85, + 0x18, 0x80, 0xEC, 0xD1, 0x85, 0x14, 0x51, 0x54, 0x01, 0xC6, 0x1E, 0x8C, + 0x00, 0xC2, 0x86, 0x19, 0x6D, 0xE0, 0xA1, 0x07, 0x20, 0x8B, 0xFC, 0x91, + 0x86, 0x18, 0x6A, 0xEC, 0x41, 0xC8, 0x61, 0xA2, 0x78, 0x62, 0x48, 0x1C, + 0x84, 0x0E, 0x9A, 0xC6, 0xD8, 0x68, 0xA0, 0x91, 0x06, 0x1B, 0x71, 0xD0, + 0x41, 0x87, 0x1E, 0x8C, 0x98, 0x82, 0xCA, 0x24, 0x82, 0xCC, 0xF9, 0xC9, + 0x25, 0x83, 0x0C, 0x82, 0x49, 0x2B, 0xA2, 0x4C, 0x52, 0x0A, 0xFE, 0x29, + 0x8A, 0x18, 0xC2, 0x08, 0xA9, 0x92, 0x52, 0x92, 0xCA, 0x2C, 0xB4, 0xC0, + 0xE2, 0xCA, 0x2B, 0xA8, 0xED, 0xB2, 0x0B, 0x2E, 0xB5, 0xEC, 0x72, 0xCC, + 0x32, 0xB7, 0x68, 0xB2, 0x09, 0x2E, 0xC8, 0xFC, 0x02, 0x4B, 0x28, 0x9E, + 0x90, 0xC2, 0x4B, 0x31, 0xB7, 0x24, 0xDB, 0x4B, 0x30, 0xCD, 0x50, 0xC3, + 0xCC, 0x2E, 0xAE, 0xA8, 0x82, 0xCA, 0x2B, 0xC9, 0x58, 0x33, 0x4C, 0x2A, + 0xAA, 0x18, 0x93, 0x0C, 0xDE, 0x9E, 0x54, 0x8B, 0x9E, 0x28, 0xA6, 0xB0, + 0x82, 0xDA, 0x2C, 0xAC, 0xAC, 0xB2, 0x8A, 0xB7, 0xB2, 0x34, 0xEE, 0x4C, + 0x3A, 0xE5, 0x1A, 0xB0, 0x40, 0x05, 0x2B, 0xF8, 0x10, 0x83, 0x05, 0x0E, + 0x40, 0x10, 0x81, 0x04, 0x11, 0x4C, 0xB4, 0x60, 0x45, 0x11, 0x44, 0x4F, + 0xC1, 0x07, 0x3D, 0x00, 0x41, 0x82, 0x06, 0x1E, 0xA4, 0xA0, 0xC2, 0x07, + 0x18, 0x64, 0x10, 0xC2, 0x0C, 0x43, 0xD4, 0x00, 0x03, 0x10, 0x57, 0x64, + 0x81, 0x84, 0x0F, 0x3F, 0xE4, 0x10, 0x62, 0x0A, 0x2E, 0xD0, 0xA0, 0xC3, + 0x11, 0x46, 0xE4, 0x30, 0x43, 0x0D, 0x1A, 0xDB, 0x40, 0x43, 0xC4, 0x36, + 0xD4, 0x10, 0xF1, 0x0D, 0x1F, 0xB7, 0x68, 0xC3, 0x0E, 0x40, 0x98, 0xCA, + 0x0E, 0x7E, 0x10, 0x95, 0xA9, 0x20, 0xE5, 0x07, 0x44, 0x90, 0x82, 0x1B, + 0xE2, 0xA0, 0x05, 0x27, 0x1C, 0xA1, 0x08, 0x47, 0x48, 0x42, 0x13, 0xA6, + 0xB0, 0x85, 0x31, 0x80, 0xE1, 0x0A, 0x4F, 0x60, 0xC2, 0x12, 0x98, 0xE0, + 0x04, 0x28, 0x48, 0x61, 0x0A, 0x54, 0xD0, 0x82, 0x1C, 0x12, 0xD1, 0x07, + 0x2F, 0x50, 0x61, 0x0A, 0x56, 0xF0, 0x82, 0x1B, 0x04, 0x71, 0x08, 0x3F, + 0xD8, 0x41, 0x0F, 0x7E, 0xF0, 0xC3, 0x21, 0x1E, 0xD1, 0x07, 0x34, 0x88, + 0x61, 0x0D, 0x85, 0xA0, 0x04, 0x25, 0x36, 0x21, 0x8A, 0x4D, 0xFC, 0xE1, + 0x69, 0xFE, 0x68, 0x58, 0x83, 0xA0, 0x08, 0xA5, 0x86, 0x34, 0xA8, 0xA1, + 0x0D, 0x71, 0x90, 0x43, 0x1C, 0xEE, 0xE0, 0x88, 0x53, 0x7C, 0x82, 0x10, + 0x78, 0xA8, 0x83, 0x1F, 0x28, 0xD1, 0x89, 0x46, 0x28, 0xC2, 0x12, 0xBB, + 0xF3, 0xC4, 0xEE, 0x2C, 0x81, 0x89, 0x51, 0x78, 0xE2, 0x11, 0x8A, 0x68, + 0xC4, 0x28, 0x68, 0xA1, 0x0A, 0x4B, 0x40, 0xC2, 0x11, 0x8F, 0x98, 0x44, + 0x26, 0x3E, 0xD1, 0x89, 0x4C, 0x24, 0x66, 0x17, 0xCA, 0xA0, 0x55, 0x28, + 0x78, 0xD1, 0x0B, 0x52, 0x70, 0x02, 0x13, 0x95, 0xE0, 0x04, 0x2C, 0x80, + 0xA1, 0x0B, 0x5A, 0xCC, 0x22, 0x16, 0xB3, 0x38, 0x46, 0x34, 0x8C, 0x71, + 0x0B, 0xC3, 0xBD, 0xA2, 0x17, 0xD1, 0x88, 0x86, 0x2D, 0x4A, 0xD1, 0xBA, + 0x66, 0x61, 0x4E, 0x76, 0xA0, 0x10, 0x05, 0x29, 0x52, 0x11, 0x8B, 0xF7, + 0xC0, 0x22, 0x77, 0xBA, 0x6B, 0x05, 0x2C, 0x6A, 0xE1, 0x8B, 0x6C, 0xBC, + 0x83, 0x3F, 0x05, 0x68, 0xC0, 0x05, 0x54, 0xC0, 0x32, 0x14, 0x54, 0x40, + 0x79, 0x12, 0xB0, 0x57, 0xBC, 0x18, 0xA4, 0x3C, 0xE5, 0x55, 0x60, 0x04, + 0x25, 0x23, 0x41, 0x06, 0x24, 0xA4, 0x82, 0x10, 0x68, 0x00, 0x7B, 0x2C, + 0x18, 0x42, 0x07, 0xC3, 0x14, 0x85, 0x1B, 0xB4, 0xC0, 0x05, 0x30, 0x80, + 0xC1, 0x0A, 0x4C, 0x80, 0x82, 0x17, 0xE4, 0x40, 0x09, 0x47, 0xB0, 0x01, + 0x0C, 0x62, 0x40, 0x83, 0x8B, 0x2D, 0xB3, 0x99, 0x31, 0xA8, 0x91, 0x8B, + 0x78, 0x54, 0x83, 0x1B, 0x54, 0xCF, 0x07, 0x40, 0xEA, 0x81, 0x11, 0xA4, + 0x42, 0x15, 0xAB, 0x14, 0x81, 0x0A, 0x6D, 0x70, 0x43, 0x18, 0xAC, 0xD0, + 0x04, 0xF8, 0x29, 0x41, 0x0A, 0x5B, 0x00, 0x43, 0x17, 0xAE, 0x00, 0x85, + 0x25, 0x54, 0x69, 0x09, 0x4E, 0x98, 0x42, 0x16, 0xBA, 0xF0, 0x05, 0x33, + 0x08, 0xC2, 0x11, 0x7C, 0xFE, 0x10, 0x83, 0x16, 0xB2, 0x90, 0x4E, 0x33, + 0x8C, 0xD0, 0x11, 0x82, 0xA8, 0x43, 0x1C, 0xF2, 0xB0, 0x08, 0x46, 0xC8, + 0x81, 0x0C, 0x60, 0x58, 0x83, 0x22, 0x30, 0x71, 0x46, 0x4B, 0x58, 0x82, + 0x0F, 0x6B, 0x70, 0x49, 0x10, 0xD9, 0x20, 0x28, 0x43, 0xF9, 0x05, 0x0E, + 0x72, 0x98, 0xC3, 0x1E, 0x2E, 0x51, 0x8A, 0x48, 0xF0, 0x61, 0x0E, 0x4B, + 0x34, 0x04, 0x27, 0x46, 0xF1, 0x89, 0x4F, 0x7C, 0x6B, 0x93, 0xB3, 0xF8, + 0x96, 0x2D, 0x56, 0x31, 0x89, 0x46, 0x4C, 0xA2, 0x15, 0xAE, 0x98, 0x04, + 0x22, 0x06, 0x21, 0x88, 0xD0, 0x80, 0x2A, 0x12, 0x8F, 0x60, 0x04, 0x25, + 0x68, 0xA1, 0x0C, 0xEC, 0xF4, 0x82, 0x73, 0x9C, 0xB0, 0x44, 0x25, 0x76, + 0xC8, 0x0A, 0x5E, 0xE4, 0x82, 0x16, 0xB2, 0x70, 0x45, 0x2C, 0x8A, 0xB1, + 0x0C, 0x5C, 0xB8, 0x02, 0xA6, 0xB6, 0x58, 0xC6, 0x34, 0x84, 0xC1, 0x8A, + 0x51, 0xB4, 0xEE, 0x75, 0x5D, 0x83, 0xA4, 0x24, 0x4F, 0x87, 0x0B, 0x5E, + 0xD4, 0xA2, 0x15, 0x8F, 0xA2, 0xCD, 0xAE, 0x86, 0x21, 0x0E, 0x79, 0x08, + 0xC4, 0x3F, 0x1B, 0x98, 0x01, 0x10, 0x82, 0x10, 0x03, 0x0E, 0x4C, 0x20, + 0x7A, 0x12, 0x70, 0x08, 0x05, 0x26, 0x52, 0x11, 0x08, 0xD8, 0x75, 0x7A, + 0x33, 0x50, 0x42, 0x10, 0x4A, 0xC0, 0x01, 0x0F, 0x9C, 0xC0, 0x60, 0x1E, + 0xE8, 0xEB, 0x09, 0x72, 0x40, 0x85, 0x2F, 0x68, 0x41, 0x0A, 0x4B, 0xB0, + 0x41, 0x0A, 0x4E, 0xC0, 0x02, 0x18, 0xB8, 0xE0, 0x04, 0x26, 0x58, 0xC1, + 0x0D, 0x3C, 0x62, 0x83, 0x16, 0xB0, 0xE0, 0x97, 0x2E, 0x78, 0x41, 0x0C, + 0x9C, 0xF9, 0x4C, 0x1C, 0x80, 0x0C, 0x07, 0xE8, 0xDB, 0xC1, 0x4F, 0x7C, + 0x60, 0x04, 0x23, 0xFC, 0x60, 0x65, 0xFA, 0xAA, 0xC2, 0x1A, 0xC6, 0x70, + 0x05, 0x2C, 0xC4, 0xE4, 0x08, 0x4B, 0xA0, 0xFE, 0x42, 0x16, 0xAE, 0x30, + 0x05, 0xB0, 0x28, 0x81, 0x4A, 0xF0, 0xB4, 0xC2, 0x17, 0xC8, 0xA0, 0x06, + 0x3C, 0x28, 0x82, 0x11, 0x79, 0x28, 0x43, 0x18, 0x4A, 0x24, 0x06, 0x30, + 0xA0, 0x81, 0x10, 0x8C, 0xB8, 0xC3, 0xA0, 0xEE, 0xD0, 0x88, 0x43, 0xAC, + 0xE1, 0x0B, 0x5F, 0x58, 0x03, 0x22, 0x1E, 0x61, 0x07, 0x39, 0xAC, 0xC9, + 0x10, 0x6F, 0x28, 0x03, 0x19, 0xCC, 0x90, 0x06, 0x21, 0x52, 0x94, 0xA2, + 0x6D, 0x78, 0x43, 0xDA, 0xFC, 0xB0, 0x89, 0x50, 0x18, 0x42, 0x0F, 0x20, + 0xA5, 0x83, 0x1F, 0x1E, 0xC1, 0xC8, 0x55, 0x24, 0xEB, 0x3D, 0xB4, 0xC0, + 0xC5, 0x2E, 0x74, 0x01, 0x0B, 0x4F, 0x68, 0xC2, 0x14, 0xB4, 0x30, 0x05, + 0x23, 0x68, 0x1A, 0x88, 0x9A, 0x16, 0x22, 0x11, 0x8F, 0xC8, 0xE9, 0x24, + 0x60, 0xC1, 0x1D, 0x64, 0x04, 0x03, 0x18, 0xB2, 0xF0, 0x44, 0x26, 0x16, + 0x1C, 0x0A, 0x5B, 0x08, 0x43, 0x17, 0xB1, 0x81, 0x85, 0x2C, 0xB6, 0x53, + 0x0B, 0x53, 0x88, 0x62, 0x14, 0xAE, 0x58, 0x46, 0x33, 0x64, 0x71, 0x8A, + 0x51, 0xB0, 0x22, 0x19, 0xD1, 0x9A, 0x56, 0xB5, 0x40, 0x11, 0x49, 0x46, + 0xBE, 0xE2, 0x16, 0xBD, 0xB8, 0xC5, 0x6C, 0x5A, 0x01, 0x53, 0x59, 0xEC, + 0x02, 0x1A, 0xEA, 0xA0, 0xC7, 0x59, 0x1D, 0xC0, 0x02, 0x22, 0x20, 0x01, + 0x08, 0x2C, 0xE0, 0x40, 0x05, 0x96, 0x47, 0x01, 0x81, 0x5D, 0x00, 0x02, + 0xF4, 0xB2, 0xAB, 0x04, 0x34, 0xD0, 0x82, 0x24, 0x44, 0xA1, 0x08, 0x22, + 0x89, 0xAC, 0x0B, 0x50, 0x10, 0x82, 0x0F, 0x84, 0x20, 0x05, 0x39, 0x90, + 0x82, 0x61, 0xC1, 0xB2, 0x03, 0x17, 0xAC, 0xC0, 0x05, 0x32, 0x88, 0xC1, + 0x0A, 0x54, 0xE0, 0x82, 0x1C, 0x4C, 0x61, 0x0A, 0x39, 0x78, 0x81, 0x0B, + 0xC6, 0x0C, 0x83, 0x15, 0xD9, 0x2F, 0x9A, 0x2B, 0xC2, 0xC1, 0xFE, 0x0E, + 0x78, 0x04, 0x5A, 0x20, 0x24, 0x85, 0x07, 0x43, 0x48, 0xC2, 0x11, 0x4E, + 0x9B, 0x24, 0x02, 0x76, 0xA1, 0x0D, 0x5E, 0xC9, 0x02, 0x16, 0xA0, 0x60, + 0x25, 0x2A, 0x60, 0xC1, 0x0A, 0x27, 0x84, 0x42, 0x13, 0x96, 0xB0, 0x41, + 0x9C, 0x81, 0xA1, 0x30, 0x7A, 0x28, 0x84, 0x22, 0xFA, 0xB0, 0x86, 0x33, + 0x9C, 0xC1, 0x0C, 0x61, 0xF0, 0x42, 0x19, 0x04, 0x81, 0x88, 0x36, 0x94, + 0x01, 0x0D, 0x79, 0x78, 0x44, 0x21, 0xD4, 0x00, 0xDD, 0x35, 0x24, 0x02, + 0x12, 0x76, 0x80, 0xC3, 0x1D, 0x22, 0x21, 0x89, 0x3B, 0xA8, 0xE1, 0x4D, + 0x6A, 0x60, 0x43, 0x38, 0x21, 0x13, 0xCE, 0x37, 0x28, 0x6A, 0x10, 0xA1, + 0x18, 0xC5, 0x22, 0x00, 0xA1, 0x87, 0x3C, 0xEC, 0x61, 0x10, 0x91, 0x08, + 0x45, 0x29, 0x4C, 0x01, 0x9B, 0x5C, 0xD8, 0x62, 0x57, 0xBD, 0xD8, 0x85, + 0x8A, 0x57, 0x11, 0xDF, 0x53, 0x48, 0xA2, 0x11, 0xC8, 0x46, 0x63, 0x1A, + 0x31, 0x81, 0x09, 0x4B, 0x6C, 0x82, 0x16, 0xC8, 0x68, 0x06, 0xE7, 0x6C, + 0x31, 0x0B, 0x53, 0x90, 0x62, 0x14, 0xA5, 0x68, 0x85, 0x2F, 0x0C, 0x8C, + 0x8B, 0x5B, 0xE0, 0x22, 0x17, 0xBF, 0x50, 0x46, 0x2F, 0x4A, 0xD1, 0x89, + 0x4F, 0xB0, 0x22, 0x56, 0xB3, 0x40, 0x45, 0x29, 0x5C, 0xC1, 0x0C, 0x6C, + 0x04, 0x03, 0x15, 0xE7, 0x49, 0x8F, 0x7A, 0x4E, 0xE1, 0x8A, 0x5A, 0xF0, + 0xE2, 0x17, 0xB6, 0x78, 0xEA, 0x2B, 0x5E, 0x11, 0x0B, 0x5A, 0xFC, 0x62, + 0x1B, 0xEF, 0xA8, 0xC7, 0x59, 0x1F, 0x00, 0x03, 0xD8, 0x1E, 0xC1, 0x07, + 0x2E, 0xD8, 0xC0, 0xBD, 0x2E, 0xC0, 0xA1, 0x0C, 0x44, 0xA0, 0x01, 0x0D, + 0x52, 0xDE, 0x04, 0x3E, 0xC0, 0x83, 0x29, 0x5C, 0x41, 0x0A, 0x4A, 0x10, + 0x02, 0xCB, 0x72, 0x50, 0x4B, 0x0F, 0x88, 0xC0, 0x05, 0x43, 0x88, 0x02, + 0x3D, 0xE8, 0xAF, 0xD0, 0x04, 0x1F, 0xD4, 0x40, 0x7F, 0x35, 0x98, 0x41, + 0x8D, 0x6E, 0x20, 0x84, 0x7D, 0xA6, 0x28, 0x64, 0x1A, 0x13, 0xCA, 0x8F, + 0xEC, 0xC7, 0x22, 0x1D, 0x14, 0x05, 0x47, 0x3E, 0x18, 0xC2, 0x10, 0x82, + 0x30, 0x04, 0x25, 0x94, 0xF3, 0x07, 0x07, 0x0C, 0x42, 0x12, 0xBE, 0xC0, + 0x86, 0x2F, 0x6C, 0xC1, 0xE8, 0x88, 0x6D, 0x42, 0x15, 0xB8, 0x10, 0x86, + 0x31, 0x84, 0x61, 0x0B, 0x53, 0x70, 0x82, 0xCF, 0xA6, 0xA0, 0x05, 0x31, + 0xA4, 0x41, 0x51, 0x79, 0x38, 0xC4, 0x21, 0xF2, 0x60, 0xE9, 0xE2, 0x7E, + 0xE1, 0x0C, 0x84, 0x58, 0x04, 0x1D, 0xCE, 0x06, 0x88, 0x47, 0x0C, 0xA2, + 0x0D, 0x64, 0x18, 0x43, 0x1B, 0x18, 0xB1, 0x89, 0x48, 0x14, 0xA2, 0x10, + 0x97, 0xE0, 0x44, 0x21, 0xE2, 0xA0, 0x6A, 0xC9, 0xC0, 0xE1, 0xEE, 0x6F, + 0x88, 0x4C, 0x1C, 0xEA, 0xD0, 0x07, 0x4A, 0xA8, 0x02, 0x6F, 0x95, 0x58, + 0xC4, 0xA5, 0x32, 0x81, 0x8A, 0x56, 0x9C, 0x42, 0x31, 0xB2, 0x38, 0xEA, + 0x2A, 0x38, 0xF9, 0x8B, 0x5E, 0xD8, 0xE2, 0x15, 0x2E, 0xC6, 0x05, 0x2C, + 0x58, 0xEC, 0x8A, 0xC3, 0x49, 0xD8, 0x16, 0xB7, 0xA8, 0x05, 0x2E, 0x86, + 0xC1, 0x8C, 0x67, 0x28, 0x63, 0x18, 0xC3, 0x20, 0xC6, 0x2F, 0x32, 0xCF, + 0x0B, 0x5E, 0xDC, 0x46, 0x18, 0xBF, 0x38, 0x06, 0x33, 0x92, 0x71, 0x0C, + 0x67, 0x10, 0x23, 0x15, 0x9F, 0x08, 0xC5, 0x2B, 0x9C, 0x71, 0x8D, 0x61, + 0x94, 0xE6, 0xDC, 0xDD, 0x58, 0xC6, 0x2A, 0xE2, 0xAD, 0x1E, 0xAB, 0xFA, + 0x1B, 0x19, 0xC0, 0xA8, 0x45, 0xE5, 0x5F, 0x01, 0x0B, 0x70, 0x0D, 0x23, + 0x1C, 0xF1, 0xA8, 0x47, 0x40, 0x00 }; + int i, r, g, b, colour_change; + + /* GIF header */ + printf("GIF"); /* signature */ + printf("87a"); /* version */ + /* logical screen descriptor */ + putchar(0); putchar(1); /* width */ + putchar(8); putchar(0); /* height */ + putchar(0xF7); /* 256 colours with table to follow */ + putchar(0); /* background colour */ + putchar(0); /* reserved (aspect ratio in 89a) */ + /* global colour table - do'n't mess the colours up, just the colour table; + like the old trick with 256-colour games where they do animation with + colour cycling */ + r = 0xF0, g = 0x00, b = 0x32; /* aiming for 0x50 0xA0 0xC0 */ + if(parm < 40) { + colour_change = (40 - parm) << 1; + r -= colour_change; + g += colour_change; + b += colour_change; + } + for(i = 0; i <= 0xFF; i++) { + if(i >= parm - 40 && i < parm + 40) { + r -= 2; g += 2; b += 2; + } + putchar(r); putchar(g); putchar(b); + } + /* image descriptor */ + putchar(','); /* image separator */ + putchar(0); putchar(0); /* image left position */ + putchar(0); putchar(0); /* image top position */ + putchar(0); putchar(1); /* image width */ + putchar(8); putchar(0); /* image height */ + putchar(0); /* packed flags - use global table */ + /* image data block, *ptr, size, ntimes, stream */ + if(fwrite(image_data, sizeof(char), sizeof(image_data), stdout) != sizeof(image_data)) + { perror("stdout"); } + putchar(0); /* terminating empty sub-block */ + /* GIF trailer */ + putchar(';'); /* trailer */ +} diff --git a/gpl.txt b/gpl.txt new file mode 100644 index 0000000..bc08fe2 --- /dev/null +++ b/gpl.txt @@ -0,0 +1,619 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. diff --git a/help.html b/help.html new file mode 100644 index 0000000..231ed0d --- /dev/null +++ b/help.html @@ -0,0 +1,25 @@ + + + +

Help

+

Q: What are the positions for?
+A: I don't really know; whatever you want.

+

Q: How do I use the App?
+A: Select your position and click "Add to Profile."

+

Q: That's stupid.
+A: How is that a question?

+

Q: My favourite position is not on the list. In fact, it's kind of +limited.
+A: Message me and maybe I'll put it on eventually.

+

Q: Is that . . . a horse?
+A: I do not condone having sex with a horse where prohibited by law.

+

Q: Isn't this Adult Content that should be restricted?
+A: Whatever.

+

Q: Who is responsible for this atrocity?
+A: +Neil! +

+

Q: Aren't you worried that you'll burn in Hades for eternity?
+A: Not really.

+ diff --git a/index.php b/index.php new file mode 100644 index 0000000..fb73c3d --- /dev/null +++ b/index.php @@ -0,0 +1,56 @@ + + +

Sex Centre

+require_login(); + $old = SexExtract($fb->api_client->profile_getFBML($user)); + + // if 'update' then the form has been self-referencing submitted + if(isset($_POST['update'])) { + $pos = $_POST['update']; + // change the box + $box = '
'. + 'sex centre'. + '
'. + '
'. + ''. + '
'; + try { + $fb->api_client->profile_setFBML(null, $user, $box, null, null, $box); + } catch(FacebookRestClientException $e) { + echo '

FacebookRestClientException (oh no!): '.$e->getMessage().'

'; + } + // update stats + SexLoad(); + SexChange($old, $pos); + SexSave(); + $old = $pos; + } + + // echo pos'n + if(isset($old)) { + echo '

'. + '
'; + } else { + echo '

No position selected.

'; + } + + // menu + echo '
'; + SexInterface(); + echo '
'; +?> +

diff --git a/media/2f-daisy.jpg b/media/2f-daisy.jpg new file mode 100644 index 0000000..780f94e Binary files /dev/null and b/media/2f-daisy.jpg differ diff --git a/media/2f-daisy.piv b/media/2f-daisy.piv new file mode 100644 index 0000000..ba75d25 Binary files /dev/null and b/media/2f-daisy.piv differ diff --git a/media/2f-daisy0.bmp b/media/2f-daisy0.bmp new file mode 100644 index 0000000..b848021 Binary files /dev/null and b/media/2f-daisy0.bmp differ diff --git a/media/3f-threesome.jpg b/media/3f-threesome.jpg new file mode 100644 index 0000000..2a2f0e2 Binary files /dev/null and b/media/3f-threesome.jpg differ diff --git a/media/3f-threesome.piv b/media/3f-threesome.piv new file mode 100644 index 0000000..a5621aa Binary files /dev/null and b/media/3f-threesome.piv differ diff --git a/media/3f-threesome0.bmp b/media/3f-threesome0.bmp new file mode 100644 index 0000000..09cf494 Binary files /dev/null and b/media/3f-threesome0.bmp differ diff --git a/media/68.jpg b/media/68.jpg new file mode 100644 index 0000000..55879f9 Binary files /dev/null and b/media/68.jpg differ diff --git a/media/68.piv b/media/68.piv new file mode 100644 index 0000000..6af4640 Binary files /dev/null and b/media/68.piv differ diff --git a/media/680.bmp b/media/680.bmp new file mode 100644 index 0000000..71f941d Binary files /dev/null and b/media/680.bmp differ diff --git a/media/69.jpg b/media/69.jpg new file mode 100644 index 0000000..ed10859 Binary files /dev/null and b/media/69.jpg differ diff --git a/media/69.piv b/media/69.piv new file mode 100644 index 0000000..c6e462a Binary files /dev/null and b/media/69.piv differ diff --git a/media/690.bmp b/media/690.bmp new file mode 100644 index 0000000..433f721 Binary files /dev/null and b/media/690.bmp differ diff --git a/media/acrobat.jpg b/media/acrobat.jpg new file mode 100644 index 0000000..1127631 Binary files /dev/null and b/media/acrobat.jpg differ diff --git a/media/acrobat.piv b/media/acrobat.piv new file mode 100644 index 0000000..d18db7a Binary files /dev/null and b/media/acrobat.piv differ diff --git a/media/acrobat0.bmp b/media/acrobat0.bmp new file mode 100644 index 0000000..7e5a52d Binary files /dev/null and b/media/acrobat0.bmp differ diff --git a/media/bukkake.jpg b/media/bukkake.jpg new file mode 100644 index 0000000..a4129c3 Binary files /dev/null and b/media/bukkake.jpg differ diff --git a/media/bukkake.piv b/media/bukkake.piv new file mode 100644 index 0000000..a5aa05d Binary files /dev/null and b/media/bukkake.piv differ diff --git a/media/bukkake0.bmp b/media/bukkake0.bmp new file mode 100644 index 0000000..cdd1218 Binary files /dev/null and b/media/bukkake0.bmp differ diff --git a/media/butterfly.jpg b/media/butterfly.jpg new file mode 100644 index 0000000..a36966b Binary files /dev/null and b/media/butterfly.jpg differ diff --git a/media/butterfly.piv b/media/butterfly.piv new file mode 100644 index 0000000..8575146 Binary files /dev/null and b/media/butterfly.piv differ diff --git a/media/butterfly0.bmp b/media/butterfly0.bmp new file mode 100644 index 0000000..0c4b213 Binary files /dev/null and b/media/butterfly0.bmp differ diff --git a/media/cat.jpg b/media/cat.jpg new file mode 100644 index 0000000..8476ad6 Binary files /dev/null and b/media/cat.jpg differ diff --git a/media/cat.piv b/media/cat.piv new file mode 100644 index 0000000..0604931 Binary files /dev/null and b/media/cat.piv differ diff --git a/media/cat0.bmp b/media/cat0.bmp new file mode 100644 index 0000000..09a907b Binary files /dev/null and b/media/cat0.bmp differ diff --git a/media/circle-jeck.jpg b/media/circle-jeck.jpg new file mode 100644 index 0000000..ff0a796 Binary files /dev/null and b/media/circle-jeck.jpg differ diff --git a/media/circle-jeck0.bmp b/media/circle-jeck0.bmp new file mode 100644 index 0000000..7894e53 Binary files /dev/null and b/media/circle-jeck0.bmp differ diff --git a/media/circle-jerk.piv b/media/circle-jerk.piv new file mode 100644 index 0000000..9df6804 Binary files /dev/null and b/media/circle-jerk.piv differ diff --git a/media/cowboy.jpg b/media/cowboy.jpg new file mode 100644 index 0000000..82e049f Binary files /dev/null and b/media/cowboy.jpg differ diff --git a/media/cowboy.piv b/media/cowboy.piv new file mode 100644 index 0000000..639fabe Binary files /dev/null and b/media/cowboy.piv differ diff --git a/media/cowboy0.bmp b/media/cowboy0.bmp new file mode 100644 index 0000000..f05add9 Binary files /dev/null and b/media/cowboy0.bmp differ diff --git a/media/cowgirl.jpg b/media/cowgirl.jpg new file mode 100644 index 0000000..3b72692 Binary files /dev/null and b/media/cowgirl.jpg differ diff --git a/media/cowgirl.piv b/media/cowgirl.piv new file mode 100644 index 0000000..fcfe9e8 Binary files /dev/null and b/media/cowgirl.piv differ diff --git a/media/cowgirl0.bmp b/media/cowgirl0.bmp new file mode 100644 index 0000000..2b4b98b Binary files /dev/null and b/media/cowgirl0.bmp differ diff --git a/media/daisy-chain.jpg b/media/daisy-chain.jpg new file mode 100644 index 0000000..ebf490b Binary files /dev/null and b/media/daisy-chain.jpg differ diff --git a/media/daisy-chain.piv b/media/daisy-chain.piv new file mode 100644 index 0000000..c532bcd Binary files /dev/null and b/media/daisy-chain.piv differ diff --git a/media/daisy-chain0.bmp b/media/daisy-chain0.bmp new file mode 100644 index 0000000..121d2e6 Binary files /dev/null and b/media/daisy-chain0.bmp differ diff --git a/media/dance.jpg b/media/dance.jpg new file mode 100644 index 0000000..e795908 Binary files /dev/null and b/media/dance.jpg differ diff --git a/media/dance.piv b/media/dance.piv new file mode 100644 index 0000000..e377b3d Binary files /dev/null and b/media/dance.piv differ diff --git a/media/dance0.bmp b/media/dance0.bmp new file mode 100644 index 0000000..2a81c1f Binary files /dev/null and b/media/dance0.bmp differ diff --git a/media/dance2.gif b/media/dance2.gif new file mode 100644 index 0000000..6d0b569 Binary files /dev/null and b/media/dance2.gif differ diff --git a/media/dance2.piv b/media/dance2.piv new file mode 100644 index 0000000..b163c29 Binary files /dev/null and b/media/dance2.piv differ diff --git a/media/dance20.bmp b/media/dance20.bmp new file mode 100644 index 0000000..3b08806 Binary files /dev/null and b/media/dance20.bmp differ diff --git a/media/doggy-style.jpg b/media/doggy-style.jpg new file mode 100644 index 0000000..e8ec8e1 Binary files /dev/null and b/media/doggy-style.jpg differ diff --git a/media/doggy-style.piv b/media/doggy-style.piv new file mode 100644 index 0000000..9971fea Binary files /dev/null and b/media/doggy-style.piv differ diff --git a/media/doggy-style0.bmp b/media/doggy-style0.bmp new file mode 100644 index 0000000..544d4aa Binary files /dev/null and b/media/doggy-style0.bmp differ diff --git a/media/double-penetration.jpg b/media/double-penetration.jpg new file mode 100644 index 0000000..a9643da Binary files /dev/null and b/media/double-penetration.jpg differ diff --git a/media/double-penetration.piv b/media/double-penetration.piv new file mode 100644 index 0000000..029c23a Binary files /dev/null and b/media/double-penetration.piv differ diff --git a/media/double-penetration0.bmp b/media/double-penetration0.bmp new file mode 100644 index 0000000..e6df01b Binary files /dev/null and b/media/double-penetration0.bmp differ diff --git a/media/elephant.jpg b/media/elephant.jpg new file mode 100644 index 0000000..56ec67c Binary files /dev/null and b/media/elephant.jpg differ diff --git a/media/elephant.piv b/media/elephant.piv new file mode 100644 index 0000000..f9ac202 Binary files /dev/null and b/media/elephant.piv differ diff --git a/media/elephant0.bmp b/media/elephant0.bmp new file mode 100644 index 0000000..9a01f3a Binary files /dev/null and b/media/elephant0.bmp differ diff --git a/media/ffm-threesome.piv b/media/ffm-threesome.piv new file mode 100644 index 0000000..41e3f9b Binary files /dev/null and b/media/ffm-threesome.piv differ diff --git a/media/ffm-threesome0.bmp b/media/ffm-threesome0.bmp new file mode 100644 index 0000000..97d54d4 Binary files /dev/null and b/media/ffm-threesome0.bmp differ diff --git a/media/frot.jpg b/media/frot.jpg new file mode 100644 index 0000000..c15f81c Binary files /dev/null and b/media/frot.jpg differ diff --git a/media/frot.piv b/media/frot.piv new file mode 100644 index 0000000..78db127 Binary files /dev/null and b/media/frot.piv differ diff --git a/media/frot0.bmp b/media/frot0.bmp new file mode 100644 index 0000000..6dabdc3 Binary files /dev/null and b/media/frot0.bmp differ diff --git a/media/horse-icon.gif b/media/horse-icon.gif new file mode 100644 index 0000000..6d1c0ea Binary files /dev/null and b/media/horse-icon.gif differ diff --git a/media/horse.jpg b/media/horse.jpg new file mode 100644 index 0000000..3ac0b40 Binary files /dev/null and b/media/horse.jpg differ diff --git a/media/horse.piv b/media/horse.piv new file mode 100644 index 0000000..903d6e4 Binary files /dev/null and b/media/horse.piv differ diff --git a/media/horse0.bmp b/media/horse0.bmp new file mode 100644 index 0000000..167eade Binary files /dev/null and b/media/horse0.bmp differ diff --git a/media/icon.piv b/media/icon.piv new file mode 100644 index 0000000..a9b2a09 Binary files /dev/null and b/media/icon.piv differ diff --git a/media/icon0.bmp b/media/icon0.bmp new file mode 100644 index 0000000..78648ec Binary files /dev/null and b/media/icon0.bmp differ diff --git a/media/kiss-logo.gif b/media/kiss-logo.gif new file mode 100644 index 0000000..8e445b9 Binary files /dev/null and b/media/kiss-logo.gif differ diff --git a/media/kiss.piv b/media/kiss.piv new file mode 100644 index 0000000..597ec6d Binary files /dev/null and b/media/kiss.piv differ diff --git a/media/kiss0.bmp b/media/kiss0.bmp new file mode 100644 index 0000000..289b191 Binary files /dev/null and b/media/kiss0.bmp differ diff --git a/media/kneeling.jpg b/media/kneeling.jpg new file mode 100644 index 0000000..f3cf18c Binary files /dev/null and b/media/kneeling.jpg differ diff --git a/media/kneeling.piv b/media/kneeling.piv new file mode 100644 index 0000000..b6fc951 Binary files /dev/null and b/media/kneeling.piv differ diff --git a/media/kneeling0.bmp b/media/kneeling0.bmp new file mode 100644 index 0000000..79e56f3 Binary files /dev/null and b/media/kneeling0.bmp differ diff --git a/media/lateral.jpg b/media/lateral.jpg new file mode 100644 index 0000000..fb99a34 Binary files /dev/null and b/media/lateral.jpg differ diff --git a/media/lateral.piv b/media/lateral.piv new file mode 100644 index 0000000..0c69fbe Binary files /dev/null and b/media/lateral.piv differ diff --git a/media/lateral0.bmp b/media/lateral0.bmp new file mode 100644 index 0000000..a3eedd6 Binary files /dev/null and b/media/lateral0.bmp differ diff --git a/media/lipstick-director.jpg b/media/lipstick-director.jpg new file mode 100644 index 0000000..36bb9d4 Binary files /dev/null and b/media/lipstick-director.jpg differ diff --git a/media/lipstick-director.piv b/media/lipstick-director.piv new file mode 100644 index 0000000..87f1914 Binary files /dev/null and b/media/lipstick-director.piv differ diff --git a/media/lipstick-director0.bmp b/media/lipstick-director0.bmp new file mode 100644 index 0000000..5d6651f Binary files /dev/null and b/media/lipstick-director0.bmp differ diff --git a/media/lotus.jpg b/media/lotus.jpg new file mode 100644 index 0000000..4981ba0 Binary files /dev/null and b/media/lotus.jpg differ diff --git a/media/lotus.piv b/media/lotus.piv new file mode 100644 index 0000000..2a85117 Binary files /dev/null and b/media/lotus.piv differ diff --git a/media/lotus0.bmp b/media/lotus0.bmp new file mode 100644 index 0000000..0b4dd03 Binary files /dev/null and b/media/lotus0.bmp differ diff --git a/media/missionary.jpg b/media/missionary.jpg new file mode 100644 index 0000000..1e334d2 Binary files /dev/null and b/media/missionary.jpg differ diff --git a/media/missionary.piv b/media/missionary.piv new file mode 100644 index 0000000..94beeb3 Binary files /dev/null and b/media/missionary.piv differ diff --git a/media/missionary0.bmp b/media/missionary0.bmp new file mode 100644 index 0000000..a078702 Binary files /dev/null and b/media/missionary0.bmp differ diff --git a/media/mmf-threesome.piv b/media/mmf-threesome.piv new file mode 100644 index 0000000..cc45535 Binary files /dev/null and b/media/mmf-threesome.piv differ diff --git a/media/mmf-threesome0.bmp b/media/mmf-threesome0.bmp new file mode 100644 index 0000000..9d1b8ed Binary files /dev/null and b/media/mmf-threesome0.bmp differ diff --git a/media/oyster.jpg b/media/oyster.jpg new file mode 100644 index 0000000..1c5b2aa Binary files /dev/null and b/media/oyster.jpg differ diff --git a/media/oyster.piv b/media/oyster.piv new file mode 100644 index 0000000..cb0c3ac Binary files /dev/null and b/media/oyster.piv differ diff --git a/media/oyster0.bmp b/media/oyster0.bmp new file mode 100644 index 0000000..38ffbb4 Binary files /dev/null and b/media/oyster0.bmp differ diff --git a/media/reverse-cowgirl.jpg b/media/reverse-cowgirl.jpg new file mode 100644 index 0000000..93858e8 Binary files /dev/null and b/media/reverse-cowgirl.jpg differ diff --git a/media/reverse-cowgirl.piv b/media/reverse-cowgirl.piv new file mode 100644 index 0000000..9b71874 Binary files /dev/null and b/media/reverse-cowgirl.piv differ diff --git a/media/reverse-cowgirl0.bmp b/media/reverse-cowgirl0.bmp new file mode 100644 index 0000000..1fea346 Binary files /dev/null and b/media/reverse-cowgirl0.bmp differ diff --git a/media/reverse-missionary.jpg b/media/reverse-missionary.jpg new file mode 100644 index 0000000..77d2d33 Binary files /dev/null and b/media/reverse-missionary.jpg differ diff --git a/media/reverse-missionary.piv b/media/reverse-missionary.piv new file mode 100644 index 0000000..07dd827 Binary files /dev/null and b/media/reverse-missionary.piv differ diff --git a/media/reverse-missionary0.bmp b/media/reverse-missionary0.bmp new file mode 100644 index 0000000..be6902f Binary files /dev/null and b/media/reverse-missionary0.bmp differ diff --git a/media/sandwich.jpg b/media/sandwich.jpg new file mode 100644 index 0000000..d8ce861 Binary files /dev/null and b/media/sandwich.jpg differ diff --git a/media/sandwich0.bmp b/media/sandwich0.bmp new file mode 100644 index 0000000..5cef3a2 Binary files /dev/null and b/media/sandwich0.bmp differ diff --git a/media/sanwich.piv b/media/sanwich.piv new file mode 100644 index 0000000..f454141 Binary files /dev/null and b/media/sanwich.piv differ diff --git a/media/space.jpg b/media/space.jpg new file mode 100644 index 0000000..cf2c453 Binary files /dev/null and b/media/space.jpg differ diff --git a/media/space.piv b/media/space.piv new file mode 100644 index 0000000..3ea4e19 Binary files /dev/null and b/media/space.piv differ diff --git a/media/space0.bmp b/media/space0.bmp new file mode 100644 index 0000000..2af9fe8 Binary files /dev/null and b/media/space0.bmp differ diff --git a/media/spoon.jpg b/media/spoon.jpg new file mode 100644 index 0000000..9898995 Binary files /dev/null and b/media/spoon.jpg differ diff --git a/media/spoon.piv b/media/spoon.piv new file mode 100644 index 0000000..d6dcd4f Binary files /dev/null and b/media/spoon.piv differ diff --git a/media/spoon0.bmp b/media/spoon0.bmp new file mode 100644 index 0000000..7248667 Binary files /dev/null and b/media/spoon0.bmp differ diff --git a/media/suspended-congress.jpg b/media/suspended-congress.jpg new file mode 100644 index 0000000..f8166d3 Binary files /dev/null and b/media/suspended-congress.jpg differ diff --git a/media/suspended-congress.piv b/media/suspended-congress.piv new file mode 100644 index 0000000..c7b3655 Binary files /dev/null and b/media/suspended-congress.piv differ diff --git a/media/suspended-congress0.bmp b/media/suspended-congress0.bmp new file mode 100644 index 0000000..e66166f Binary files /dev/null and b/media/suspended-congress0.bmp differ diff --git a/media/suspended.piv b/media/suspended.piv new file mode 100644 index 0000000..79f9f6a Binary files /dev/null and b/media/suspended.piv differ diff --git a/media/suspended0.bmp b/media/suspended0.bmp new file mode 100644 index 0000000..d813108 Binary files /dev/null and b/media/suspended0.bmp differ diff --git a/media/topping.jpg b/media/topping.jpg new file mode 100644 index 0000000..e6436b7 Binary files /dev/null and b/media/topping.jpg differ diff --git a/media/topping.piv b/media/topping.piv new file mode 100644 index 0000000..5d6eba7 Binary files /dev/null and b/media/topping.piv differ diff --git a/media/topping0.bmp b/media/topping0.bmp new file mode 100644 index 0000000..66829dc Binary files /dev/null and b/media/topping0.bmp differ diff --git a/media/toy.jpg b/media/toy.jpg new file mode 100644 index 0000000..46ed7a9 Binary files /dev/null and b/media/toy.jpg differ diff --git a/media/toy.piv b/media/toy.piv new file mode 100644 index 0000000..080463a Binary files /dev/null and b/media/toy.piv differ diff --git a/media/toy0.bmp b/media/toy0.bmp new file mode 100644 index 0000000..d8e10d2 Binary files /dev/null and b/media/toy0.bmp differ diff --git a/media/vibrator.jpg b/media/vibrator.jpg new file mode 100644 index 0000000..22abb1a Binary files /dev/null and b/media/vibrator.jpg differ diff --git a/media/vibrator.piv b/media/vibrator.piv new file mode 100644 index 0000000..aba9369 Binary files /dev/null and b/media/vibrator.piv differ diff --git a/media/vibrator0.bmp b/media/vibrator0.bmp new file mode 100644 index 0000000..a6b8f71 Binary files /dev/null and b/media/vibrator0.bmp differ diff --git a/media/victory.jpg b/media/victory.jpg new file mode 100644 index 0000000..94de81f Binary files /dev/null and b/media/victory.jpg differ diff --git a/media/victory.piv b/media/victory.piv new file mode 100644 index 0000000..322c0dd Binary files /dev/null and b/media/victory.piv differ diff --git a/media/victory0.bmp b/media/victory0.bmp new file mode 100644 index 0000000..e73babd Binary files /dev/null and b/media/victory0.bmp differ diff --git a/media/wheelbarrow.jpg b/media/wheelbarrow.jpg new file mode 100644 index 0000000..d9832a8 Binary files /dev/null and b/media/wheelbarrow.jpg differ diff --git a/media/wheelbarrow.piv b/media/wheelbarrow.piv new file mode 100644 index 0000000..67d3c23 Binary files /dev/null and b/media/wheelbarrow.piv differ diff --git a/media/wheelbarrow0.bmp b/media/wheelbarrow0.bmp new file mode 100644 index 0000000..0078099 Binary files /dev/null and b/media/wheelbarrow0.bmp differ diff --git a/news.php b/news.php new file mode 100644 index 0000000..2e43fd2 --- /dev/null +++ b/news.php @@ -0,0 +1,46 @@ + +
+back +
+

Advertise

+

+This advertises your position on your newsfeed. You may need to change the +settings for Sex so that +it has permission to write to your wall. You can always delete to story or +change the size. +

+
+require_login(); + $bundle = 48640476760; + + if(isset($_POST['sex']) && $_POST['sex'] == 'foo') { + if(($pos = SexExtract($fb->api_client->profile_getFBML($user))) == null) { + echo 'That\'s fucked up. Try choosing your position again.'; + break; + } + echo 'Publishing '.$pos.' . . . '; + $replace = '{"pos":"'.$sex[$pos][0].'","images":[{'. + '"src":"http://www.cs.mcgill.ca/~nedelm/sex/stuff/'.$pos.'.jpg",'. + '"href":"http://apps.facebook.com/sexfuck/"}]}'; + try { + $fb->api_client->feed_publishUserAction($bundle, $replace, null, null, 2); + } catch(Exception $e) { + echo 'OhNo! '.$e->getMessage().'.'; + } + } else { + echo '
'. + ''. + ''; + } +?> +
diff --git a/post-remove.php b/post-remove.php new file mode 100644 index 0000000..dee26df --- /dev/null +++ b/post-remove.php @@ -0,0 +1,15 @@ + +

Removed Sex

+require_login(); + $old = SexExtract($fb->api_client->profile_getFBML($user)); + + SexChange($old, null); +?> +

Erasing all trace.

diff --git a/privacy.html b/privacy.html new file mode 100644 index 0000000..7fa486b --- /dev/null +++ b/privacy.html @@ -0,0 +1,13 @@ + + + +

Privacy

+

+Sex stores no information; it is completely contained in +Facebook. Statistics are anonymous, completely divorced from +user information; it just increments a count. I do a type +of Stokes, using endpoints, but I can have no idea if I've +covered all the bases. +

+ diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..346fea2 --- /dev/null +++ b/readme.txt @@ -0,0 +1,8 @@ +Copyright (C) 2008 Neil Edelman, see copying.txt. +neil dot edelman each mail dot mcgill dot ca + +A Facebook application, Sex. I coded it in a day, 2008-09-27. +Now, Facebook doesn't support creating content on the users' +profile since so many apps abused this system. It has had a +brief run. I used Pivot to make the graphics, which are cute, +and available under the gpl. diff --git a/sex b/sex new file mode 100644 index 0000000..f7957b1 --- /dev/null +++ b/sex @@ -0,0 +1,34 @@ +68 2 +69 0 +acrobat 0 +bukkake 0 +butterfly 1 +cat 0 +circle-jerk 0 +cowboy 0 +cowgirl 0 +daisy-chain 0 +doggy-style 0 +double-penetration 0 +elephant 3 +fff-threesome 0 +ffm-daisy 0 +frot 0 +horse 1 +kneeling 0 +lateral 0 +lipstick-director 2 +lotus 0 +missionary 1 +mm-topping 0 +oyster 2 +reverse-cowgirl 1 +reverse-missionary 0 +sandwich 0 +space 1 +spoon 0 +suspended-congress 0 +toy 1 +vibrator 0 +victory 1 +wheelbarrow 2 diff --git a/sex.php b/sex.php new file mode 100644 index 0000000..43da532 --- /dev/null +++ b/sex.php @@ -0,0 +1,96 @@ + +array("68", 0), + "69" =>array("69", 0), + "acrobat" =>array("Acrobat", 0), + "bukkake" =>array("Bukkake", 0), + "circle-jerk" =>array("Circle-Jerk", 0), + "cowboy" =>array("Cowboy", 0), + "cowgirl" =>array("Cowgirl", 0), + "daisy-chain" =>array("Daisy-Chain", 0), + "doggy-style" =>array("Doggy Style", 0), + "double-penetration"=>array("Double Penetration", 0), + "fff-threesome" =>array("FFF Threesome", 0), + "ffm-daisy" =>array("FFM Daisy-Chain", 0), + "frot" =>array("MM Frot", 0), + "horse" =>array("Bestiality", 0), + "kneeling" =>array("MF Kneeling", 0), + "lipstick-director" =>array("Lipstick Director", 0), + "lotus" =>array("Lotus", 0), + "missionary" =>array("Missionary", 0), + "mm-topping" =>array("MM Topping", 0), + "reverse-cowgirl" =>array("Reverse Cowgirl", 0), + "reverse-missionary"=>array("Reverse Missionary", 0), + "sandwich" =>array("Sandwich", 0), + "suspended-congress"=>array("Suspended Congress", 0), + "toy" =>array("FF Toy", 0), + "vibrator" =>array("Vibrator", 0), + "victory" =>array("Victory", 0), + "wheelbarrow" =>array("Wheelbarrow", 0) + ); + + // load from sex file + function SexLoad() { + global $sex; + $contents = file_get_contents("sex") or die("Error with sex."); + $lines = split("\n", $contents); + foreach($lines as $line) { + if(!strcmp($line, '')) break; + list($n, $v) = split("\t", $line); + if(!isset($n) or !isset($v) or !isset($sex[$n]) or !is_numeric($v)) continue; + $sex[$n][1] = $v; + } + } + + // overwrite the sex file with the values currently in sex + function SexSave() { + global $sex; + $fp = fopen("sex", "w"); + foreach($sex as $key => $s) fwrite($fp, $key."\t".$s[1]."\n"); + fclose($fp); + } + + // change old to new positions + function SexChange($old, $new) { + global $sex; + if(isset($sex[$old]) and $sex[$old][1] > 0) $sex[$old][1]--; + if(isset($sex[$new])) $sex[$new][1]++; + } + + // display the question + function SexInterface() { + global $sex; + echo ''; + echo ''; + echo ''; + echo ''; + } + + // display the stats + function SexDisplay() { + global $sex; + $max = 1; foreach($sex as $s) if($s[1] > $max) $max = $s[1]; + echo ''; + foreach($sex as $s) { + $rel = round(($s[1] * 255.0) / $max); + echo ''; + } + echo '
'.$s[0].''.$s[1].''; + echo ''.$rel.'
'; + } + + // extracts image name from the html, a real hack + function SexExtract($html) { + $name = substr(strstr($html, ' diff --git a/stats.php b/stats.php new file mode 100644 index 0000000..d756d9a --- /dev/null +++ b/stats.php @@ -0,0 +1,17 @@ + +
+back +
+

Statistics

+

+ +

+

+These statistics may not be exact. See why. +

diff --git a/stuff/68.jpg b/stuff/68.jpg new file mode 100644 index 0000000..55879f9 Binary files /dev/null and b/stuff/68.jpg differ diff --git a/stuff/69.jpg b/stuff/69.jpg new file mode 100644 index 0000000..eea778b Binary files /dev/null and b/stuff/69.jpg differ diff --git a/stuff/acrobat.jpg b/stuff/acrobat.jpg new file mode 100644 index 0000000..1127631 Binary files /dev/null and b/stuff/acrobat.jpg differ diff --git a/stuff/bukkake.jpg b/stuff/bukkake.jpg new file mode 100644 index 0000000..85f999f Binary files /dev/null and b/stuff/bukkake.jpg differ diff --git a/stuff/butterfly.jpg b/stuff/butterfly.jpg new file mode 100644 index 0000000..a36966b Binary files /dev/null and b/stuff/butterfly.jpg differ diff --git a/stuff/cat.jpg b/stuff/cat.jpg new file mode 100644 index 0000000..8476ad6 Binary files /dev/null and b/stuff/cat.jpg differ diff --git a/stuff/circle-jerk.jpg b/stuff/circle-jerk.jpg new file mode 100644 index 0000000..44b10a4 Binary files /dev/null and b/stuff/circle-jerk.jpg differ diff --git a/stuff/cowboy.jpg b/stuff/cowboy.jpg new file mode 100644 index 0000000..82e049f Binary files /dev/null and b/stuff/cowboy.jpg differ diff --git a/stuff/cowgirl.jpg b/stuff/cowgirl.jpg new file mode 100644 index 0000000..279001d Binary files /dev/null and b/stuff/cowgirl.jpg differ diff --git a/stuff/daisy-chain.jpg b/stuff/daisy-chain.jpg new file mode 100644 index 0000000..1e9f79c Binary files /dev/null and b/stuff/daisy-chain.jpg differ diff --git a/stuff/dance.jpg b/stuff/dance.jpg new file mode 100644 index 0000000..e795908 Binary files /dev/null and b/stuff/dance.jpg differ diff --git a/stuff/doggy-style.jpg b/stuff/doggy-style.jpg new file mode 100644 index 0000000..66a9e5d Binary files /dev/null and b/stuff/doggy-style.jpg differ diff --git a/stuff/double-penetration.jpg b/stuff/double-penetration.jpg new file mode 100644 index 0000000..7794255 Binary files /dev/null and b/stuff/double-penetration.jpg differ diff --git a/stuff/fff-threesome.jpg b/stuff/fff-threesome.jpg new file mode 100644 index 0000000..9208548 Binary files /dev/null and b/stuff/fff-threesome.jpg differ diff --git a/stuff/ffm-daisy.jpg b/stuff/ffm-daisy.jpg new file mode 100644 index 0000000..61e0fe9 Binary files /dev/null and b/stuff/ffm-daisy.jpg differ diff --git a/stuff/frot.jpg b/stuff/frot.jpg new file mode 100644 index 0000000..6ff2694 Binary files /dev/null and b/stuff/frot.jpg differ diff --git a/stuff/horse.jpg b/stuff/horse.jpg new file mode 100644 index 0000000..dd11269 Binary files /dev/null and b/stuff/horse.jpg differ diff --git a/stuff/kneeling.jpg b/stuff/kneeling.jpg new file mode 100644 index 0000000..f3cf18c Binary files /dev/null and b/stuff/kneeling.jpg differ diff --git a/stuff/lateral.jpg b/stuff/lateral.jpg new file mode 100644 index 0000000..fb99a34 Binary files /dev/null and b/stuff/lateral.jpg differ diff --git a/stuff/lipstick-director.jpg b/stuff/lipstick-director.jpg new file mode 100644 index 0000000..36bb9d4 Binary files /dev/null and b/stuff/lipstick-director.jpg differ diff --git a/stuff/lotus.jpg b/stuff/lotus.jpg new file mode 100644 index 0000000..1a097ae Binary files /dev/null and b/stuff/lotus.jpg differ diff --git a/stuff/missionary.jpg b/stuff/missionary.jpg new file mode 100644 index 0000000..648f35d Binary files /dev/null and b/stuff/missionary.jpg differ diff --git a/stuff/oyster.jpg b/stuff/oyster.jpg new file mode 100644 index 0000000..1c5b2aa Binary files /dev/null and b/stuff/oyster.jpg differ diff --git a/stuff/reverse-cowgirl.jpg b/stuff/reverse-cowgirl.jpg new file mode 100644 index 0000000..b74f400 Binary files /dev/null and b/stuff/reverse-cowgirl.jpg differ diff --git a/stuff/reverse-missionary.jpg b/stuff/reverse-missionary.jpg new file mode 100644 index 0000000..728af00 Binary files /dev/null and b/stuff/reverse-missionary.jpg differ diff --git a/stuff/sandwich.jpg b/stuff/sandwich.jpg new file mode 100644 index 0000000..5de3212 Binary files /dev/null and b/stuff/sandwich.jpg differ diff --git a/stuff/spoon.jpg b/stuff/spoon.jpg new file mode 100644 index 0000000..9898995 Binary files /dev/null and b/stuff/spoon.jpg differ diff --git a/stuff/suspended-congress.jpg b/stuff/suspended-congress.jpg new file mode 100644 index 0000000..9a92251 Binary files /dev/null and b/stuff/suspended-congress.jpg differ diff --git a/stuff/topping.jpg b/stuff/topping.jpg new file mode 100644 index 0000000..b616a5a Binary files /dev/null and b/stuff/topping.jpg differ diff --git a/stuff/toy.jpg b/stuff/toy.jpg new file mode 100644 index 0000000..f66d1dc Binary files /dev/null and b/stuff/toy.jpg differ diff --git a/stuff/vibrator.jpg b/stuff/vibrator.jpg new file mode 100644 index 0000000..22abb1a Binary files /dev/null and b/stuff/vibrator.jpg differ diff --git a/stuff/victory.jpg b/stuff/victory.jpg new file mode 100644 index 0000000..94de81f Binary files /dev/null and b/stuff/victory.jpg differ diff --git a/stuff/wheelbarrow.jpg b/stuff/wheelbarrow.jpg new file mode 100644 index 0000000..d9832a8 Binary files /dev/null and b/stuff/wheelbarrow.jpg differ