backend.php 26 KB

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