backend.php 24 KB

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