1<?php 2/* 3 * vim:set softtabstop=4 shiftwidth=4 expandtab: 4 * 5 * LICENSE: GNU Affero General Public License, version 3 (AGPL-3.0-or-later) 6 * Copyright 2001 - 2020 Ampache.org 7 * 8 * This program is free software: you can redistribute it and/or modify 9 * it under the terms of the GNU Affero General Public License as published by 10 * the Free Software Foundation, either version 3 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU Affero General Public License for more details. 17 * 18 * You should have received a copy of the GNU Affero General Public License 19 * along with this program. If not, see <https://www.gnu.org/licenses/>. 20 * 21 */ 22 23declare(strict_types=0); 24 25namespace Ampache\Module\Util\Captcha; 26 27use Ampache\Module\System\Core; 28 29/** 30 * Class easy_captcha_utility 31 * 32 * "AJAX" and utility code 33 */ 34class easy_captcha_utility 35{ 36 37 38 #-- determine usable temp directory 39 /** 40 * @return mixed 41 */ 42 public function tmp() 43 { 44 return current(array_filter(// filter by writability 45 array_filter(// filter empty entries 46 array( 47 $_SERVER['TMPDIR'], 48 $_SERVER['REDIRECT_TMPDIR'], 49 $_SERVER['TEMP'], 50 ini_get('upload_tmp_dir'), 51 $_SERVER['TMP'], 52 $_SERVER['TEMPDIR'], 53 function_exists("sys_get_temp_dir") ? sys_get_temp_dir() : "", 54 '/tmp' 55 )), "is_writable")); 56 } 57 58 59 #-- script was called directly 60 61 /** 62 * @return boolean 63 */ 64 public static function API() 65 { 66 67 #-- load data 68 if ($id = Core::get_get(CAPTCHA_PARAM_ID)) { 69 #-- special case 70 if ($id == 'base.js') { 71 easy_captcha_utility::js_base(); 72 } else { 73 $c = new easy_captcha($id = null, $ignore_expiration = 1); 74 $expired = !$c->is_valid(); 75 76 #-- JS-RPC request, check entered solution on the fly 77 if ($test = $_REQUEST[CAPTCHA_PARAM_INPUT]) { 78 #-- check 79 if ($expired || empty($c->image)) { 80 die(easy_captcha_utility::js_header('alert("captcha error: request invalid (wrong storage id) / or expired");')); 81 } 82 if (0 >= $c->ajax_tries--) { 83 $c->log("::API", "JS-RPC", "ajax_tries exhausted ($c->ajax_tries)"); 84 } 85 $okay = $c->image->solved($test) || $c->text->solved($test); 86 87 #-- sendresult 88 easy_captcha_utility::js_rpc($okay); 89 } else { 90 #-- generate and send image file 91 if ($expired) { 92 $type = "image/png"; 93 $bin = easy_captcha_utility::expired_png(); 94 } else { 95 $type = "image/jpeg"; 96 $bin = $c->image->jpeg(); 97 } 98 header("Pragma: no-cache"); 99 header("Cache-Control: no-cache, no-store, must-revalidate, private"); 100 header("Expires: " . gmdate("r", time())); 101 header("Content-Length: " . strlen($bin)); 102 header("Content-Type: $type"); 103 print $bin; 104 } 105 } 106 107 return false; 108 } 109 110 return false; 111 } 112 113 #-- hardwired error img 114 115 /** 116 * @return false|string 117 */ 118 public function expired_png() 119 { 120 return base64_decode("iVBORw0KGgoAAAANSUhEUgAAADwAAAAUAgMAAACsbba6AAAADFBMVEUeEhFcMjGgWFf9jIrTTikpAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA3UlEQVQY01XPzwoBcRAH8F9RjpSTm9xR9qQwtnX/latX0DrsA3gC8QDK0QO4bv7UOtmM+x4oZ4X5FQc1hlb41dR8mm/9ZhT/P7X/dDcpZPU3FYft9kWbLuWp4Bgt9v1oGG07Ja8ojfjxQFym02DVmoixkV/m2JI/TUtefR7nD9rkrhkC+6D77/8mUhDvw0ymLPwxf8esghEFRq8hqKcu2iG16Vlun1zYTO7RwCeFyoJqAgC3LQwzYiCokDj0MWRxb+Z6R8mPJb8Q77zlPbuCoJE8a/t7P773uv36tdcTmsXfRycoRJ8AAAAASUVORK5CYII="); 121 } 122 123 #-- send base javascript 124 public function js_base() 125 { 126 $captcha_new_urls = $_GET["captcha_new_urls"] ? 0 : 1; 127 $BASE_URL = CAPTCHA_BASE_URL; 128 $PARAM_ID = CAPTCHA_PARAM_ID; 129 $PARAM_INPUT = CAPTCHA_PARAM_INPUT; 130 $COLOR_CALC = CAPTCHA_INVERSE ? "32 +" : "224 -"; 131 easy_captcha_utility::js_header(); 132 print<<<END_____BASE__BASE__BASE__BASE__BASE__BASE__BASE__BASE_____END 133 134 135/* easy_captcha utility code */ 136 137// global vars 138captcha_url_rx = /(https?:\/\/\w[^\/,\]\[=#]+)/ig; 139captcha_form_urls = new Array(); 140captcha_sol_cb = ""; 141captcha_rpc = 0; 142 143// set up watchers 144if ($captcha_new_urls) { 145 window.setTimeout("captcha_form_urls = captcha_find_urls_in_form()", 500); 146 window.setInterval("captcha_spamfree_no_new_urls()", 3000); 147} 148 149// scans for URLs in any of the form fields 150function captcha_find_urls_in_form() { 151 var nf, ne, nv; 152 for (nf=0; nf<document.forms.length; nf++) { 153 for (ne=0; ne<document.forms[nf].elements.length; ne++) { 154 nv += "\\n" + document.forms[nf].elements[ne].value; 155 } 156 } 157 var r = nv.match(captcha_url_rx); 158 if (!r) { r = new Array(); } 159 return r; 160} 161// diff URL lists and hide captcha if nothing new was entered 162function captcha_spamfree_no_new_urls() { 163 var has_new_urls = captcha_find_urls_in_form().join(",") != captcha_form_urls.join(","); 164 var s = document.getElementById("captcha").style; 165 if (s.opacity) { 166 s.opacity = has_new_urls ? "0.9" : "0.1"; 167 } 168 else { 169 s.display = has_new_urls ? "block" : "none"; 170 } 171} 172 173// if a certain solution length is reached, check it remotely (JS-RPC) 174function captcha_check_solution() { 175 var cid = document.getElementById("{$PARAM_ID}"); 176 var inf = document.getElementById("{$PARAM_INPUT}"); 177 var len = inf.value.length; 178 // visualize processissing 179 if (len >= 4) { 180 inf.style.border = "2px solid #FF9955"; 181 } 182 // if enough letters entered 183 if (len >= 4) { 184 // remove old <script> node 185 var scr; 186 if (src = document.getElementById("captcha_ajax_1")) { 187 src.parentNode.removeChild(src); 188 } 189 // create new <script> node, initiate JS-RPC call thereby 190 scr = document.createElement("script"); 191 var url = "$BASE_URL" + "?$PARAM_ID=" + cid.value + "&$PARAM_INPUT=" + inf.value; 192 scr.setAttribute("src", url); 193 scr.setAttribute("id", "captcha_ajax_1"); 194 document.getElementById("captcha").appendChild(scr); 195 captcha_rpc = 1; 196 } 197 // visual feedback for editing 198 var col = $COLOR_CALC len * 5; 199 col = col.toString(16); 200 inf.style.background = "#"+col+col+col; 201} 202 203 204END_____BASE__BASE__BASE__BASE__BASE__BASE__BASE__BASE_____END; 205 } 206 207 #-- javascript header (also prevent caching) 208 209 /** 210 * @param string $print 211 */ 212 public function js_header($print = '') 213 { 214 header("Pragma: no-cache"); 215 header("Cache-Control: no-cache, no-store, must-revalidate, private"); 216 header("Expires: " . gmdate("r", time())); 217 header("Content-Type: text/javascript"); 218 if ($print) { 219 print $print; 220 } 221 } 222 223 224 #-- response javascript 225 226 /** 227 * @param $yes 228 */ 229 public function js_rpc($yes) 230 { 231 $yes = $yes ? 1 : 0; 232 $PARAM_INPUT = CAPTCHA_PARAM_INPUT; 233 easy_captcha_utility::js_header(); 234 print<<<END_____JSRPC__JSRPC__JSRPC__JSRPC__JSRPC__JSRPC_____END 235 236 237// JS-RPC response 238if (1) { 239 captcha_rpc = 0; 240 var inf = document.getElementById("{$PARAM_INPUT}"); 241 inf.style.borderColor = $yes ? "#22AA22" : "#AA2222"; 242} 243 244 245END_____JSRPC__JSRPC__JSRPC__JSRPC__JSRPC__JSRPC_____END; 246 } 247 248 /* static */ 249 /** 250 * @param $url 251 * @return string 252 */ 253 public function canonical_path($url) 254 { 255 $path = parse_url($url); 256 257 if (is_array($path) && !empty($path['path'])) { 258 $url = $path['path']; 259 } 260 261 $path = array(); 262 $abspath = substr("$url ", 0, 1) == '/' ? '/' : ''; 263 $ncomp = 0; 264 265 foreach (explode('/', $url) as $comp) { 266 switch ($comp) { 267 case '': 268 case '.': 269 break; 270 case '..': 271 if ($ncomp--) { 272 array_pop($path); 273 break; 274 } 275 // Intentional break fall-through 276 default: 277 $path[] = $comp; 278 $ncomp++; 279 break; 280 } 281 } 282 283 $path = $abspath . implode('/', $path); 284 285 return empty($path) ? '.' : $path; 286 } //patch contributed from Fedora downstream by Patrick Monnerat 287} 288