/** * Test validate url. */ public function test_validate_url() { $invalid = array(true, false, null, array(), new \stdClass(), 1, 1.2, '1', 'test', '*****@*****.**', 'example.com'); foreach ($invalid as $val) { $this->assertEquals(false, \pdyn\datatype\Url::validate($val)); } $protocols = array('http://', 'https://', 'ftp://'); $prefixes = array('', 'james@', 'james:1234@'); $domains = array('example.com', 'one.example.com', 'one.two.example.com'); $suffixes = array('', ':80', ':8080'); $files = array('', '/', '/one', '/one/', '/one/two', '/one/two/', '/test.html', '/one/test.html', '/one/two/test.html'); foreach ($protocols as $a) { foreach ($prefixes as $b) { foreach ($domains as $c) { foreach ($suffixes as $d) { foreach ($files as $e) { $this->assertEquals(true, \pdyn\datatype\Url::validate($a . $b . $c . $d . $e)); } } } } } }
/** * This sends a request to the hub. * * @param string $mode The request mode. 'subscribe' or 'unsubscribe' * @param string $url The URL to send the request to - the hub URL. * @param string $topic The topic you are referring to. * @param string $callback The local callback URL. * @param \pdyn\httpclient\HttpClientInterface $httpclient An HttpClientInterface object that will handle the transfer. * @return bool|array True if successful, array of 'status_code', and 'msg', specifying received http code and body text. */ protected function send_request($mode, $url, $topic, $callback, \pdyn\httpclient\HttpClientInterface $httpclient) { if (!\pdyn\datatype\Url::validate($callback)) { throw new Exception('Bad $callback received', Exception::ERR_BAD_REQUEST); } if (!\pdyn\datatype\Url::validate($url)) { throw new Exception('Bad $url received', Exception::ERR_BAD_REQUEST); } $request = $this->storage->get_request($mode, $topic, $url, $callback); $verifytoken = \pdyn\base\Utils::genRandomStr(64); if (empty($request)) { $request = $this->storage->create_request($mode, $topic, $url, $callback, $verifytoken); } $postdata = ['hub.mode' => $mode, 'hub.callback' => $callback, 'hub.topic' => $topic, 'hub.verify' => 'async', 'hub.verify_token' => $verifytoken, 'hub.lease_seconds' => 604800]; $response = $httpclient->post($url, $postdata); if ($response->status_code() === 204 || $response->status_code() === 202) { return true; } else { $this->storage->delete_request($request->id); return ['status_code' => $response->status_code(), 'msg' => $response->body()]; } }
/** * Clean an HTML string. * * Performs the following functions: * - Removes any unsafe tags. * - For allowed tags that specify URL attributes, transforms relative URLs to absolute URLs. * * @param array|bool $allowed_tags An array of allow tags, or true for the internal list of safe tags. * @return string The cleaned HTML string. */ public function clean($allowed_tags = true) { static $abs_urls = []; preg_match_all('#' . static::EXTRACT_HTMLTAG . '#iums', $this->val, $input_html); if (empty($input_html[0])) { $this->val = static::escape_html($this->val); return; } $input_text = preg_split('#' . static::EXTRACT_HTMLTAG . '#iums', $this->val); if ($allowed_tags === true || !is_array($allowed_tags)) { $allowed_tags = static::get_allowed_tags(); } $void_elements = static::get_void_elements(); $this->val = ''; $tag_count = []; // Everything in this loop gets called a lot, reduce wherever posible. foreach ($input_html[1] as $i => $tag) { $closingtag = false; if ($tag[0] === '/') { $closingtag = true; $tag = mb_substr($tag, 1); } $tag = !isset($allowed_tags[$tag]) ? mb_strtolower($tag) : $tag; if (isset($allowed_tags[$tag])) { // Clean up allowed tags. if ($closingtag === true) { // Add a closing tag, or remove needless closing tags for void elements. $html = isset($void_elements[$tag]) ? '' : '</' . $tag . '>'; } else { if (isset($allowed_tags[$tag]['opts']['max']) && isset($tag_count[$tag]) && $tag_count[$tag] >= $allowed_tags[$tag]['opts']['max']) { unset($allowed_tags[$tag]); unset($input_html[1][$i]); continue; } $html = '<' . $tag; foreach ($allowed_tags[$tag] as $attr => $type) { if ($attr === 'opts') { // Opts are options in how to treat the tag (max count, etc), do not treat as an attribute continue; } if (mb_strpos($input_html[0][$i], $attr) !== false) { preg_match('#' . $attr . '\\s*=\\s*("((?U).*)?"|\'((?U).*)?\'|([^>\\s]+))\\s*#iums', $input_html[0][$i], $m_attr); if (!empty($m_attr)) { $attr_val = end($m_attr); $html .= ' ' . $attr . '="'; if ($type === 'uri') { // Fix spaces in URLs. $attr_val = str_replace(' ', '+', $attr_val); if (!isset($abs_urls[$attr_val])) { $abs_urls[$attr_val] = \pdyn\datatype\Url::validate($attr_val) ? $attr_val : \pdyn\datatype\Url::make_absolute($attr_val, $this->sourceurl, true); } $html .= $abs_urls[$attr_val]; } else { $html .= htmlspecialchars($attr_val, ENT_QUOTES, 'UTF-8', false); } $html .= '"'; } } } if (isset($void_elements[$tag])) { $html .= '/'; } $html .= '>'; if (isset($tag_count[$tag])) { $tag_count[$tag]++; } else { $tag_count[$tag] = 1; } } } else { /* here we're removing a disallowed tag from the $input_html array so we can use the array for close_unclosed_tags this allows tells close_unclosed_tags which tags remain in the html and lets it do it's job without having to do another tag discovery regex on the text so basically, this is just a timesaving feature. */ unset($input_html[1][$i]); $html = ''; } $this->val .= htmlspecialchars($input_text[$i], ENT_COMPAT, 'UTF-8', false) . $html; } //if (isset($input_text[$i+1])) $this->val.=htmlentities($input_text[$i+1],ENT_QUOTES,'UTF-8',false); if (isset($input_text[$i + 1])) { $this->val .= $input_text[$i + 1]; } $this->close_tags($input_html); $this->val = trim($this->val); }
/** * Create a resource image from a URL. * * @param string $url The URL to create the resource from. * @param \pdyn\cache\CacheInterface $cache A caching object. * @param \pdyn\httpclient\HttpClientInterface $httpclient An HttpClientInterface instance. * @param int $ttl How long the cached information will stay valid, in seconds. * @return HttpResource An HttpResource object, or one of it's decendents. */ public static function instance($url, \pdyn\cache\CacheInterface $cache, \pdyn\httpclient\HttpClientInterface $httpclient, $ttl = 7200) { // Normalize/validate URL. $url = \pdyn\datatype\Url::normalize($url); if (!\pdyn\datatype\Url::validate($url)) { throw new Exception('Invalid URL: ' . $url, Exception::ERR_BAD_REQUEST); } $cache_key = sha1($url); $link_info = $cache->get('link_basic', $cache_key); if (!empty($link_info) && is_array($link_info)) { $link_info = $link_info['data']; } else { $response = $httpclient->get($url); $link_info = ['mime_type' => mb_strtolower($response->mime_type()), 'body' => base64_encode(gzdeflate($response->body(), 9)), 'url' => $url]; $link_info['handler'] = static::detect_resource_type($url, $link_info['mime_type'], $response->body()); $cache->store('link_basic', $cache_key, $link_info, time() + $ttl); } unset($link_info['body']); $resourcetypes = static::getresourcetypes(); $classname = $link_info['handler']; if (in_array($classname, $resourcetypes)) { $resource = new $classname($url, $cache, $httpclient, $ttl, $link_info); return $resource; } else { throw new Exception('rmtRes: did not know how to handle ' . $link_info['handler'] . ' resource type', Exception::ERR_BAD_REQUEST); } }
/** * Test validate id. * * @dataProvider dataprovider_validate * @param string $test Test data. * @param string $expected Expected output. */ public function test_validate($test, $expected) { $this->assertEquals($expected, \pdyn\datatype\Url::validate($test)); }
/** * Assign values to class properties. * * @param array $info An array of data to assign. * @return bool Success/Failure. */ protected function populate(array $info = array()) { if (empty($info)) { $this->post_populate(); return false; } // Load datatype information into static var cache. $obj_datatypes = static::get_obj_datatypes($this->DB); $info = array_intersect_key($info, $obj_datatypes); foreach ($info as $key => $val) { switch ($obj_datatypes[$key]) { case 'email': if (\pdyn\datatype\Validator::email($val)) { $this->{$key} = (string) $val; } break; case 'timestamp': if ((int) $val > 0) { $this->{$key} = (int) $val; } break; case 'str': case 'text': case 'longtext': $this->{$key} = (string) $val; break; case 'filename': $val = trim($val); if (!empty($val)) { $this->{$key} = \pdyn\filesystem\FilesystemUtils::sanitize_filename($val); } break; case 'int': case 'bigint': $this->{$key} = (int) $val; break; case 'float': $this->{$key} = (double) $val; break; case 'id': $this->{$key} = (int) $val; break; case 'bool': $this->{$key} = (bool) $val; break; case 'user_id': if (\pdyn\datatype\Id::validate($val) === true) { $this->{$key} = (int) $val; } break; case 'url': if (\pdyn\datatype\Url::validate($val) === true) { $this->{$key} = (string) $val; } break; case 'mime': if (\pdyn\datatype\Validator::mime($val) === true) { $this->{$key} = (string) $val; } break; default: if (is_array($obj_datatypes[$key])) { if (in_array($val, $obj_datatypes[$key], true)) { $this->{$key} = $val; } } else { $this->{$key} = $val; } } } $this->post_populate(); return true; }
/** * Process and clean an incoming request. * * @param array $params Array of received parameters. * @return Request A populated Request object. */ protected function process_request($params) { if (empty($params['hub_callback']) || empty($params['hub_mode']) || empty($params['hub_topic']) || empty($params['hub_verify'])) { throw new Exception('Bad Subscription Request: One or more of hub_callback, hub_mode, hub_topic, or hub_verify was not present or empty', Exception::ERR_BAD_REQUEST); } $params['hub_mode'] = mb_strtolower($params['hub_mode']); $params['hub_verify'] = mb_strtolower($params['hub_verify']); if (!in_array($params['hub_verify'], ['sync', 'async'], true)) { throw new Exception('We currently only support sync and async verify methods', Exception::ERR_NOT_IMPLEMENTED); } if (\pdyn\datatype\Url::validate($params['hub_callback']) === false) { throw new Exception('Invalid Callback URL', Exception::ERR_BAD_REQUEST); } if (!is_string($params['hub_mode']) || !method_exists($this, 'handle_request_' . $params['hub_mode'])) { throw new Exception('hub_mode not accepted.', Exception::ERR_NOT_IMPLEMENTED); } if (!isset($params['hub_verify_token'])) { $params['hub_verify_token'] = ''; } // Override Lease Seconds // We're going to disrespect subscriber requested lease seconds to show them who's boss. // (also because constant revalidation of subscriptions is a good thing and we don't intend on keeping permanent or // long-term subscriptions active if they are requested) $params['hub_lease_seconds'] = 604800; $request = new Request(); $request->mode = $params['hub_mode']; $request->callback = $params['hub_callback']; $request->topic = $params['hub_topic']; $request->verifymode = $params['hub_verify']; $request->token = $params['hub_verify_token']; $request->timerequested = time(); $request->leaseseconds = $params['hub_lease_seconds']; return $request; }