backend.php 26 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007
  1. <?php
  2. define('CACHE_LIFETIME_MAX', 86400*7);
  3. define('PROXY_REQ_FAILED', 0);
  4. define('PROXY_REQ_PROGRESS', 1);
  5. define('PROXY_REQ_OK', 2);
  6. define('PROXY_CONCUR_TRIES', 8); // seconds to wait before attempting another request
  7. set_include_path(get_include_path() . PATH_SEPARATOR .
  8. dirname(__FILE__) ."/include");
  9. /* remove ill effects of magic quotes */
  10. ini_set('user_agent',
  11. 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:50.0) Gecko/20100101 Firefox/50.0');
  12. if (get_magic_quotes_gpc()) {
  13. function stripslashes_deep($value) {
  14. $value = is_array($value) ?
  15. array_map('stripslashes_deep', $value) : stripslashes($value);
  16. return $value;
  17. }
  18. $_POST = array_map('stripslashes_deep', $_POST);
  19. $_GET = array_map('stripslashes_deep', $_GET);
  20. $_COOKIE = array_map('stripslashes_deep', $_COOKIE);
  21. $_REQUEST = array_map('stripslashes_deep', $_REQUEST);
  22. }
  23. require_once "functions.php";
  24. require_once "sessions.php";
  25. require_once "db-prefs.php";
  26. require_once "sanity_check.php";
  27. require_once "version.php";
  28. require_once "config.php";
  29. require_once "prefs.php";
  30. require_once "users.php";
  31. $dbh = DB::get();
  32. $dt_add = get_script_dt_add();
  33. header('Content-Type: text/json; charset=utf-8');
  34. $op = $_REQUEST["op"];
  35. if (!$_SESSION["uid"] && $op != "fetch-profiles" && $op != "login") {
  36. print json_encode(array("error" => 6));
  37. return;
  38. } else if ($_SESSION["uid"]) {
  39. @$csrf_token = $_REQUEST["csrf_token"];
  40. $csrf_ignore = [ "login", "init", "urlmetadata", "imgproxy",
  41. "vidproxy", "emoticons", "emoticons_list", "logout", "embed" ];
  42. if ($csrf_token != $_SESSION["csrf_token"] && !in_array($op, $csrf_ignore)) {
  43. print json_encode(array("error" => 6));
  44. return;
  45. }
  46. update_heartbeat();
  47. }
  48. if (!sanity_check()) { return; }
  49. switch ($op) {
  50. case "create-user":
  51. $login = strtolower($_REQUEST["login"]);
  52. $rv = array();
  53. if ($_SESSION["access_level"] >= 10) {
  54. $sth = $dbh->prepare("SELECT id FROM ttirc_users WHERE
  55. login = ?");
  56. $sth->execute([$login]);
  57. if (!$sth->fetch()) {
  58. $tmp_password = make_password();
  59. $salt = substr(bin2hex(get_random_bytes(125)), 0, 250);
  60. $pwd_hash = encrypt_password($tmp_password, $salt, true);
  61. $rv[0] = T_sprintf("Created user %s with password <b>%s</b>.",
  62. $login, $tmp_password);
  63. $sth = $dbh->prepare("INSERT INTO ttirc_users
  64. (login, pwd_hash, email, nick, realname, salt)
  65. VALUES
  66. (?, ?, ?, ?, ?, ?)");
  67. $sth->execute([$login, $pwd_hash, "[email protected]", $login, $login, $salt]);
  68. } else {
  69. $rv[0] = T_sprintf("User %s already exists", $login);
  70. }
  71. $rv[1] = format_users();
  72. print json_encode($rv);
  73. }
  74. break;
  75. case "reset-password":
  76. $id = $_REQUEST["id"];
  77. if ($_SESSION["access_level"] >= 10) {
  78. $tmp_password = make_password();
  79. $login = get_user_login($id);
  80. $salt = substr(bin2hex(get_random_bytes(125)), 0, 250);
  81. $pwd_hash = encrypt_password($tmp_password, $salt, true);
  82. $sth = $dbh->prepare("UPDATE ttirc_users SET pwd_hash = ?, salt = ?
  83. WHERE id = ?");
  84. $sth->execute([$pwd_hash, $salt, $id]);
  85. print json_encode(array("message" =>
  86. T_sprintf("Reset password of user %s to <b>%s</b>.", $login,
  87. $tmp_password)));
  88. }
  89. break;
  90. case "delete-user":
  91. if ($_SESSION["access_level"] >= 10) {
  92. $ids = explode(",", $_REQUEST["ids"]);
  93. $ids_qmarks = arr_qmarks($ids);
  94. $sth = $dbh->prepare("DELETE FROM ttirc_users WHERE
  95. id in ($ids_qmarks) AND id != ?");
  96. $sth->execute(array_merge($ids, [$_SESSION["uid"]]));
  97. print format_users();
  98. }
  99. break;
  100. case "users":
  101. if ($_SESSION["access_level"] >= 10) {
  102. show_users();
  103. }
  104. break;
  105. case "part-channel":
  106. $last_id = (int) $_REQUEST["last_id"];
  107. $chan = $_REQUEST["chan"];
  108. $connection_id = $_REQUEST["connection"];
  109. if ($chan && valid_connection($connection_id)) {
  110. handle_command($connection_id, $chan, "/part");
  111. $sth = $dbh->prepare("DELETE FROM ttirc_channels WHERE LOWER(channel) = LOWER(?)
  112. AND connection_id = ?");
  113. $sth->execute([$chan, $connection_id]);
  114. }
  115. $lines = get_new_lines($last_id);
  116. $conn = get_conn_info();
  117. $chandata = get_chan_data();
  118. $params = get_misc_params();
  119. print json_encode(array($conn, $lines, $chandata, $params));
  120. break;
  121. case "query-user":
  122. $nick = trim($_REQUEST["nick"]);
  123. $last_id = (int) $_REQUEST["last_id"];
  124. $connection_id = $_REQUEST["connection"];
  125. if ($nick && valid_connection($connection_id)) {
  126. handle_command($connection_id, $chan, "/query $nick");
  127. }
  128. $lines = get_new_lines($last_id);
  129. $conn = get_conn_info();
  130. $chandata = get_chan_data();
  131. $params = get_misc_params();
  132. print json_encode(array($conn, $lines, $chandata, $params));
  133. break;
  134. case "send":
  135. $message = trim(shorten_urls($_REQUEST["message"]));
  136. $last_id = (int) $_REQUEST["last_id"];
  137. $chan = $_REQUEST["chan"];
  138. $connection_id = $_REQUEST["connection"];
  139. $tab_type = $_REQUEST["tab_type"];
  140. @$uniqid = $_REQUEST["uniqid"];
  141. if ($message !== "" && valid_connection($connection_id)) {
  142. if (mb_strpos($message, "/") === 0) {
  143. handle_command($connection_id, $chan, $message);
  144. } else {
  145. $popcon_matches = array();
  146. preg_match_all("/(:[^ :]+:)/", $message, $popcon_matches);
  147. $emoticons_map = get_emoticons_map();
  148. if ($emoticons_map && count($popcon_matches[0]) > 0) {
  149. foreach ($popcon_matches[0] as $emoticon) {
  150. if (isset($emoticons_map[$emoticon])) {
  151. $sth = $dbh->prepare("SELECT id, times_used FROM ttirc_emoticons_popcon
  152. WHERE emoticon = ? AND owner_uid = ?");
  153. $sth->execute([$emoticon, $_SESSION["uid"]]);
  154. if ($row = $sth->fetch()) {
  155. $ref_id = $row['id'];
  156. $times_used = $row['times_used'];
  157. $sth = $dbh->prepare("UPDATE ttirc_emoticons_popcon SET times_used = times_used + 1
  158. WHERE id = ?");
  159. $sth->execute([$ref_id]);
  160. } else {
  161. $sth = $dbh->prepare("INSERT INTO ttirc_emoticons_popcon (emoticon, times_used, owner_uid)
  162. VALUES (?, 1, ?)");
  163. $sth->execute([$emoticon, $_SESSION['uid']]);
  164. }
  165. }
  166. }
  167. }
  168. update_last_message();
  169. if (is_instance($connection_id)) {
  170. #$lines = array_map("trim", explode("\n", $message));
  171. // this is a hard cap for incoming message length
  172. $message = mb_substr($message, 0, 32768);
  173. relay_message($connection_id, $chan, $message);
  174. } else {
  175. $lines = array_map("trim", explode("\n", $message));
  176. if ($tab_type == "P") {
  177. foreach ($lines as $line)
  178. if (mb_strlen($line) > 0)
  179. push_message($connection_id, $chan, $line, false,
  180. MSGT_PRIVATE_PRIVMSG);
  181. } else {
  182. $l = 0;
  183. foreach ($lines as $line) {
  184. if (mb_strlen($line) > 0) {
  185. push_message($connection_id, $chan, $line);
  186. ++$l;
  187. if ($l > 4) break;
  188. }
  189. }
  190. }
  191. }
  192. /* $lines = explode("\n", wordwrap($message, 200, "\n"));
  193. foreach ($lines as $line) {
  194. push_message($connection_id, $chan, $line);
  195. } */
  196. }
  197. }
  198. $dup = [ "duplicate" => true ];
  199. print json_encode([$dup, [], $dup, $dup]);
  200. break;
  201. case "update":
  202. cleanup_session_cache();
  203. $last_id = (int) $_REQUEST["last_id"];
  204. $init = $_REQUEST["init"];
  205. @$bufsize = (int) $_REQUEST["bufsize"];
  206. @$uniqid = $_REQUEST["uniqid"];
  207. if (!$bufsize) $bufsize = DEFAULT_BUFFER_SIZE;
  208. $update_delay = strpos($_SERVER['HTTP_USER_AGENT'], 'Dalvik/') === FALSE ?
  209. UPDATE_DELAY_MAX : 40;
  210. if (!$init && !num_new_lines($last_id, $bufsize)) {
  211. $dbh->exec("LISTEN ttirc_messages");
  212. $res = $dbh->pgsqlGetNotify(PDO::FETCH_ASSOC, $update_delay * 1000);
  213. }
  214. $lines = get_new_lines($last_id, $bufsize);
  215. $conn = get_conn_info();
  216. $chandata = get_chan_data();
  217. $params = $init ? get_misc_params($uniqid) : ['duplicate' => true];
  218. if ($uniqid) {
  219. if (serialize($conn) == $_SESSION["cache"][$uniqid]["conn"]) {
  220. $conn = array("duplicate" => true);
  221. } else {
  222. $_SESSION["cache"][$uniqid]["conn"] = serialize($conn);
  223. }
  224. if (serialize($chandata) == $_SESSION["cache"][$uniqid]["chandata"]) {
  225. $chandata = array("duplicate" => true);
  226. } else {
  227. $_SESSION["cache"][$uniqid]["chandata"] = serialize($chandata);
  228. }
  229. if (serialize($params) == $_SESSION["cache"][$uniqid]["params"]) {
  230. $params = array("duplicate" => true);
  231. } else {
  232. $_SESSION["cache"][$uniqid]["params"] = serialize($params);
  233. }
  234. $_SESSION["cache"][$uniqid]["last"] = time();
  235. }
  236. print json_encode(array($conn, $lines, $chandata, $params));
  237. break;
  238. case "history":
  239. $chan = $_REQUEST["chan"];
  240. $connection_id = $_REQUEST["connection"];
  241. $lines = get_history_lines($connection_id, $chan);
  242. $dup = [ "duplicate" => true ];
  243. print json_encode([$dup, $lines, $dup, $dup]);
  244. break;
  245. case "set-topic":
  246. $last_id = (int) $_REQUEST["last_id"];
  247. $topic = $_REQUEST["topic"];
  248. $chan = $_REQUEST["chan"];
  249. $connection_id = $_REQUEST["connection"];
  250. if ($topic !== FALSE) {
  251. handle_command($connection_id, $chan, "/topic $topic");
  252. }
  253. $lines = get_new_lines($last_id);
  254. $conn = get_conn_info();
  255. $chandata = get_chan_data();
  256. $params = get_misc_params();
  257. print json_encode(array($conn, $lines, $chandata, $params));
  258. break;
  259. case "login":
  260. $login = $_REQUEST["user"];
  261. $password = $_REQUEST["password"];
  262. if (authenticate_user($login, $password)) {
  263. $_SESSION["csrf_token"] = uniqid_short();
  264. print json_encode(array("sid" => session_id(), "version" => VERSION,
  265. "uniqid" => uniqid(), "csrf_token" => $_SESSION["csrf_token"]));
  266. } else {
  267. print json_encode(array("error" => 6));
  268. }
  269. break;
  270. case "init":
  271. $dbh = Db::get();
  272. $sth = $dbh->prepare("SELECT MAX(ttirc_messages.id) AS max_id
  273. FROM ttirc_messages, ttirc_connections
  274. WHERE connection_id = ttirc_connections.id AND owner_uid = ?");
  275. $sth->execute([$_SESSION["uid"]]);
  276. $rv = array();
  277. if ($row = $sth->fetch()) {
  278. $rv["max_id"] = $row['max_id'];
  279. } else {
  280. $rv["max_id"] = 0;
  281. }
  282. $rv["status"] = 1;
  283. $rv["theme"] = get_pref("USER_THEME");
  284. $rv["update_delay_max"] = UPDATE_DELAY_MAX;
  285. $rv["uniqid"] = uniqid();
  286. $rv["emoticons"] = [];
  287. $rv["emoticons_mtime"] = is_readable(EMOTICONS_MAP) ? filemtime(EMOTICONS_MAP) : -1;
  288. $rv["csrf_token"] = $_SESSION["csrf_token"];
  289. print json_encode($rv);
  290. break;
  291. case "prefs":
  292. main_prefs();
  293. break;
  294. case "prefs-conn-save":
  295. $title = $_REQUEST["title"];
  296. $autojoin = $_REQUEST["autojoin"];
  297. $connect_cmd = $_REQUEST["connect_cmd"];
  298. $encoding = $_REQUEST["encoding"];
  299. $nick = $_REQUEST["nick"];
  300. $auto_connect = bool_to_sql_bool($_REQUEST["auto_connect"]);
  301. $permanent = bool_to_sql_bool($_REQUEST["permanent"]);
  302. $connection_id = $_REQUEST["connection_id"];
  303. $visible = bool_to_sql_bool($_REQUEST["visible"]);
  304. $server_password = $_REQUEST["server_password"];
  305. $use_ssl = bool_to_sql_bool($_REQUEST["use_ssl"]);
  306. if (!$title) $title = __("[Untitled]");
  307. if (valid_connection($connection_id)) {
  308. $sth = $dbh->prepare("UPDATE ttirc_connections SET title = ?,
  309. autojoin = ?,
  310. connect_cmd = ?,
  311. auto_connect = ?,
  312. server_password = ?,
  313. visible = ?,
  314. use_ssl = ?,
  315. nick = ?,
  316. encoding = ?,
  317. permanent = ?
  318. WHERE id = ?");
  319. $sth->execute([$title, $autojoin, $connect_cmd, $auto_connect, $server_password,
  320. $visible, $use_ssl, $nick, $encoding, $permanent, $connection_id]);
  321. //print json_encode(array("error" => "Function not implemented."));
  322. }
  323. print json_encode(["status" => "OK"]);
  324. break;
  325. case "prefs-save":
  326. //print json_encode(array("error" => "Function not implemented."));
  327. $realname = $_REQUEST["realname"];
  328. $quit_message = $_REQUEST["quit_message"];
  329. $new_password = $_REQUEST["new_password"];
  330. $confirm_password = $_REQUEST["confirm_password"];
  331. $nick = $_REQUEST["nick"];
  332. $email = $_REQUEST["email"];
  333. $theme = $_REQUEST["theme"];
  334. $highlight_on = $_REQUEST["highlight_on"];
  335. $hide_join_part = bool_to_sql_bool($_REQUEST["hide_join_part"]);
  336. $disable_image_preview = bool_to_sql_bool($_REQUEST["disable_image_preview"]);
  337. $theme_changed = false;
  338. $_SESSION["prefs_cache"] = false;
  339. if (get_user_theme() != $theme) {
  340. set_pref("USER_THEME", $theme);
  341. $theme_changed = true;
  342. }
  343. set_pref("HIGHLIGHT_ON", $highlight_on);
  344. set_pref("DISABLE_IMAGE_PREVIEW", $disable_image_preview);
  345. $sth = $dbh->prepare("UPDATE ttirc_users SET realname = ?,
  346. quit_message = ?,
  347. email = ?,
  348. hide_join_part = ?,
  349. nick = ? WHERE id = ?");
  350. $sth->execute([$realname, $quit_message, $email, $hide_join_part, $nick, $_SESSION['uid']]);
  351. if ($new_password != $confirm_password) {
  352. print json_encode(array("error" => "Passwords do not match."));
  353. return;
  354. }
  355. if ($confirm_password == $new_password && $new_password) {
  356. $salt = substr(bin2hex(get_random_bytes(125)), 0, 250);
  357. $pwd_hash = encrypt_password($new_password, $salt, true);
  358. $sth = $dbh->prepare("UPDATE ttirc_users SET pwd_hash = ?, salt = ?
  359. WHERE id = ?");
  360. $sth->execute([$pwd_hash, $salt, $_SESSION['uid']]);
  361. }
  362. if ($theme_changed) {
  363. print json_encode(array("message" => "THEME_CHANGED"));
  364. return;
  365. }
  366. break;
  367. case "prefs-edit-con":
  368. $connection_id = (int) $_REQUEST["id"];
  369. connection_editor($connection_id);
  370. break;
  371. case "prefs-customize-css":
  372. css_editor();
  373. break;
  374. case "prefs-save-css":
  375. $user_css = $_REQUEST["user_css"];
  376. set_pref("USER_STYLESHEET", $user_css);
  377. print json_encode(["status" => "OK"]);
  378. //print json_encode(array("error" => "Function not implemented."));
  379. break;
  380. case "create-server":
  381. $connection_id = (int) $_REQUEST["connection_id"];
  382. list($server, $port) = explode(":", $_REQUEST["data"]);
  383. if (valid_connection($connection_id)) {
  384. if ($server && $port) {
  385. $sth = $dbh->prepare("INSERT INTO ttirc_servers (server, port, connection_id)
  386. VALUES (?, ?, ?)");
  387. $sth->execute([$server, $port, $connection_id]);
  388. print_servers($connection_id);
  389. } else {
  390. $error = T_sprintf("Couldn't add server (%s:%d): Invalid syntax.",
  391. $server, $port);
  392. print json_encode(array("error" => $error));
  393. }
  394. }
  395. break;
  396. case "delete-server":
  397. $ids = explode(",", $_REQUEST["ids"]);
  398. $connection_id = (int) $_REQUEST["connection_id"];
  399. if (valid_connection($connection_id)) {
  400. $ids_qmarks = arr_qmarks($ids);
  401. $sth = $dbh->prepare("DELETE FROM ttirc_servers WHERE
  402. id in ($ids_qmarks) AND connection_id = ?");
  403. $sth->execute(array_merge($ids, [$connection_id]));
  404. print_servers($connection_id);
  405. }
  406. break;
  407. case "delete-connection":
  408. $ids = explode(",", $_REQUEST["ids"]);
  409. $ids_qmarks = arr_qmarks($ids);
  410. $sth = $dbh->prepare("DELETE FROM ttirc_connections WHERE
  411. id IN ($ids_qmarks) AND status = 0 AND owner_uid = ?");
  412. $sth = $sth->execute(array_merge($ids, [$_SESSION['uid']]));
  413. print_connections();
  414. break;
  415. case "create-connection":
  416. $title = trim(str_replace("_ttirc_instance", "", $_REQUEST["title"]));
  417. if ($title) {
  418. $sth = $dbh->prepare( "INSERT INTO ttirc_connections (enabled, title, owner_uid)
  419. VALUES (false, ?, ?)");
  420. $sth->execute([$title, $_SESSION['uid']]);
  421. }
  422. print_connections();
  423. break;
  424. case "toggle-connection":
  425. $connection_id = (int) $_REQUEST["connection_id"];
  426. $status = bool_to_sql_bool($_REQUEST["set_enabled"]);
  427. $server = get_random_server($connection_id);
  428. if (!$server) $status = 0;
  429. $sth = $dbh->prepare("UPDATE ttirc_connections SET enabled = ?
  430. WHERE id = ? AND owner_uid = ?");
  431. $sth->execute([$status, $connection_id, $_SESSION['uid']]);
  432. print json_encode(array("status" => $status));
  433. break;
  434. case "prefs-edit-notify":
  435. notification_editor();
  436. break;
  437. case "prefs-save-notify":
  438. $notify_events = json_encode($_REQUEST["notify_event"]);
  439. set_pref("NOTIFY_ON", $notify_events);
  440. print json_encode(["status" => "OK"]);
  441. break;
  442. case "preview":
  443. $url = htmlspecialchars($_REQUEST["url"]);
  444. header("Location: $url");
  445. break;
  446. case "emoticons":
  447. @$modified = (int)$_REQUEST['modified'];
  448. if (file_exists(EMOTICONS_MAP) &&
  449. (!$modified || $modified < filemtime(EMOTICONS_MAP))) {
  450. header("X-Map-Last-Modified: " . filemtime(EMOTICONS_MAP));
  451. print file_get_contents(EMOTICONS_MAP);
  452. } else {
  453. print json_encode([]);
  454. }
  455. break;
  456. case "emoticons_list":
  457. header('Content-Type: text/html; charset=utf-8');
  458. render_emoticons_full();
  459. break;
  460. case "embed":
  461. $url = htmlspecialchars($_REQUEST["url"]);
  462. header("Content-Type: text/html");
  463. print "<html><head>
  464. <title>Tiny Tiny IRC: $url</title>
  465. <style type='text/css'>
  466. body { background : #333;
  467. margin : 0px; padding : 0px;
  468. text-align : center;
  469. height : 100%; width : 100%; }
  470. video, img.fit { max-width : 100%; max-height : 100%;
  471. width : auto; height : auto;
  472. position : relative; top : 50%; transform: translateY(-50%); }
  473. img {
  474. cursor : pointer;
  475. }
  476. </style></head><body>";
  477. print javascript_tag("lib/bootstrap/js/jquery.js");
  478. if (preg_match("/\.(mp4|webm|gifv)/", $url, $matches)) {
  479. $type = $matches[1];
  480. $embed_url = $url;
  481. if ($type == "gifv") {
  482. $type = "mp4";
  483. $embed_url = str_replace(".gifv", ".mp4", $embed_url);
  484. }
  485. header("Content-type: text/html");
  486. print "<video class=\"\" autoplay=\"true\" controls=\"true\" loop=\"true\">";
  487. print "<source src=\"$embed_url\" type=\"video/$type\">";
  488. print "</video>";
  489. print "<script type='text/javascript'>
  490. $('video').height($(document).height() * 0.95);
  491. </script>";
  492. } else {
  493. print "<img class=\"fit\" src=\"$url\">";
  494. print "<script type='text/javascript'>
  495. $('img').click(function(e) {
  496. $(this).toggleClass('fit');
  497. });
  498. </script>";
  499. }
  500. print "</body></html>";
  501. break;
  502. case "vidproxy";
  503. $url = $_REQUEST["url"];
  504. if (preg_match("/\.(mp4|webm|gifv)/", $url, $matches)) {
  505. $type = $matches[1];
  506. $embed_url = $url;
  507. if ($type == "gifv") {
  508. $type = "mp4";
  509. $embed_url = str_replace(".gifv", ".mp4", $embed_url);
  510. }
  511. header("Content-type: text/html");
  512. $embed_url = htmlspecialchars("backend.php?op=imgproxy&url=" . urlencode($embed_url));
  513. print "<video class=\"\" autoplay=\"true\" controls=\"true\" loop=\"true\">";
  514. print "<source src=\"$embed_url\" type=\"video/$type\">";
  515. print "</video>";
  516. } else {
  517. header("Location: " . htmlspecialchars($url));
  518. }
  519. break;
  520. case "chanmute":
  521. $connection_id = (int) $_REQUEST["connection_id"];
  522. $channel = $_REQUEST["channel"];
  523. if ($channel != "---") {
  524. $sth = $dbh->prepare("SELECT id FROM ttirc_connections WHERE
  525. id = ? AND owner_uid = ?");
  526. $sth->execute([$connection_id, $_SESSION['uid']]);
  527. if ($sth->fetch()) {
  528. $sth = $dbh->prepare("UPDATE ttirc_channels SET muted = NOT muted
  529. WHERE connection_id = ? AND LOWER(channel) = LOWER(?) RETURNING muted");
  530. $sth->execute([$connection_id, $channel]);
  531. if ($row = $sth->fetch()) {
  532. print json_encode(["connection_id" => $connection_id,
  533. "channel" => $channel, "muted" => $row['muted']]);
  534. }
  535. }
  536. } else {
  537. $sth = $dbh->prepare("UPDATE ttirc_connections SET muted = NOT muted
  538. WHERE id = ? AND owner_uid = ? RETURNING muted");
  539. $sth->execute([$connection_id, $_SESSION['uid']]);
  540. if ($row = $sth->fetch()) {
  541. print json_encode(["connection_id" => $connection_id,
  542. "channel" => "---", "muted" => $row['muted'] ]);
  543. }
  544. }
  545. break;
  546. case "uploadandpost":
  547. $file = $_FILES['file'];
  548. $reason = "";
  549. if (!is_writable("cache/uploads")) {
  550. $reason = __("Cache not writable");
  551. } else if (!$file) {
  552. $reason = __("No file uploaded.");
  553. } else if ($file['size'] > 50000000) {
  554. $reason = __("File is too large.");
  555. } else {
  556. $new_file_name = 'cache/uploads/' . time() . '-' . basename($file['name']);
  557. $new_file_name = str_replace(" ", "_", $new_file_name);
  558. $result = move_uploaded_file($file['tmp_name'], $new_file_name);
  559. if ($result) {
  560. chmod($new_file_name, 0644);
  561. $url = get_self_url_prefix() . "/$new_file_name";
  562. if (function_exists("shorten_url")) $url = shorten_url($url);
  563. print json_encode(['upload_url' => $url]);
  564. return;
  565. } else {
  566. $reason = __("Error moving file.");
  567. }
  568. }
  569. print json_encode(["status" => "UPLOAD_FAILED", "reason" => $reason]);
  570. break;
  571. case "urlmetadata":
  572. if (defined('FETCH_URL_TITLES') && FETCH_URL_TITLES && class_exists("Memcached")) {
  573. $srv = explode(":", MEMCACHE_SERVER, 2);
  574. $memcache = new Memcached();
  575. $memcache->addServer($srv[0], $srv[1]);
  576. $base_url = $_SERVER["REQUEST_SCHEME"] . "://" . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"];
  577. $url = rewrite_relative_url($base_url, $_REQUEST["url"]);
  578. $key = 'ttirc.urlcache:' . sha1($url);
  579. $req_key = 'ttirc.urlcache-req:' . sha1($url);
  580. $nocache = (bool) $_REQUEST["nocache"];
  581. if (!$nocache && !$rv = $memcache->get($key)) {
  582. $tries = 0;
  583. while ($tries < PROXY_CONCUR_TRIES && $rc = $memcache->get($req_key)) {
  584. if (!$rc || $rc == 2)
  585. break;
  586. $tries++;
  587. sleep(1);
  588. }
  589. }
  590. if ($nocache || !$rv = $memcache->get($key)) {
  591. $rv = [ ];
  592. $options = ['http'=> [
  593. 'protocol_version'=> 1.1,
  594. 'header' => [
  595. 'Connection: close',
  596. 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*\/*;q=0.8',
  597. '_Range: bytes=0-32768' ],
  598. 'timeout' => 10 ]
  599. ];
  600. if (defined('_HTTP_PROXY')) {
  601. $options['http']['request_fulluri'] = true;
  602. $options['http']['proxy'] = _HTTP_PROXY;
  603. }
  604. $memcache->set($req_key, PROXY_REQ_PROGRESS, CACHE_LIFETIME_MAX);
  605. if (strpos($url, "//") === 0) $url = "https:" . $url;
  606. $ctx = stream_context_create($options);
  607. $data = @file_get_contents($url, false, $ctx);
  608. if (isset($http_response_header) && is_array($http_response_header)) {
  609. foreach ($http_response_header as $header) {
  610. if (strstr($header, ": ") !== FALSE) {
  611. list ($key, $value) = explode(": ", $header);
  612. if (strtolower($key) == 'content-type') $rv['content-type'] = $value;
  613. }
  614. }
  615. }
  616. if ($data) {
  617. $tmp = @gzdecode($data);
  618. if ($tmp) $data = $tmp;
  619. $data = mb_substr($data, 0, 32768);
  620. $doc = new DOMDocument('UTF-8');
  621. $preamble = '<?xml encoding="utf-8" ?>\n';
  622. if (@$doc->loadHTML($preamble . $data)) {
  623. $xpath = new DOMXpath($doc);
  624. $m_title = $xpath->query("//meta[@property='og:title']")->item(0);
  625. $m_image = $xpath->query("//meta[@property='og:image']")->item(0);
  626. $m_descr = $xpath->query("//meta[@property='og:description']")->item(0);
  627. if ($m_title) {
  628. $rv['title'] = $m_title->getAttribute('content');
  629. } else {
  630. $node = $doc->getElementsByTagName('title')->item(0);
  631. if ($node) {
  632. $rv['title'] = preg_replace("/[\r\n\t]/", "", trim($node->nodeValue));
  633. }
  634. }
  635. if ($m_image)
  636. $rv['image'] = rewrite_relative_url($url, $m_image->getAttribute('content'));
  637. if ($m_descr)
  638. $rv['descr'] = $m_descr->getAttribute('content');
  639. foreach ($rv as $k => $v) {
  640. $rv[$k] = mb_substr($v, 0, 1024);
  641. }
  642. if ($rv['title'])
  643. $memcache->set($key, $rv, CACHE_LIFETIME_MAX);
  644. }
  645. $memcache->set($req_key, PROXY_REQ_OK, CACHE_LIFETIME_MAX);
  646. } else {
  647. $memcache->set($req_key, PROXY_REQ_FAILED, CACHE_LIFETIME_MAX);
  648. }
  649. }
  650. print json_encode($rv);
  651. }
  652. break;
  653. case "imgproxy";
  654. $base_url = $_SERVER["REQUEST_SCHEME"] . "://" . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"];
  655. $url = rewrite_relative_url($base_url, $_REQUEST["url"]);
  656. @$resize = (int) $_REQUEST['resize'];
  657. @$nocache = (bool) $_REQUEST['nocache'];
  658. $cache_key = "cache/imgproxy/" . sha1($url) . "-$resize.jpg";
  659. $mem_key = 'ttirc.imgcache:' . sha1($cache_key);
  660. $memcache = false;
  661. if (class_exists("Memcached") && defined('MEMCACHE_SERVER') && MEMCACHE_SERVER) {
  662. $srv = explode(":", MEMCACHE_SERVER, 2);
  663. $memcache = new Memcached();
  664. $memcache->addServer($srv[0], $srv[1]);
  665. }
  666. if (!file_exists($cache_key) || filesize($cache_key) == 0) {
  667. if ($memcache) {
  668. $tries = 0;
  669. while ($tries < PROXY_CONCUR_TRIES && $rc = $memcache->get($mem_key)) {
  670. if (!$rc || $rc == 2)
  671. break;
  672. $tries++;
  673. sleep(1);
  674. }
  675. } else {
  676. sleep(rand(1,3));
  677. }
  678. }
  679. if (!$nocache && file_exists($cache_key) && filesize($cache_key) > 0) {
  680. @touch($cache_key);
  681. $ctype = mime_content_type($cache_key);
  682. header("Content-type: $ctype");
  683. header("Cache-control: max-age=" . CACHE_LIFETIME_MAX);
  684. header("Last-Modified: " .
  685. gmdate("D, d M Y H:i:s \G\M\T", filemtime($cache_key)));
  686. header("Expires: " .
  687. gmdate("D, d M Y H:i:s \G\M\T", filemtime($cache_key)+CACHE_LIFETIME_MAX));
  688. if (defined('_NGINX_XACCEL_PREFIX')) {
  689. header("X-Accel-Redirect: " . _NGINX_XACCEL_PREFIX . "/$cache_key");
  690. } else {
  691. readfile($cache_key);
  692. }
  693. } else {
  694. if ($memcache) $memcache->set($mem_key, PROXY_REQ_PROGRESS, CACHE_LIFETIME_MAX);
  695. $options = ['http'=> [
  696. 'protocol_version'=> 1.1,
  697. 'header' => [
  698. 'Connection: close',
  699. ],
  700. 'timeout' => 15 ]
  701. ];
  702. if (defined('_HTTP_PROXY')) {
  703. $options['http']['request_fulluri'] = true;
  704. $options['http']['proxy'] = _HTTP_PROXY;
  705. }
  706. if (strpos($url, "//") === 0) $url = "https:" . $url;
  707. $ctx = stream_context_create($options);
  708. $data = @file_get_contents($url, false, $ctx);
  709. if ($data) {
  710. if ($resize)
  711. $data = make_thumbnail($data, $resize, $resize);
  712. if (@file_put_contents($cache_key, $data)) {
  713. $ctype = mime_content_type($cache_key);
  714. header("Content-Type: $ctype");
  715. header("Cache-control: max-age=" . CACHE_LIFETIME_MAX);
  716. header("Last-Modified: " .
  717. gmdate("D, d M Y H:i:s \G\M\T", filemtime($cache_key)));
  718. header("Expires: " .
  719. gmdate("D, d M Y H:i:s \G\M\T", filemtime($cache_key)+CACHE_LIFETIME_MAX));
  720. }
  721. if ($memcache) $memcache->set($mem_key, PROXY_REQ_OK, CACHE_LIFETIME_MAX);
  722. print $data;
  723. } else {
  724. if ($memcache) $memcache->set($mem_key, PROXY_REQ_FAILED, CACHE_LIFETIME_MAX);
  725. }
  726. }
  727. break;
  728. case "expirecaches":
  729. $files = glob("cache/uploads/*");
  730. foreach ($files as $file) {
  731. if (time() - filemtime($file) > CACHE_LIFETIME_MAX) {
  732. unlink($file);
  733. }
  734. }
  735. $files = glob("cache/imgproxy/*.jpg");
  736. foreach ($files as $file) {
  737. if (time() - filemtime($file) > CACHE_LIFETIME_MAX) {
  738. unlink($file);
  739. }
  740. }
  741. print json_encode(["status" => "OK"]);
  742. break;
  743. case "logout":
  744. logout_user();
  745. header("Location: index.php");
  746. break;
  747. }
  748. ?>