/**
  * Run the security call and see what falls out.
  * 
  * @param \Raml\SecurityScheme $securityscheme_obj The security scheme to process the call data for
  * @param \V1\APICall $apicall_obj					The APICall object
  * 
  * @return bool True on success, or false on fail
  */
 public function run(\Raml\SecurityScheme $securityscheme_obj, \V1\APICall $apicall_obj)
 {
     /**
      * @link https://en.wikipedia.org/wiki/Basic_access_authentication
      */
     $credentials = $apicall_obj->get_credentials();
     // Make sure that we have the required data.
     if (empty($credentials['BASIC_USERNAME']) || empty($credentials['BASIC_PASSWORD'])) {
         return false;
     }
     $encoded_credentials = 'Basic ' . base64_encode($credentials['BASIC_USERNAME'] . ':' . $credentials['BASIC_PASSWORD']);
     $apicall_obj->set_header('Authorization', $encoded_credentials);
     return true;
 }
 /**
  * Configure a dynamic call
  *
  * @return mixed The \V1\APICall object or the error array on fail
  */
 public static function configure_call()
 {
     // Dynamic calls don't work through JS.
     if (\Session::get('public', true) === true) {
         return \Utility::format_error(400, \V1\Err::NO_JS_CALLS, \Lang::get('v1::errors.no_js_calls'));
     }
     // We need API configuration data.
     if (!is_array(\V1\APIRequest::get('configure', false))) {
         return \Utility::format_error(400, \V1\Err::MISSING_CONFIGURE, \Lang::get('v1::errors.missing_configure'));
     }
     // For some reason we can't parse the RAML, so we throw a 500 error.
     if (($api_def = \V1\RAML::parse()) === false) {
         return \Utility::format_error(500);
     } elseif (is_array($api_def)) {
         // Specific error
         return $api_def;
     }
     if (is_string($uri = \V1\APIRequest::get('configure.uri')) && is_string($method = \V1\APIRequest::get('configure.method'))) {
         $api_call = null;
         // Custom calls
         if (\V1\APIRequest::get('api') === 'custom') {
             if (is_string($url = \V1\APIRequest::get('configure.url')) && !empty($url)) {
                 $api_call = \V1\APICall::forge($api_def, $method, $uri, null, true, $url);
             }
             return \Utility::format_error(400, \V1\Err::NO_URL, \Lang::get('v1::errors.no_url'));
         }
         $api_data = \V1\Model\APIs::get_api();
         try {
             // Is it a valid resource?
             $api_def->getResourceByUri($uri)->getMethod($method);
             // We'll validate the call unless both the API provider and calling script deny that protection.
             $custom_dynamic = false;
             if ($api_data['force_validation'] === 0 && \V1\APIRequest::get('no-validate', false) === true) {
                 $custom_dynamic = true;
             }
             $api_call = \V1\APICall::forge($api_def, $method, $uri, null, $custom_dynamic);
         } catch (\Raml\Exception\BadParameter\ResourceNotFoundException $e) {
             // Does the API Provider allow for unconfigured static calls on their server?
             if ($api_data['allow_custom_dynamic'] === 1) {
                 $api_call = \V1\APICall::forge($api_def, $method, $uri, null, true);
             }
         }
         if (is_object($api_call)) {
             if (!empty($api_call->get_errors())) {
                 // Errors from \APICall
                 return $api_call->get_errors();
             } else {
                 // Return the \APICall object
                 return $api_call;
             }
         }
     }
     // Not found
     return \Utility::format_error(400, \V1\Err::BAD_DYNAMIC, \Lang::get('v1::errors.bad_dynamic'));
 }
 /**
  * Process the request data to include the new headers.
  * 
  * @param string $request The entire request to the remote server including headers and the body
  * @return string The altered $request data
  */
 public function before_send($request)
 {
     /**
      * @TODO Finish processing the HA2 data, then form the header. Check if APICall set a
      * header with the same name, and if so, overwrite it in the request string.
      */
     $request_parts = explode("\r\n\r\n", $request);
     $uri = $this->apicall->get_uri();
     $method = \Str::upper($this->apicall->get_method());
     /**
      * HA2
      */
     if (empty($this->qop) || \Str::lower($this->qop === 'auth')) {
         $this->ha2 = md5($method . ':' . $uri);
     } else {
         /**
          * If we don't have a body, then we hash null.
          * 
          * @link http://curl.haxx.se/mail/tracker-2013-06/0083.html
          */
         $body = empty($request_parts[1]) ? 'd41d8cd98f00b204e9800998ecf8427e' : md5($request_parts[1]);
         $this->ha2 = md5($method . ':' . $uri . ':' . $body);
     }
     /**
      * RESPONSE
      */
     if (empty($this->qop)) {
         $response = md5($this->ha1 . ':' . $this->nonce . ':' . $this->ha2);
     } else {
         $response = md5($this->ha1 . ':' . $this->nonce . ':' . $this->nonce_count . ':' . $this->cnonce . ':' . $this->qop . ':' . $this->ha2);
     }
     // Protect against CRLF, and add the header.
     $auth_header = str_replace("\r\n", '', 'Authorization: Digest username="******",realm="' . $this->realm . '",nonce="' . $this->nonce . '",uri="' . $uri . '",qop=' . $this->qop . ',nc=' . $this->nonce_count . ',cnonce="' . $this->cnonce . '",response="' . $response . '",opaque="' . $this->opaque . '"');
     $request_parts[0] .= "\r\n" . $auth_header;
     $request = implode("\r\n\r\n", $request_parts);
     return $request;
 }
 /**
  * Call the remote API server
  * 
  * @param \V1\APICall $apicall_obj The APICall object we're using to make calls
  * @return array The array of data ready for display (Response array or error array)
  */
 public function make_the_call(\V1\APICall $apicall_obj)
 {
     /**
      * RUNCLE RICK'S RAD RUN CALL :)
      */
     $api = \V1\Model\APIs::get_api();
     $account = \V1\Model\Account::get_account();
     /*
      * When we make a call from the local server, we'll get the localhost IP. If that's the case,
      * we'll set our public IP. DO NOT use X-Forwarded-For in the request headers to us. It's unreliable.
      * We'll still set our X-Forwarded-For in case the API provider wishes to use it.
      */
     $forwarded_for = \Input::real_ip('0.0.0.0', true);
     if ($internal_call = \Utility::is_internal_call()) {
         $forwarded_for = \Config::get('engine.call_test_ip');
     }
     /*
      * Add our own headers to allow for authenticating our server and customers. We overwrite any
      * of these headers that were specified by the API Provider through RAML, or by the developer
      * through thier configuration.
      */
     $headers = static::get_headers($apicall_obj->get_headers());
     $call = array('url' => $apicall_obj->get_url(), 'method' => $apicall_obj->get_method(), 'headers' => $headers, 'body' => $apicall_obj->get_method_params(), 'body-type' => $apicall_obj->get_body_type());
     if (\Fuel::$env !== 'production' && \Config::get('engine.dev_disable_live_calls', false) === true) {
         /**
          * In dev mode we can disable calls to the remote server. Feel free to change the
          * dummy response to whatever you'd like to.
          */
         $response = array('status' => 200, 'headers' => array('X-Dev-Mode' => 'Dummy header'), 'body' => array('dummy_key' => 'dummy_val'));
         return \Utility::format_response(200, $response);
     } else {
         /*
          * We'll see if anyone got a cached entry into our system while we were configuring stuff.
          * That way we'll save time.
          */
         if (\V1\APIRequest::is_static() && is_array($cached_data = \V1\Call\StaticCall::get_call_cache())) {
             // Return the response-formatted data from the cached entry.
             return $cached_data;
         }
         $queued = \V1\Socket::forge()->queue_call(\V1\APIRequest::get('api'), $call, $apicall_obj);
     }
     // Non-Data Calls grab the request right away.
     if (\Session::get('data_call', false) === false) {
         if ($queued === false) {
             // Server unavailable
             return \Utility::format_error(503, \Err::SERVER_ERROR, \Lang::get('v1::errors.remote_unavailable'));
         }
         // Pull the results.
         $result = \V1\Socket::forge()->get_results();
         if (is_array($result)) {
             // We only have one call.
             return $result[\V1\APIRequest::get('api')][0];
         } else {
             // If the request failed with false, it means that all streams timed out.
             return \Utility::format_error(500);
         }
     }
     $dc_response = array('status' => 200, 'headers' => array(), 'body' => \V1\Constant::QUEUED_CALL);
     // In Data Call mode we just signify that we've queued the call.
     return \Utility::format_response(200, $dc_response);
 }
 /**
  * Run the request
  * 
  * @param \Raml\SecurityScheme $securityscheme_obj The security scheme to process the call data for
  * @param \V1\APICall $apicall_obj					The APICall object
  * 
  * @return mixed The object we just completed or an array describing the next step in the security process
  */
 public function run(\Raml\SecurityScheme $securityscheme_obj, \V1\APICall $apicall_obj)
 {
     $settings = $securityscheme_obj->getSettings();
     $credentials = $apicall_obj->get_credentials();
     // Save the credentials
     \V1\Keyring::set_credentials($credentials);
     /**
      * By default we'll return the response from the authentication request so that it's meaningful.
      * However, in doing so, we'll need to block the main request, so developers may set this flag
      * to ignore the authentication, signifying that they've already got the information they needed
      * from it.
      * 
      * NOTE: This security method is meant as a basic way to catch security methods we otherwise
      * haven't implemented in our system. Take it for what it's worth. 
      * 
      * @TODO When using this security method, skip processing the APICall object for a speedup.
      */
     if (!empty($credentials['CUSTOM_IGNORE_AUTH'])) {
         return true;
     }
     // Remove unused credentials so as not to replace bad variables in the template.
     foreach ($credentials as $variable => $entry) {
         if (strpos($variable, 'CUSTOM_') !== 0) {
             unset($credentials[$variable]);
         }
     }
     // We need the method or we'll fail the call.
     if (empty($settings['method'])) {
         $this->error = true;
         return $this;
     }
     // Normalize the data into arrays.
     $described_by = $securityscheme_obj->getDescribedBy();
     $headers = $this->get_param_array($described_by->getHeaders());
     $query_params = $this->get_param_array($described_by->getQueryParameters());
     $bodies = $described_by->getBodies();
     $method = \Str::upper($settings['method']);
     $url = $settings['url'];
     // Grab the body if we have one, and the method supports one.
     $body = null;
     $body_type = null;
     if (count($bodies) > 0 && !in_array($method, array('GET', 'HEAD'))) {
         reset($bodies);
         $body_type = key($bodies);
         $body = $bodies[$body_type]->getExamples()[0];
     }
     /**
      * NOTE: These replacements may ruin the formatting or allow for people to inject data into them.
      * API Providers should be aware of that possibility.
      * 
      * @TODO In the future, we can consider implementing checking to verify that people aren't sending
      * crap data through the system.
      */
     $headers = $this->remove_cr_and_lf($this->replace_variables($headers, $credentials));
     $query_params = $this->remove_cr_and_lf($this->replace_variables($query_params, $credentials));
     $body = $this->replace_variables($body, $credentials);
     if (!empty($query_params)) {
         $query_string = http_build_query($query_params, null, '&');
         if (strpos($url, '?') === false) {
             $url .= '?' . $query_string;
         } else {
             $url .= '&' . $query_string;
         }
     }
     /**
      * RUNCLE RICK'S RAD RUN CALLS (The second coming!)
      */
     $curl = \Remote::forge($url, 'curl', $method);
     // Set the headers
     $headers = \V1\RunCall::get_headers($headers);
     foreach ($headers as $header_name => $header_value) {
         $curl->set_header($header_name, $header_value);
     }
     // Return the headers
     $curl->set_option(CURLOPT_HEADER, true);
     // If we need a body, set that.
     if (!empty($body) && !in_array($method, array('GET', 'HEAD'))) {
         $curl->set_header('Content-Type', $body_type);
         $curl->set_params($body);
     }
     // Run the request
     try {
         $response = $curl->execute()->response();
     } catch (\RequestStatusException $e) {
         $response = \Remote::get_response($curl);
     } catch (\RequestException $e) {
         $this->error = true;
         return $this;
     }
     // Set the usage stats, and format the response
     return \V1\Socket::prepare_response(array('status' => $response->status, 'headers' => $response->headers, 'body' => $response->body));
 }
 /**
  * Try to get an \APICall object
  *
  * @param string $call The static call name, or null if we're making a dynamic call.
  * @return mixed The \APICall object on success, or false if an error occurred.
  */
 protected static function apicall_object($call = null)
 {
     // For some reason we can't parse the RAML, so we throw a 500 error.
     if (($api_def = \V1\RAML::parse()) === false) {
         return \Utility::format_error(500);
     } elseif (is_array($api_def)) {
         // Specific error
         return $api_def;
     }
     $all_calls = (array) $api_def->getResourcesAsUri();
     $all_calls = reset($all_calls);
     $api_call = null;
     // Loop through every possible URI on the API
     foreach ($all_calls as $uri => $call_data) {
         // GET /res/name
         $uri_explode = explode(' ', $uri);
         // Is it the static call we need?
         if (($call_uri = str_replace('/{{static-calls}}' . $call, '', $uri_explode[1])) !== $uri_explode[1]) {
             /*
              * Static calls only have one method, so since it matches the resource, we'll pass along
              * the method it uses.
              */
             $api_call = \V1\APICall::forge($api_def, $uri_explode[0], $call_uri, $uri_explode[1]);
             break;
         }
     }
     if (is_object($api_call)) {
         if (!empty($api_call->get_errors())) {
             // Errors from \APICall
             return $api_call->get_errors();
         } else {
             // Return the \APICall object
             return $api_call;
         }
     }
     return false;
 }
 /**
  * Run the request
  * 
  * @param \Raml\SecurityScheme $securityscheme_obj The security scheme to process the call data for
  * @param \V1\APICall $apicall_obj					The APICall object
  * 
  * @return mixed The object we just completed or an array describing the next step in the security process
  */
 public function run(\Raml\SecurityScheme $securityscheme_obj, \V1\APICall $apicall_obj)
 {
     $settings = $securityscheme_obj->getSettings()->asArray();
     $credentials = $apicall_obj->get_credentials();
     $settings['authorization'] = empty($settings['authorization']) ? 'header' : \Str::lower($settings['authorization']);
     // Verify that we have the required credentials for the request.
     if (empty($credentials['OAUTH_CONSUMER_KEY']) || empty($credentials['OAUTH_CONSUMER_SECRET']) || empty($credentials['OAUTH_USER_ID'])) {
         $this->error = true;
         return $this;
     }
     // Store the proper credentials in the DB.
     $this->store_credentials($credentials);
     // Pull data from the cache for the current request, allowing for multiple authentications for the customer.
     $this->cache_id = hash('sha256', $credentials['OAUTH_CONSUMER_KEY'] . $credentials['OAUTH_CONSUMER_SECRET'] . $credentials['OAUTH_USER_ID']);
     $credentials = array_replace($this->get_cache(), $credentials);
     // Where should we set the authorization data?
     switch ($settings['authorizeLocation']) {
         case 'header':
             $authorize_location = OAUTH_AUTH_TYPE_AUTHORIZATION;
             break;
         case 'query':
             $authorize_location = OAUTH_AUTH_TYPE_URI;
             break;
         case 'body':
             $authorize_location = OAUTH_AUTH_TYPE_FORM;
             break;
         case 'none':
             $authorize_location = OAUTH_AUTH_TYPE_NONE;
             break;
     }
     try {
         // Create the PECL installed OAuth object.
         $oauth = new \OAuth($credentials['OAUTH_CONSUMER_KEY'], $credentials['OAUTH_CONSUMER_SECRET'], $settings['signatureMethod'], $authorize_location);
         if (\Fuel::$env !== 'production') {
             $oauth->enableDebug();
         }
         if (empty($credentials['OAUTH_ACCESS_TOKEN']) || empty($credentials['OAUTH_ACCESS_TOKEN_SECRET'])) {
             // Get our access token and secret.
             if (($credentials = $this->get_access_tokens($oauth, $settings, $credentials)) === false) {
                 $this->error = true;
                 return $this;
             }
             // Authentication of my second leg (Yup. It's hairy, so it must be mine.)
             if (!empty($credentials['errors'])) {
                 return $credentials;
             }
         }
         $oauth->setToken($credentials['OAUTH_ACCESS_TOKEN'], $credentials['OAUTH_ACCESS_TOKEN_SECRET']);
         // Collect parameters to build our signature
         $params = null;
         if ($apicall_obj->get_body_type() === 'application/x-www-form-urlencoded') {
             // If we need to handle string bodies later, we will.
             if (is_array($apicall_obj->get_method_params())) {
                 $params = http_build_query($apicall_obj->get_method_params(), null, '&', PHP_QUERY_RFC3986) . '&';
             }
         }
         $params .= http_build_query($apicall_obj->get_query_params(), null, '&') . '&' . ($params .= http_build_query($apicall_obj->get_headers(), null, '&'));
         $header = $oauth->getRequestHeader($apicall_obj->get_method(), $apicall_obj->get_url(), $params);
         $apicall_obj->set_header('Authorization', $header);
         return true;
     } catch (\OAuthException $e) {
         // Something went wrong, so destroy the cache so it can get fixed.
         $this->delete_cache();
         // Let the script automatically continue searching for security methods.
         $this->error = true;
         return $this;
     }
 }
 /**
  * Queue a call
  * 
  * @param string $api		The name of the API we're calling
  * @param array $call_data	The array of call data for the call
  * @param \V1\APICall $apicall_obj	The APICall object for the current call
  * 
  * @return bool True on success, or false on fail
  */
 public function queue_call($api, array $call_data, \V1\APICall $apicall_obj)
 {
     // We need a valid URL
     if (($parsed_url = parse_url($call_data['url'])) === false || empty($parsed_url['scheme']) || empty($parsed_url['host'])) {
         $this->streams[$api][] = \Utility::format_error(500);
         return false;
     }
     /**
      * Configure
      */
     $parsed_url['path'] = empty($parsed_url['path']) ? '/' : $parsed_url['path'];
     $parsed_url['query'] = empty($parsed_url['query']) ? null : '?' . $parsed_url['query'];
     $call_data['method'] = empty($call_data['method']) ? 'GET' : \Str::upper($call_data['method']);
     $stream_scheme = null;
     if ($parsed_url['scheme'] === 'https') {
         $parsed_url['port'] = empty($parsed_url['port']) ? '443' : $parsed_url['port'];
         $stream_scheme = 'ssl://';
     } else {
         $parsed_url['port'] = empty($parsed_url['port']) ? '80' : $parsed_url['port'];
     }
     $opts['http'] = array('method' => $call_data['method'], 'ignore_errors' => true, 'protocol_version' => 1.1);
     /**
      * Request
      */
     $request = $call_data['method'] . " " . $parsed_url['path'] . $parsed_url['query'] . " HTTP/1.1\r\n";
     $request .= "Host: " . $parsed_url['host'] . ":" . $parsed_url['port'] . "\r\n";
     $request .= "Accept-Encoding: gzip, deflate\r\n";
     // User headers
     if (!empty($call_data['headers']) && is_array($call_data['headers'])) {
         foreach ($call_data['headers'] as $header_name => $header_val) {
             $request .= $header_name . ": " . $header_val . "\r\n";
         }
     }
     /**
      * Body
      */
     if (!empty($call_data['body-type']) && !empty($call_data['body']) && !in_array($call_data['method'], array('GET', 'HEAD'))) {
         $build_body = static::build_body($call_data['method'], $call_data['body-type'], $call_data['body']);
         if (is_array($build_body)) {
             // Content-Type and Content-Length headers and our body
             $request .= $build_body['header'] . "\r\n";
             $request .= "Content-Length: " . strlen($build_body['body']) . "\r\n\r\n";
             $request .= $build_body['body'] . "\r\n";
         } else {
             // We need to post, but we can't.
             if (\V1\APIRequest::is_static() === true) {
                 $this->streams[$api][] = \Utility::format_error(400, \V1\Err::BAD_FORMAT, \Lang::get('v1::errors.bad_format_static'));
             }
             $this->streams[$api][] = \Utility::format_error(400, \V1\Err::BAD_FORMAT, \Lang::get('v1::errors.bad_format'));
             return false;
         }
     } else {
         // Finish our request
         $request .= "\r\n";
     }
     /**
      * CALLBACKS
      */
     if (!empty($security_calls = $apicall_obj->get_security_calls())) {
         foreach ($security_calls as $security_call) {
             // Process the request data as needed.
             $request = $security_call->before_send($request);
         }
     }
     $context = stream_context_create($opts);
     try {
         $stream = stream_socket_client($stream_scheme . $parsed_url['host'] . ":" . $parsed_url['port'], $errno, $errstr, \Config::get('v1::socket.timeout', 5), STREAM_CLIENT_ASYNC_CONNECT | STREAM_CLIENT_CONNECT, $context);
     } catch (\Exception $e) {
         // Server unavailable
         $this->streams[$api][] = \Utility::format_error(503, \Err::SERVER_ERROR, \Lang::get('v1::errors.remote_unavailable'));
         return false;
     }
     $this->api_request[$api][] = \V1\APIRequest::get();
     $this->is_static[$api][] = \V1\APIRequest::is_static();
     $this->api_call[$api][] = $apicall_obj;
     if ($stream !== false) {
         fwrite($stream, $request);
         $this->streams[$api][] =& $stream;
         return true;
     } else {
         // Server unavailable
         $this->streams[$api][] = \Utility::format_error(503, \Err::SERVER_ERROR, \Lang::get('v1::errors.remote_unavailable'));
         return false;
     }
 }