function api_edit_url_update() { if (!isset($_REQUEST['shorturl'])) { return array('statusCode' => 400, 'status' => 'fail', 'simple' => "Need a 'shorturl' parameter", 'message' => 'error: missing param'); } if (!isset($_REQUEST['url'])) { return array('statusCode' => 400, 'status' => 'fail', 'simple' => "Need a 'url' parameter", 'message' => 'error: missing param'); } $shorturl = $_REQUEST['shorturl']; $url = $_REQUEST['url']; if (yourls_get_protocol($shorturl)) { $keyword = yourls_get_relative_url($shorturl); } else { $keyword = $shorturl; } if (!yourls_is_shorturl($keyword)) { return array('statusCode' => 404, 'status' => 'fail', 'simple ' => "Error: keyword {$keyword} not found", 'message' => 'error: not found'); } $title = ''; if (isset($_REQUEST['title'])) { $title = $_REQUEST['title']; } if (yourls_edit_link($url, $keyword, $keyword, $title)) { return array('statusCode' => 200, 'simple' => "Keyword {$keyword} updated to {$url}", 'message' => 'success: updated'); } else { return array('statusCode' => 500, 'status' => 'fail', 'simple' => 'Error: could not edit keyword, not sure why :-/', 'message' => 'error: unknown error'); } }
function clayton_api_action_delete() { // We don't want unauthenticated users deleting links // If YOURLS is in public mode, force authentication anyway if (!yourls_is_private()) { yourls_do_action('require_auth'); require_once YOURLS_INC . '/auth.php'; } // Need 'shorturl' parameter if (!isset($_REQUEST['shorturl'])) { return array('statusCode' => 400, 'simple' => "Need a 'shorturl' parameter", 'message' => 'error: missing param'); } $shorturl = $_REQUEST['shorturl']; // Check if valid shorturl if (!yourls_is_shorturl($shorturl)) { return array('statusCode' => 404, 'simple ' => 'Error: short URL not found', 'message' => 'error: not found'); } // Is $shorturl a URL (http://sho.rt/abc) or a keyword (abc) ? if (yourls_get_protocol($shorturl)) { $keyword = yourls_get_relative_url($shorturl); } else { $keyword = $shorturl; } // Delete shorturl if (yourls_delete_link_by_keyword($keyword)) { return array('statusCode' => 200, 'simple' => "Shorturl {$shorturl} deleted", 'message' => 'success: deleted'); } else { return array('statusCode' => 500, 'simple' => 'Error: could not delete shorturl, not sure why :-/', 'message' => 'error: unknown error'); } }
function ozh_yourls_antispam_check_add($false, $url) { // Sanitize URL and make sure there's a protocol $url = yourls_sanitize_url($url); // only check for 'http(s)' if (!in_array(yourls_get_protocol($url), array('http://', 'https://'))) { return false; } if (ozh_yourls_antispam_is_blacklisted($url) != false) { return array('status' => 'fail', 'code' => 'error:spam', 'message' => 'This domain is blacklisted', 'errorCode' => '403'); } // All clear, not interrupting the normal flow of events return false; }
/** * Checks and cleans a URL before printing it. Stolen from WP. * * A number of characters are removed from the URL. If the URL is for displaying * (the default behaviour) ampersands are also replaced. * * @since 1.6 * * @param string $url The URL to be cleaned. * @param string $context 'display' or something else. Use yourls_sanitize_url() for database or redirection usage. * @param array $protocols Optional. Array of allowed protocols, defaults to global $yourls_allowedprotocols * @return string The cleaned $url */ function yourls_esc_url($url, $context = 'display', $protocols = array()) { // make sure there's only one 'http://' at the beginning (prevents pasting a URL right after the default 'http://') $url = str_replace(array('http://*****:*****@)?([^/#?]+)(.*)$!', $url, $matches)) { list($all, $scheme, $slashes, $userinfo, $domain, $rest) = $matches; $scheme = strtolower($scheme); // Domain to lowercase. On URN eg "urn:example:animal:ferret:nose" don't lowercase anything else if ($slashes == '//') { $domain = strtolower($domain); } $url = $scheme . $slashes . $userinfo . $domain . $rest; } $url = preg_replace('|[^a-z0-9-~+_.?#=!&;,/:%@$\\|*\'()\\x80-\\xff]|i', '', $url); // Previous regexp in YOURLS was '|[^a-z0-9-~+_.?\[\]\^#=!&;,/:%@$\|*`\'<>"()\\x80-\\xff\{\}]|i' // TODO: check if that was it too destructive $strip = array('%0d', '%0a', '%0D', '%0A'); $url = yourls_deep_replace($strip, $url); $url = str_replace(';//', '://', $url); // Replace ampersands and single quotes only when displaying. if ('display' == $context) { $url = yourls_kses_normalize_entities($url); $url = str_replace('&', '&', $url); $url = str_replace("'", ''', $url); } if (!is_array($protocols) or !$protocols) { global $yourls_allowedprotocols; $protocols = yourls_apply_filter('esc_url_protocols', $yourls_allowedprotocols); // Note: $yourls_allowedprotocols is also globally filterable in functions-kses.php/yourls_kses_init() } if (!yourls_is_allowed_protocol($url, $protocols)) { return ''; } // I didn't use KSES function kses_bad_protocol() because it doesn't work the way I liked (returns //blah from illegal://blah) return yourls_apply_filter('esc_url', $url, $original_url, $context); }
/** * Return an "Add" row for the main table * * @return string HTML of the edit row */ function yourls_table_add_row($keyword, $url, $title = '', $ip, $clicks, $timestamp) { $keyword = yourls_sanitize_string($keyword); $id = yourls_string2htmlid($keyword); // used as HTML #id $shorturl = yourls_link($keyword); $statlink = yourls_statlink($keyword); $delete_link = yourls_nonce_url('delete-link_' . $id, yourls_add_query_arg(array('id' => $id, 'action' => 'delete', 'keyword' => $keyword), yourls_admin_url('admin-ajax.php'))); $edit_link = yourls_nonce_url('edit-link_' . $id, yourls_add_query_arg(array('id' => $id, 'action' => 'edit', 'keyword' => $keyword), yourls_admin_url('admin-ajax.php'))); // Action link buttons: the array $actions = array('stats' => array('href' => $statlink, 'id' => "statlink-{$id}", 'title' => yourls_esc_attr__('Stats'), 'anchor' => yourls__('Stats')), 'share' => array('href' => '', 'id' => "share-button-{$id}", 'title' => yourls_esc_attr__('Share'), 'anchor' => yourls__('Share'), 'onclick' => "toggle_share('{$id}');return false;"), 'edit' => array('href' => $edit_link, 'id' => "edit-button-{$id}", 'title' => yourls_esc_attr__('Edit'), 'anchor' => yourls__('Edit'), 'onclick' => "edit_link_display('{$id}');return false;"), 'delete' => array('href' => $delete_link, 'id' => "delete-button-{$id}", 'title' => yourls_esc_attr__('Delete'), 'anchor' => yourls__('Delete'), 'onclick' => "remove_link('{$id}');return false;")); $actions = yourls_apply_filter('table_add_row_action_array', $actions); // Action link buttons: the HTML $action_links = ''; foreach ($actions as $key => $action) { $onclick = isset($action['onclick']) ? 'onclick="' . $action['onclick'] . '"' : ''; $action_links .= sprintf('<a href="%s" id="%s" title="%s" class="%s" %s>%s</a>', $action['href'], $action['id'], $action['title'], 'button button_' . $key, $onclick, $action['anchor']); } $action_links = yourls_apply_filter('action_links', $action_links, $keyword, $url, $ip, $clicks, $timestamp); if (!$title) { $title = $url; } $protocol_warning = ''; if (!in_array(yourls_get_protocol($url), array('http://', 'https://'))) { $protocol_warning = yourls_apply_filter('add_row_protocol_warning', '<span class="warning" title="' . yourls__('Not a common link') . '">★</span>'); } // Row cells: the array $cells = array('keyword' => array('template' => '<a href="%shorturl%">%keyword_html%</a>', 'shorturl' => yourls_esc_url($shorturl), 'keyword_html' => yourls_esc_html($keyword)), 'url' => array('template' => '<a href="%long_url%" title="%title_attr%">%title_html%</a><br/><small>%warning%<a href="%long_url%">%long_url_html%</a></small>', 'long_url' => yourls_esc_url($url), 'title_attr' => yourls_esc_attr($title), 'title_html' => yourls_esc_html(yourls_trim_long_string($title)), 'long_url_html' => yourls_esc_html(yourls_trim_long_string($url)), 'warning' => $protocol_warning), 'timestamp' => array('template' => '%date%', 'date' => date('M d, Y H:i', $timestamp + YOURLS_HOURS_OFFSET * 3600)), 'ip' => array('template' => '%ip%', 'ip' => $ip), 'clicks' => array('template' => '%clicks%', 'clicks' => yourls_number_format_i18n($clicks, 0, '', '')), 'actions' => array('template' => '%actions% <input type="hidden" id="keyword_%id%" value="%keyword%"/>', 'actions' => $action_links, 'id' => $id, 'keyword' => $keyword)); $cells = yourls_apply_filter('table_add_row_cell_array', $cells, $keyword, $url, $title, $ip, $clicks, $timestamp); // Row cells: the HTML. Replace every %stuff% in 'template' with 'stuff' value. $row = "<tr id=\"id-{$id}\">"; foreach ($cells as $cell_id => $elements) { $callback = new yourls_table_add_row_callback($elements); $row .= sprintf('<td class="%s" id="%s">', $cell_id, $cell_id . '-' . $id); $row .= preg_replace_callback('/%([^%]+)?%/', array($callback, 'callback'), $elements['template']); // For the record, in PHP 5.3+ we don't need to introduce a class in order to pass additional parameters // to the callback function. Instead, we would have used the 'use' keyword : // $row .= preg_replace_callback( '/%([^%]+)?%/', function( $match ) use ( $elements ) { return $elements[ $match[1] ]; }, $elements['template'] ); $row .= '</td>'; } $row .= "</tr>"; $row = yourls_apply_filter('table_add_row', $row, $keyword, $url, $title, $ip, $clicks, $timestamp); return $row; }
/** * Explode a URL in an array of ( 'protocol' , 'slashes if any', 'rest of the URL' ) * * Some hosts trip up when a query string contains 'http://' - see http://git.io/j1FlJg * The idea is that instead of passing the whole URL to a bookmarklet, eg index.php?u=http://blah.com, * we pass it by pieces to fool the server, eg index.php?proto=http:&slashes=//&rest=blah.com * * Known limitation: this won't work if the rest of the URL itself contains 'http://', for example * if rest = blah.com/file.php?url=http://foo.com * * Sample returns: * * with 'mailto:jsmith@example.com?subject=hey' : * array( 'protocol' => 'mailto:', 'slashes' => '', 'rest' => 'jsmith@example.com?subject=hey' ) * * with 'http://example.com/blah.html' : * array( 'protocol' => 'http:', 'slashes' => '//', 'rest' => 'example.com/blah.html' ) * * @since 1.7 * @param string $url URL to be parsed * @param array $array Optional, array of key names to be used in returned array * @return mixed false if no protocol found, array of ('protocol' , 'slashes', 'rest') otherwise */ function yourls_get_protocol_slashes_and_rest($url, $array = array('protocol', 'slashes', 'rest')) { $proto = yourls_get_protocol($url); if (!$proto or count($array) != 3) { return false; } list($null, $rest) = explode($proto, $url, 2); list($proto, $slashes) = explode(':', $proto); return array($array[0] => $proto . ':', $array[1] => $slashes, $array[2] => $rest); }
/** * Lowercase scheme and domain of an URI - see issues 591, 1630, 1889 * * This function is trickier than what seems to be needed at first * * First, we need to handle several URI types: http://example.com, mailto:ozh@ozh.ozh, facetime:user@example.com, and so on, see * yourls_kses_allowed_protocols() in functions-kses.php * The general rule is that the scheme ("stuff://" or "stuff:") is case insensitive and should be lowercase. But then, depending on the * scheme, parts of what follows the scheme may or may not be case sensitive. * * Second, simply using parse_url() and its opposite http_build_url() (see functions-compat.php) is a pretty unsafe process: * - parse_url() can easily trip up on malformed or weird URLs * - exploding a URL with parse_url(), lowercasing some stuff, and glueing things back with http_build_url() does not handle well * "stuff:"-like URI [1] and can result in URLs ending modified [2][3]. We don't want to *validate* URI, we just want to lowercase * what is supposed to be lowercased. * * So, to be conservative, this functions: * - lowercases the scheme * - does not lowercase anything else on "stuff:" URI * - tries to lowercase only scheme and domain of "stuff://" URI * * [1] http_build_url(parse_url("mailto:ozh")) == "mailto:///ozh" * [2] http_build_url(parse_url("http://blah#omg")) == "http://blah/#omg" * [3] http_build_url(parse_url("http://blah?#")) == "http://blah/" * * @since 1.7.1 * @param string $url URL * @return string URL with lowercase scheme and protocol */ function yourls_lowercase_scheme_domain($url) { $scheme = yourls_get_protocol($url); if ('' == $scheme) { // Scheme not found, malformed URL? Something else? Not sure. return $url; } // Case 1 : scheme like "stuff://" (eg "http://example.com/" or "ssh://joe@joe.com") if (substr($scheme, -2, 2) == '//') { $parts = parse_url($url); // Most likely malformed stuff, could not parse : we'll just lowercase the scheme and leave the rest untouched if (false == $parts) { $url = str_replace($scheme, strtolower($scheme), $url); // URL seems parsable, let's do the best we can } else { $lower = array(); $lower['scheme'] = strtolower($parts['scheme']); if (isset($parts['host'])) { $lower['host'] = strtolower($parts['host']); } else { $parts['host'] = '***'; } // We're not going to glue back things that could be modified in the process unset($parts['path']); unset($parts['query']); unset($parts['fragment']); // original beginning of the URL and its lowercase-where-needed counterpart // We trim the / after the domain to avoid avoid "http://example.com" being reconstructed as "http://example.com/" $partial_original_url = trim(http_build_url($parts), '/'); $partial_lower_original_url = trim(http_build_url($parts, $lower), '/'); $url = str_replace($partial_original_url, $partial_lower_original_url, $url); } // Case 2 : scheme like "stuff:" (eg "mailto:joe@joe.com" or "bitcoin:15p1o8vnWqNkJBJGgwafNgR1GCCd6EGtQR?amount=1&label=Ozh") // In this case, we only lowercase the scheme, because depending on it, things after should or should not be lowercased } else { $url = str_replace($scheme, strtolower($scheme), $url); } return $url; }
/** * Checks and cleans a URL before printing it. Stolen from WP. * * A number of characters are removed from the URL. If the URL is for displaying * (the default behaviour) ampersands are also replaced. * * @since 1.6 * * @param string $url The URL to be cleaned. * @param string $context 'display' or something else. Use yourls_sanitize_url() for database or redirection usage. * @param array $protocols Optional. Array of allowed protocols, defaults to global $yourls_allowedprotocols * @return string The cleaned $url */ function yourls_esc_url($url, $context = 'display', $protocols = array()) { // make sure there's only one 'http://' at the beginning (prevents pasting a URL right after the default 'http://') $url = str_replace(array('http://*****:*****@$\\|*\'()\\x80-\\xff]|i', '', $url); // Previous regexp in YOURLS was '|[^a-z0-9-~+_.?\[\]\^#=!&;,/:%@$\|*`\'<>"()\\x80-\\xff\{\}]|i' // TODO: check if that was it too destructive $strip = array('%0d', '%0a', '%0D', '%0A'); $url = yourls_deep_replace($strip, $url); $url = str_replace(';//', '://', $url); // Replace ampersands and single quotes only when displaying. if ('display' == $context) { $url = yourls_kses_normalize_entities($url); $url = str_replace('&', '&', $url); $url = str_replace("'", ''', $url); } if (!is_array($protocols) or !$protocols) { global $yourls_allowedprotocols; $protocols = yourls_apply_filter('esc_url_protocols', $yourls_allowedprotocols); // Note: $yourls_allowedprotocols is also globally filterable in functions-kses.php/yourls_kses_init() } if (!yourls_is_allowed_protocol($url, $protocols)) { return ''; } // I didn't use KSES function kses_bad_protocol() because it doesn't work the way I liked (returns //blah from illegal://blah) $url = substr($url, 0, 1999); return yourls_apply_filter('esc_url', $url, $original_url, $context); }
/** * Check if a URL protocol is allowed * * Checks a URL against a list of whitelisted protocols. Protocols must be defined with * their complete scheme name, ie 'stuff:' or 'stuff://' (for instance, 'mailto:' is a valid * protocol, 'mailto://' isn't, and 'http:' with no double slashed isn't either * * @since 1.6 * * @param string $url URL to be check * @param array $protocols Optional. Array of protocols, defaults to global $yourls_allowedprotocols * @return boolean true if protocol allowed, false otherwise */ function yourls_is_allowed_protocol($url, $protocols = array()) { if (!$protocols) { global $yourls_allowedprotocols; $protocols = $yourls_allowedprotocols; } $protocol = yourls_get_protocol($url); return yourls_apply_filter('is_allowed_protocol', in_array($protocol, $protocols), $url, $protocols); }