get_channel(); if(($aid) && (x($_REQUEST,'channel'))) { // Only change channel if it is different than the current channel if($channel && x($channel,'channel_address') && $channel['channel_address'] != $_REQUEST['channel']) { $c = q("select channel_id from channel where channel_address = '%s' and channel_account_id = %d limit 1", dbesc($_REQUEST['channel']), intval($aid) ); if((! $c) || (! change_channel($c[0]['channel_id']))) return false; } } if ($_SESSION["allow_api"]) return local_channel(); return false; } function api_date($str){ //Wed May 23 06:01:13 +0000 2007 return datetime_convert('UTC', 'UTC', $str, "D M d H:i:s +0000 Y" ); } function api_register_func($path, $func, $auth=false){ global $API; $API[$path] = array('func'=>$func, 'auth'=>$auth); } /** * Simple HTTP Login */ function api_login(&$a){ // login with oauth try { $oauth = new FKOAuth1(); $req = OAuthRequest::from_request(); list($consumer,$token) = $oauth->verify_request($req); // list($consumer,$token) = $oauth->verify_request(OAuthRequest::from_request()); if (!is_null($token)){ $oauth->loginUser($token->uid); call_hooks('logged_in', $a->user); return; } echo __file__.__line__.__function__."
"; 
			var_dump($consumer, $token); 
			die();
		}
		catch(Exception $e) {
			logger(__file__.__line__.__function__."\n".$e);
		}
		
		// workaround for HTTP-auth in CGI mode
		if(x($_SERVER,'REDIRECT_REMOTE_USER')) {
		 	$userpass = base64_decode(substr($_SERVER["REDIRECT_REMOTE_USER"],6)) ;
			if(strlen($userpass)) {
			 	list($name, $password) = explode(':', $userpass);
				$_SERVER['PHP_AUTH_USER'] = $name;
				$_SERVER['PHP_AUTH_PW'] = $password;
			}
		}
		if(x($_SERVER,'HTTP_AUTHORIZATION')) {
		 	$userpass = base64_decode(substr($_SERVER["HTTP_AUTHORIZATION"],6)) ;
			if(strlen($userpass)) {
			 	list($name, $password) = explode(':', $userpass);
				$_SERVER['PHP_AUTH_USER'] = $name;
				$_SERVER['PHP_AUTH_PW'] = $password;
			}
		}
		if (!isset($_SERVER['PHP_AUTH_USER'])) {
		   logger('API_login: ' . print_r($_SERVER,true), LOGGER_DEBUG);
		    header('WWW-Authenticate: Basic realm="Red"');
		    header('HTTP/1.0 401 Unauthorized');
		    die('This api requires login');
		}
		
		// process normal login request
		require_once('include/auth.php');
		$channel_login = 0;
		$record = account_verify_password($_SERVER['PHP_AUTH_USER'],$_SERVER['PHP_AUTH_PW']);
		if(! $record) {
	        $r = q("select * from channel where channel_address = '%s' limit 1",
    	        dbesc($_SERVER['PHP_AUTH_USER'])
        	);
        	if ($r) {
            	$x = q("select * from account where account_id = %d limit 1",
                	intval($r[0]['channel_account_id'])
            	);
            	if ($x) {
					$record = account_verify_password($x[0]['account_email'],$_SERVER['PHP_AUTH_PW']);
					if($record)
						$channel_login = $r[0]['channel_id'];
				}
			}
			if(! $record) {	
				logger('API_login failure: ' . print_r($_SERVER,true), LOGGER_DEBUG);
				header('WWW-Authenticate: Basic realm="Red"');
				header('HTTP/1.0 401 Unauthorized');
				die('This api requires login');
			}
		}
		require_once('include/security.php');
		authenticate_success($record);
		if($channel_login)
			change_channel($channel_login);
		$_SESSION['allow_api'] = true;
	}
	
	/**************************
	 *  MAIN API ENTRY POINT  *
	 **************************/
	function api_call(&$a){
		GLOBAL $API, $called_api;
		// preset
		$type="json";
		foreach ($API as $p=>$info){
			if (strpos($a->query_string, $p)===0){
				$called_api= explode("/",$p);
				//unset($_SERVER['PHP_AUTH_USER']);
				if ($info['auth'] === true && api_user() === false) {
						api_login($a);
				}
				load_contact_links(api_user());
				$channel = $a->get_channel();
				logger('API call for ' . $channel['channel_name'] . ': ' . $a->query_string);
				logger('API parameters: ' . print_r($_REQUEST,true));
				$type="json";
				if (strpos($a->query_string, ".xml")>0) $type="xml";
				if (strpos($a->query_string, ".json")>0) $type="json";
				if (strpos($a->query_string, ".rss")>0) $type="rss";
				if (strpos($a->query_string, ".atom")>0) $type="atom";
				if (strpos($a->query_string, ".as")>0) $type="as";
				$r = call_user_func($info['func'], $a, $type);
				if ($r===false) return;
				switch($type){
					case "xml":
						$r = mb_convert_encoding($r, "UTF-8",mb_detect_encoding($r));
						header ("Content-Type: text/xml");
						return ''."\n".$r;
						break;
					case "json":
						header ("Content-Type: application/json");
						foreach($r as $rr)
							$json = json_encode($rr);
						if ($_GET['callback'])
							$json = $_GET['callback']."(".$json.")";
						return $json; 
						break;
					case "rss":
						header ("Content-Type: application/rss+xml");
						return ''."\n".$r;
						break;
					case "atom":
						header ("Content-Type: application/atom+xml");
						return ''."\n".$r;
						break;
					case "as":
						//header ("Content-Type: application/json");
						//foreach($r as $rr)
						//    return json_encode($rr);
						return json_encode($r);
						break;
				}
				//echo ""; var_dump($r); die();
			}
		}
		header("HTTP/1.1 404 Not Found");
		logger('API call not implemented: '.$a->query_string." - ".print_r($_REQUEST,true));
		$r = 'not implemented 0.9.7 ' . "\r\n";
			killme();
		}
		elseif($type === 'json') {
			header("Content-type: application/json");
			echo '"0.9.7"';
			killme();
		}
	}
	api_register_func('api/statusnet/version','api_statusnet_version',false);
	function api_friendica_version(&$a,$type) {
		if($type === 'xml') {
			header("Content-type: application/xml");
			echo '' . "\r\n" . '' . RED_VERSION . ' ' . "\r\n";
			killme();
		}
		elseif($type === 'json') {
			header("Content-type: application/json");
			echo '"' . RED_VERSION . '"';
			killme();
		}
	}
	api_register_func('api/friendica/version','api_friendica_version',false);
	api_register_func('api/red/version','api_friendica_version',false);
	function api_ff_ids(&$a,$type,$qtype) {
		if(! api_user())
			return false;
		// For Red, the closest thing we can do to figure out if you're friends is if both of you are sending each other your streams.
		// This won't work if either of you send your stream to everybody on the network
		if($qtype == 'friends')
			$sql_extra = sprintf(" AND ( abook_their_perms & %d )>0 and ( abook_my_perms & %d )>0 ", intval(PERMS_W_STREAM), intval(PERMS_W_STREAM));
		if($qtype == 'followers')
			$sql_extra = sprintf(" AND ( abook_my_perms & %d )>0 and not ( abook_their_perms & %d )>0 ", intval(PERMS_W_STREAM), intval(PERMS_W_STREAM));
 
		$r = q("SELECT abook_id FROM abook where abook_flags = 0 and abook_channel = %d $sql_extra",
			intval(api_user())
		);
		if(is_array($r)) {
			if($type === 'xml') {
				header("Content-type: application/xml");
				echo '' . "\r\n" . '' . "\r\n";
				foreach($r as $rr)
					echo '' . $rr['abook_id'] . ' ' . "\r\n";
				echo ' ' . "\r\n";
				killme();
			}
			elseif($type === 'json') {
				$ret = array();
				header("Content-type: application/json");
				foreach($r as $rr) $ret[] = $rr['abook_id'];
				echo json_encode($ret);
				killme();
			}
		}
	}
	function api_friends_ids(&$a,$type) {
		api_ff_ids($a,$type,'friends');
	}
	function api_followers_ids(&$a,$type) {
		api_ff_ids($a,$type,'followers');
	}
	api_register_func('api/friends/ids','api_friends_ids',true);
	api_register_func('api/followers/ids','api_followers_ids',true);
	function api_direct_messages_new(&$a, $type) {
		if (api_user()===false) return false;
		
		if (!x($_POST, "text") || !x($_POST,"screen_name")) return;
		$sender = api_get_user($a);
		
		require_once("include/message.php");
		// in a decentralised world the screen name is ambiguous
		$r = q("SELECT `abook_id` FROM `abook` left join xchan on abook_xchan = xchan_hash WHERE `abook_channel`=%d and xchan_addr like '%s'",
				intval(api_user()),
				dbesc($_POST['screen_name'] . '@%')
		);
		$recipient = api_get_user($a, $r[0]['abook_id']);			
		$replyto = '';
		$sub     = '';
		if (x($_REQUEST,'replyto')) {
			$r = q('SELECT `parent_mid`, `title` FROM `mail` WHERE `uid`=%d AND `id`=%d',
					intval(api_user()),
					intval($_REQUEST['replyto']));
			$replyto = $r[0]['parent_mid'];
			$sub     = $r[0]['title'];
		}
		else {
			if (x($_REQUEST,'title')) {
				$sub = $_REQUEST['title'];
			}
			else {
				$sub = ((strlen($_POST['text'])>10)?substr($_POST['text'],0,10)."...":$_POST['text']);
			}
		}
		$id = send_message($recipient['id'], $_POST['text'], $sub, $replyto);
		if ($id>-1) {
			$r = q("SELECT * FROM `mail` WHERE id=%d", intval($id));
			$ret = api_format_messages($r[0], $recipient, $sender);
		
		} else {
			$ret = array("error"=>$id);	
		}
		
		$data = Array('$messages'=>$ret);
		
		switch($type){
			case "atom":
			case "rss":
				$data = api_rss_extra($a, $data, $user_info);
		}
				
		return  api_apply_template("direct_messages", $type, $data);
				
	}
	api_register_func('api/direct_messages/new','api_direct_messages_new',true);
	function api_direct_messages_box(&$a, $type, $box) {
		if (api_user()===false) return false;
		
		$user_info = api_get_user($a);
		
		// params
		$count = (x($_GET,'count')?$_GET['count']:20);
		$page = (x($_REQUEST,'page')?$_REQUEST['page']-1:0);
		if ($page<0) $page=0;
		
		$start = $page*$count;
		$channel = $a->get_channel();		
		$profile_url = $a->get_baseurl() . '/channel/' . $channel['channel_address'];
		if ($box=="sentbox") {
			$sql_extra = "`from_xchan`='".dbesc( $channel['channel_hash'] )."'";
		}
		elseif ($box=="conversation") {
			$sql_extra = "`parent_mid`='".dbesc( $_GET["uri"] )  ."'";
		}
		elseif ($box=="all") {
			$sql_extra = "true";
		}
		elseif ($box=="inbox") {
			$sql_extra = "`from_xchan`!='".dbesc( $channel['channel_hash'] )."'";
		}
		
		$r = q("SELECT * FROM `mail` WHERE channel_id = %d AND $sql_extra ORDER BY created DESC LIMIT %d OFFSET %d",
				intval(api_user()),
				intval($count), intval($start)
		);
		
		$ret = Array();
		if($r) {
			foreach($r as $item) {
				if ($box == "inbox" || $item['from-url'] != $profile_url){
					$recipient = $user_info;
					// fixme to lookup recipient
					$sender = api_get_user($a);
				}
				elseif ($box == "sentbox" || $item['from-url'] != $profile_url){
					// fixme to lookup recipient
					$recipient = api_get_user($a);
					$sender = $user_info;
				}
	
				$ret[]=api_format_messages($item, $recipient, $sender);
			}
		}
		
		$data = array('$messages' => $ret);
		switch($type){
			case "atom":
			case "rss":
				$data = api_rss_extra($a, $data, $user_info);
		}
				
		return  api_apply_template("direct_messages", $type, $data);
		
	}
	function api_direct_messages_sentbox(&$a, $type){
		return api_direct_messages_box($a, $type, "sentbox");
	}
	function api_direct_messages_inbox(&$a, $type){
		return api_direct_messages_box($a, $type, "inbox");
	}
	function api_direct_messages_all(&$a, $type){
		return api_direct_messages_box($a, $type, "all");
	}
	function api_direct_messages_conversation(&$a, $type){
		return api_direct_messages_box($a, $type, "conversation");
	}
	api_register_func('api/direct_messages/conversation','api_direct_messages_conversation',true);
	api_register_func('api/direct_messages/all','api_direct_messages_all',true);
	api_register_func('api/direct_messages/sent','api_direct_messages_sentbox',true);
	api_register_func('api/direct_messages','api_direct_messages_inbox',true);
	function api_oauth_request_token(&$a, $type){
		try{
			$oauth = new FKOAuth1();
			$req = OAuthRequest::from_request();
logger('Req: ' . var_export($req,true));
			$r = $oauth->fetch_request_token($req);
		}catch(Exception $e){
			logger('oauth_exception: ' . print_r($e->getMessage(),true));
			echo "error=". OAuthUtil::urlencode_rfc3986($e->getMessage()); 
			killme();
		}
		echo $r;
		killme();	
	}
	function api_oauth_access_token(&$a, $type){
		try{
			$oauth = new FKOAuth1();
			$req = OAuthRequest::from_request();
			$r = $oauth->fetch_access_token($req);
		}catch(Exception $e){
			echo "error=". OAuthUtil::urlencode_rfc3986($e->getMessage()); killme();
		}
		echo $r;
		killme();			
	}
	api_register_func('api/oauth/request_token', 'api_oauth_request_token', false);
	api_register_func('api/oauth/access_token', 'api_oauth_access_token', false);
/*
Not implemented by now:
favorites
favorites/create
favorites/destroy
statuses/retweets_of_me
friendships/create
friendships/destroy
friendships/exists
friendships/show
account/update_location
account/update_profile_background_image
account/update_profile_image
blocks/create
blocks/destroy
Not implemented in status.net:
statuses/retweeted_to_me
statuses/retweeted_by_me
direct_messages/destroy
account/end_session
account/update_delivery_device
notifications/follow
notifications/leave
blocks/exists
blocks/blocking
lists
*/