functions.php 47 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023
  1. <?php
  2. if (defined('E_DEPRECATED')) {
  3. error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED);
  4. } else {
  5. error_reporting(E_ALL & ~E_NOTICE);
  6. }
  7. mb_internal_encoding("UTF-8");
  8. require_once "config.php";
  9. require_once "version.php";
  10. require_once "message_types.php";
  11. require_once "classes/db.php";
  12. require_once "db-prefs.php";
  13. define('SINGLE_USER_MODE', false);
  14. define('EMOTICONS_MAP', 'emoticons/emoticons.json');
  15. define('DEFAULT_BUFFER_SIZE', 256);
  16. $connection_nick_cache = [];
  17. if (DB_TYPE == 'mysql') {
  18. define('DB_KEY_FIELD', 'param');
  19. } else {
  20. define('DB_KEY_FIELD', 'key');
  21. }
  22. if (DB_TYPE == "pgsql") {
  23. define('SUBSTRING_FOR_DATE', 'SUBSTRING_FOR_DATE');
  24. } else {
  25. define('SUBSTRING_FOR_DATE', 'SUBSTRING');
  26. }
  27. define('URL_REGEXP', "/(([a-z]+):\/\/[(,_)'\[email protected]:\!%_\+.~#?&\/=]+)/i");
  28. function get_translations() {
  29. $tr = array(
  30. "auto" => "Detect automatically",
  31. "en_US" => "English",
  32. "es_MX" => "Spanish (Mexican)",
  33. "fr_FR" => "Français",
  34. "xx_XX" => "Bork bork bork!");
  35. return $tr;
  36. }
  37. if (ENABLE_TRANSLATIONS == true) { // If translations are enabled.
  38. require_once "lib/accept-to-gettext.php";
  39. require_once "lib/gettext/gettext.inc";
  40. function startup_gettext() {
  41. # Get locale from Accept-Language header
  42. $lang = al2gt(array_keys(get_translations()), "text/html");
  43. if (defined('_TRANSLATION_OVERRIDE_DEFAULT')) {
  44. $lang = _TRANSLATION_OVERRIDE_DEFAULT;
  45. }
  46. if ($_COOKIE["ttirc_lang"] && $_COOKIE["ttirc_lang"] != "auto") {
  47. $lang = $_COOKIE["ttirc_lang"];
  48. }
  49. /* In login action of mobile version */
  50. if ($_POST["language"] && defined('MOBILE_VERSION')) {
  51. $lang = $_POST["language"];
  52. $_COOKIE["ttirc_lang"] = $lang;
  53. }
  54. if ($lang) {
  55. if (defined('LC_MESSAGES')) {
  56. _setlocale(LC_MESSAGES, $lang);
  57. } else if (defined('LC_ALL')) {
  58. _setlocale(LC_ALL, $lang);
  59. } else {
  60. die("can't setlocale(): please set ENABLE_TRANSLATIONS to false in config.php");
  61. }
  62. if (defined('MOBILE_VERSION')) {
  63. _bindtextdomain("messages", "../locale");
  64. } else {
  65. _bindtextdomain("messages", "locale");
  66. }
  67. _textdomain("messages");
  68. _bind_textdomain_codeset("messages", "UTF-8");
  69. }
  70. }
  71. startup_gettext();
  72. } else { // If translations are enabled.
  73. function __($msg) {
  74. return $msg;
  75. }
  76. function startup_gettext() {
  77. // no-op
  78. return true;
  79. }
  80. } // If translations are enabled.
  81. require_once "errors.php";
  82. function uniqid_short() {
  83. return uniqid(base_convert(rand(), 10, 36));
  84. }
  85. function validate_nick($nick) {
  86. $tmp = strip_prefix(trim(mb_substr(strip_tags($nick), 0, 119)));
  87. $tmp = preg_replace("/([#:\n]|---)/", "", $tmp);
  88. return $tmp;
  89. }
  90. function is_instance($connection_id) {
  91. $dbh = DB::get();
  92. if (isset($_SESSION['is_instance_cache']) && isset($_SESSION['is_instance_cache'][$connection_id]))
  93. return $_SESSION['is_instance_cache'][$connection_id];
  94. $sth = $dbh->prepare("SELECT id FROM ttirc_connections WHERE
  95. id = ? AND title = '_ttirc_instance'");
  96. $sth->execute([$connection_id]);
  97. if (!isset($_SESSION['is_instance_cache']))
  98. $_SESSION['is_instance_cache'] = [];
  99. if ($sth->fetch()) {
  100. $_SESSION['is_instance_cache'][$connection_id] = true;
  101. return true;
  102. } else {
  103. $_SESSION['is_instance_cache'][$connection_id] = false;
  104. return false;
  105. }
  106. }
  107. function login_sequence($mobile = false) {
  108. $_SESSION["prefs_cache"] = array();
  109. $dbh = DB::get();
  110. if (!SINGLE_USER_MODE) {
  111. $login_action = $_POST["login_action"];
  112. # try to authenticate user if called from login form
  113. if ($login_action == "do_login") {
  114. $login = $_POST["login"];
  115. $password = $_POST["password"];
  116. $remember_me = $_POST["remember_me"];
  117. if (authenticate_user($login, $password)) {
  118. $_POST["password"] = "";
  119. $_SESSION["language"] = $_POST["language"];
  120. $_SESSION["ref_schema_version"] = get_schema_version(true);
  121. $_SESSION["bw_limit"] = !!$_POST["bw_limit"];
  122. $_SESSION["csrf_token"] = uniqid_short();
  123. header("Location: " . $_SERVER["REQUEST_URI"]);
  124. exit;
  125. return;
  126. } else {
  127. $_SESSION["login_error_msg"] = __("Incorrect username or password");
  128. }
  129. }
  130. if (!$_SESSION["uid"] || !validate_session()) {
  131. header('Cache-Control: public');
  132. render_login_form($mobile);
  133. //header("Location: login.php");
  134. exit;
  135. } else {
  136. /* bump login timestamp */
  137. if (get_schema_version() >= 3) {
  138. $sth = $dbh->prepare("UPDATE ttirc_users SET last_login = NOW() WHERE id = ?");
  139. $sth->execute([$_SESSION['uid']]);
  140. }
  141. $_SESSION["last_login_update"] = time();
  142. if ($_SESSION["language"] && SESSION_COOKIE_LIFETIME > 0) {
  143. setcookie("ttirc_lang", $_SESSION["language"],
  144. time() + SESSION_COOKIE_LIFETIME);
  145. }
  146. /* Enable automatic connections */
  147. $sth = $dbh->prepare("UPDATE ttirc_connections SET enabled = true
  148. WHERE auto_connect = true AND owner_uid = ?");
  149. $sth->execute([$_SESSION['uid']]);
  150. initialize_user_prefs($_SESSION["uid"]);
  151. }
  152. } else {
  153. return authenticate_user("admin", null);
  154. }
  155. }
  156. function render_login_form($mobile = 0) {
  157. switch ($mobile) {
  158. case 0:
  159. require_once "login_form.php";
  160. break;
  161. case 1:
  162. require_once "mobile/login_form.php";
  163. break;
  164. case 2:
  165. require_once "mobile/classic/login_form.php";
  166. }
  167. }
  168. function print_select($id, $default, $values, $attributes = "") {
  169. print "<select class=\"form-control\" name=\"$id\" id=\"$id\" $attributes>";
  170. foreach ($values as $v) {
  171. if ($v == $default)
  172. $sel = " selected";
  173. else
  174. $sel = "";
  175. print "<option$sel>$v</option>";
  176. }
  177. print "</select>";
  178. }
  179. function print_select_hash($id, $default, $values, $attributes = "") {
  180. print "<select name=\"$id\" id='$id' $attributes>";
  181. foreach (array_keys($values) as $v) {
  182. if ($v == $default)
  183. $sel = 'selected="selected"';
  184. else
  185. $sel = "";
  186. print "<option $sel value=\"$v\">".$values[$v]."</option>";
  187. }
  188. print "</select>";
  189. }
  190. function encrypt_password($pass, $salt = '', $mode2 = false) {
  191. if ($salt && $mode2) {
  192. return "MODE2:" . hash('sha256', $salt . $pass);
  193. } else if ($salt) {
  194. return "SHA1X:" . sha1("$salt:$pass");
  195. } else {
  196. return "SHA1:" . sha1($pass);
  197. }
  198. } // function encrypt_password
  199. function authenticate_user($login, $password, $force_auth = false) {
  200. $dbh = DB::get();
  201. if (!SINGLE_USER_MODE) {
  202. $pwd_hash1 = encrypt_password($password);
  203. $pwd_hash2 = encrypt_password($password, $login);
  204. $lsth = false;
  205. if (get_schema_version() > 6) {
  206. $sth = $dbh->prepare("SELECT salt FROM ttirc_users WHERE
  207. login = ?");
  208. $sth->execute([$login]);
  209. $row = $sth->fetch();
  210. $salt = $row['salt'];
  211. if ($salt == "") {
  212. // verify and upgrade password to new salt base
  213. $sth = $dbh->prepare("SELECT id,login,access_level,pwd_hash
  214. FROM ttirc_users WHERE
  215. login = ? AND (pwd_hash = ? OR pwd_hash = ?)");
  216. $sth->execute([$login, $pwd_hash1, $pwd_hash2]);
  217. if ($row = $sth->fetch()) {
  218. // upgrade password to MODE2
  219. $salt = substr(bin2hex(get_random_bytes(125)), 0, 250);
  220. $pwd_hash = encrypt_password($password, $salt, true);
  221. $sth = $dbh->prepare("UPDATE ttirc_users SET
  222. pwd_hash = ?, salt = ? WHERE login = ?");
  223. $sth->execute([$pwd_hash, $salt, $login]);
  224. $lsth = $dbh->prepare("SELECT id,login,access_level,pwd_hash
  225. FROM ttirc_users WHERE
  226. login = ? AND pwd_hash = ?");
  227. $lsth->execute([$login, $pwd_hash]);
  228. } else {
  229. return false;
  230. }
  231. } else {
  232. $pwd_hash = encrypt_password($password, $salt, true);
  233. $lsth = $dbh->prepare("SELECT id,login,access_level,pwd_hash
  234. FROM ttirc_users WHERE
  235. login = ? AND pwd_hash = ?");
  236. $lsth->execute([$login, $pwd_hash]);
  237. }
  238. } else {
  239. $lsth = $dbh->prepare("SELECT id,login,access_level,pwd_hash
  240. FROM ttirc_users WHERE
  241. login = ? AND (pwd_hash = ? OR pwd_hash = ?)");
  242. $lsth->execute([$login, $pwd_hash1, $pwd_hash2]);
  243. }
  244. if ($lsth && $row = $lsth->fetch()) {
  245. $_SESSION["uid"] = $row["id"];
  246. $_SESSION["name"] = $row["login"];
  247. $_SESSION["access_level"] = $row["access_level"];
  248. $sth = $dbh->prepare("UPDATE ttirc_users SET last_login = NOW() WHERE id = ?");
  249. $sth->execute([$_SESSION['uid']]);
  250. $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"];
  251. $_SESSION["pwd_hash"] = $row["pwd_hash"];
  252. initialize_user_prefs($_SESSION["uid"]);
  253. return true;
  254. }
  255. return false;
  256. } else {
  257. $_SESSION["uid"] = 1;
  258. $_SESSION["name"] = "admin";
  259. $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"];
  260. initialize_user_prefs($_SESSION["uid"]);
  261. return true;
  262. }
  263. }
  264. function get_schema_version($nocache = false) {
  265. if (!$_SESSION["schema_version"] || $nocache) {
  266. $dbh = DB::get();
  267. $row = $dbh->query("SELECT schema_version FROM ttirc_version")->fetch();
  268. $version = $row['schema_version'];
  269. $_SESSION["schema_version"] = $version;
  270. return $version;
  271. } else {
  272. return $_SESSION["schema_version"];
  273. }
  274. }
  275. function validate_session() {
  276. $dbh = DB::get();
  277. if (SINGLE_USER_MODE) {
  278. return true;
  279. }
  280. if ($_SESSION["ref_schema_version"] != get_schema_version(true)) {
  281. return false;
  282. }
  283. if ($_SESSION["uid"]) {
  284. $sth = $dbh->prepare("SELECT pwd_hash FROM ttirc_users WHERE id = ?");
  285. $sth->execute([$_SESSION['uid']]);
  286. $row = $sth->fetch();
  287. if ($row['pwd_hash'] != $_SESSION["pwd_hash"]) {
  288. return false;
  289. }
  290. }
  291. return true;
  292. }
  293. function get_script_dt_add() {
  294. return time();
  295. }
  296. function theme_image($filename) {
  297. $theme_path = get_user_theme_path();
  298. if ($theme_path && is_file($theme_path.$filename)) {
  299. return $theme_path."/".$filename;
  300. } else {
  301. return $filename;
  302. }
  303. }
  304. function get_user_theme_params() {
  305. $theme_name = get_user_theme();
  306. if ($theme_name) {
  307. if (is_dir("themes.local/$theme_name")) {
  308. return parse_ini_file("themes.local/$theme_name/theme.ini");
  309. } else {
  310. return parse_ini_file("themes/$theme_name/theme.ini");
  311. }
  312. } else {
  313. return false;
  314. }
  315. }
  316. function get_user_theme() {
  317. $theme_name = get_pref("USER_THEME");
  318. if (is_dir("themes.local/$theme_name")) {
  319. return $theme_name;
  320. } else if (is_dir("themes/$theme_name")) {
  321. return $theme_name;
  322. } else {
  323. return '';
  324. }
  325. }
  326. function get_user_theme_path() {
  327. $theme_name = get_pref("USER_THEME");
  328. if ($theme_name && is_dir("themes.local/$theme_name")) {
  329. $theme_path = "themes.local/$theme_name";
  330. } else if ($theme_name && is_dir("themes/$theme_name")) {
  331. $theme_path = "themes/$theme_name";
  332. } else {
  333. $theme_name = '';
  334. }
  335. return $theme_path;
  336. }
  337. function get_all_themes() {
  338. $themes = array_filter(array_merge(glob("themes/*"), glob("themes.local/*")), "is_dir");
  339. asort($themes);
  340. $rv = array();
  341. foreach ($themes as $t) {
  342. if (is_file("$t/theme.ini")) {
  343. $ini = parse_ini_file("$t/theme.ini", true);
  344. if ($ini['theme']['version'] && !$ini['theme']['disabled']) {
  345. $entry = array();
  346. $entry["path"] = $t;
  347. $entry["base"] = basename($t);
  348. $entry["name"] = $ini['theme']['name'];
  349. $entry["version"] = $ini['theme']['version'];
  350. $entry["author"] = $ini['theme']['author'];
  351. $entry["options"] = $ini['theme']['options'];
  352. array_push($rv, $entry);
  353. }
  354. }
  355. }
  356. return $rv;
  357. }
  358. function logout_user() {
  359. session_destroy();
  360. if (isset($_COOKIE[session_name()])) {
  361. setcookie(session_name(), '', time()-42000, '/');
  362. }
  363. }
  364. function format_warning($msg, $id = "") {
  365. return "<div class=\"alert alert-warning\" id=\"$id\">$msg</div>";
  366. }
  367. function format_notice($msg) {
  368. return "<div class=\"alert alert-info\" id=\"$id\">$msg</div>";
  369. }
  370. function format_error($msg) {
  371. return "<div class=\"alert alert-danger\" id=\"$id\">$msg</div>";
  372. }
  373. function print_notice($msg) {
  374. print format_notice($msg);
  375. }
  376. function print_warning($msg) {
  377. print format_warning($msg);
  378. }
  379. function print_error($msg) {
  380. print format_error($msg);
  381. }
  382. function T_sprintf() {
  383. $args = func_get_args();
  384. return vsprintf(__(array_shift($args)), $args);
  385. }
  386. function _debug($msg) {
  387. $ts = strftime("%H:%M:%S", time());
  388. if (function_exists('posix_getpid')) {
  389. $ts = "$ts/" . posix_getpid();
  390. }
  391. print "[$ts] $msg\n";
  392. } // function _debug
  393. function get_nick($connection_id) {
  394. global $connection_nick_cache;
  395. $dbh = DB::get();
  396. if (isset($connection_nick_cache[$connection_id])) {
  397. return $connection_nick_cache[$connection_id];
  398. }
  399. $sth = $dbh->prepare("SELECT active_nick FROM ttirc_connections
  400. WHERE id = ?");
  401. $sth->execute([$connection_id]);
  402. if ($row = $sth->fetch()) {
  403. $connection_nick_cache[$connection_id] = $row['active_nick'];
  404. return $connection_nick_cache[$connection_id];
  405. } else {
  406. return '?UNKNOWN?';
  407. }
  408. }
  409. function instance_userhosts($connection_id) {
  410. $dbh = DB::get();
  411. $sth = $dbh->query("SELECT DISTINCT login, realname,
  412. active_nick,
  413. heartbeat < NOW() - interval '15 minute' AS is_away
  414. FROM ttirc_users u, ttirc_connections cs WHERE
  415. cs.owner_uid = u.id AND cs.title = '_ttirc_instance'");
  416. $rv = [];
  417. while ($line = $sth->fetch()) {
  418. $rv[$line['active_nick']] = [
  419. $line["login"],
  420. "local",
  421. "local",
  422. $line["realname"],
  423. $line['is_away'],
  424. $line['is_away'] ? __('Offline') : '',
  425. ];
  426. }
  427. return $rv;
  428. }
  429. function instance_nicklist($channel) {
  430. $dbh = DB::get();
  431. $sth = $dbh->prepare("SELECT
  432. active_nick,
  433. access_level >= 5 AS is_op,
  434. (heartbeat < NOW() - interval '15 minutes') AS is_away,
  435. (last_message >= NOW() - interval '15 minutes') AS is_voiced
  436. FROM ttirc_connections cs, ttirc_users u
  437. WHERE title = '_ttirc_instance' AND
  438. u.id = cs.owner_uid AND
  439. (SELECT id FROM ttirc_channels WHERE LOWER(channel) = LOWER(?) AND connection_id = cs.id) > 0
  440. ORDER BY active_nick");
  441. $sth->execute([$channel]);
  442. $rv = [];
  443. while ($line = $sth->fetch()) {
  444. if ($line['is_op'])
  445. $line["active_nick"] = '@' . $line["active_nick"];
  446. else if (!$line["is_away"] && $line['is_voiced'])
  447. $line["active_nick"] = '+' . $line["active_nick"];
  448. array_push($rv, $line["active_nick"]);
  449. }
  450. return sort_nicklist($rv);
  451. }
  452. function sync_channel_topic($connection_id, $chan) {
  453. $dbh = DB::get();
  454. $sth = $dbh->prepare("UPDATE ttirc_channels
  455. SET topic = ex.topic, topic_owner = ex.topic_owner, topic_set = ex.topic_set
  456. FROM (SELECT topic, topic_owner, topic_set
  457. FROM ttirc_channels ch, ttirc_connections cs
  458. WHERE LOWER(channel) = LOWER(:chan)
  459. AND cs.title = '_ttirc_instance'
  460. AND cs.id = ch.connection_id
  461. AND ch.connection_id != :connection_id LIMIT 1) AS ex
  462. WHERE connection_id = :connection_id AND LOWER(channel) = LOWER(:chan)");
  463. $sth->execute([':chan' => $chan, ':connection_id' => $connection_id]);
  464. }
  465. function relay_others($except_connection_id, $channel, $message,
  466. $message_type = MSGT_PRIVMSG, $sender = false) {
  467. if (!$message) return;
  468. $dbh = DB::get();
  469. $sth = false;
  470. if ($channel[0] == "#") {
  471. if (!$sender) {
  472. if ($channel != "---") {
  473. $sender = get_nick($except_connection_id);
  474. } else {
  475. $sender = "---";
  476. }
  477. }
  478. $userhost = mb_substr($_SESSION['name'] . '@local', 0, 119);
  479. $sth = $dbh->prepare("INSERT INTO ttirc_messages
  480. (incoming, connection_id, channel, sender, message, message_type, ts, userhost)
  481. SELECT ?, cs.id, ?, ?, ?, ?, NOW(), ? FROM ttirc_connections cs, ttirc_channels ch
  482. WHERE title = '_ttirc_instance' AND ch.connection_id = cs.id
  483. AND LOWER(channel) = LOWER(?) AND cs.id != ?");
  484. $sth->execute([true, $channel, $sender, $message, $message_type, $userhost,
  485. $channel, $except_connection_id]);
  486. }
  487. }
  488. function relay_message($connection_id, $channel, $message,
  489. $message_type = MSGT_PRIVMSG, $sender = false) {
  490. $dbh = DB::get();
  491. if (!$message) return;
  492. if (!$sender) {
  493. if ($channel != "---") {
  494. $sender = get_nick($connection_id);
  495. } else {
  496. $sender = "---";
  497. }
  498. }
  499. if ($channel == "---" || $channel[0] == "#") {
  500. if (is_instance($connection_id)) {
  501. $userhost = mb_substr($_SESSION['name'] . '@local', 0, 119);
  502. } else {
  503. $userhost = null;
  504. }
  505. $sth = false;
  506. if ($channel == "---") {
  507. $sth = $dbh->prepare("INSERT INTO ttirc_messages
  508. (incoming, connection_id, channel, sender, message, message_type, ts, userhost)
  509. SELECT ?, cs.id, ?, ?, ?, ?, NOW(), ? FROM ttirc_connections cs
  510. WHERE title = '_ttirc_instance' AND LOWER(active_nick) = LOWER(?)");
  511. } else {
  512. $sth = $dbh->prepare("INSERT INTO ttirc_messages
  513. (incoming, connection_id, channel, sender, message, message_type, ts, userhost)
  514. SELECT ?, cs.id, ?, ?, ?, ?, NOW(), ? FROM ttirc_connections cs, ttirc_channels ch
  515. WHERE title = '_ttirc_instance' AND ch.connection_id = cs.id AND LOWER(channel) = LOWER(?)");
  516. }
  517. $sth->execute([true, $channel, $sender, $message, $message_type, $userhost, $channel]);
  518. } else {
  519. $sth = $dbh->prepare("SELECT id FROM ttirc_connections WHERE
  520. title = '_ttirc_instance' AND LOWER(active_nick) = LOWER(?) LIMIT 1");
  521. $sth->execute([$channel]);
  522. if ($row = $sth->fetch()) {
  523. $dst_conn_id = (int) $row['id'];
  524. $dst_has_chan = false;
  525. push_message($connection_id, $channel, $message,
  526. false, $message_type,
  527. $sender);
  528. $sth = $dbh->prepare("SELECT cs.id FROM ttirc_channels ch, ttirc_connections cs
  529. WHERE ch.connection_id = cs.id
  530. AND cs.id = ?
  531. AND LOWER(ch.channel) = LOWER(?)");
  532. $sth->execute([$dst_conn_id, $sender]);
  533. if (!$sth->fetch()) {
  534. $sth = $dbh->prepare("INSERT INTO ttirc_channels
  535. (channel, connection_id, chan_type) VALUES
  536. (?, ?, ?) RETURNING id");
  537. $sth->execute([$sender, $dst_conn_id, CT_PRIVATE]);
  538. if ($row = $sth->fetch()) {
  539. $dst_has_chan = $row['id'];
  540. }
  541. } else {
  542. $dst_has_chan = true;
  543. }
  544. if ($dst_has_chan) {
  545. push_message($dst_conn_id,
  546. $sender, $message, true, $message_type, $sender);
  547. }
  548. } else {
  549. push_message($connection_id, "---",
  550. "No such nick: $channel", true, MSGT_SYSTEM);
  551. }
  552. }
  553. }
  554. function handle_command($connection_id, $channel, $message) {
  555. $dbh = DB::get();
  556. list ($command, $arguments) = explode(" ", $message, 2);
  557. $command = mb_substr(trim(mb_strtolower($command)), 1);
  558. $arguments = trim($arguments);
  559. if ($command == "j") $command = "join";
  560. if ($command == "me") {
  561. $command = "action";
  562. if (is_instance($connection_id)) {
  563. relay_message($connection_id, $channel,
  564. $arguments, MSGT_ACTION);
  565. } else {
  566. push_message($connection_id, $channel,
  567. "$arguments", true, MSGT_ACTION);
  568. }
  569. }
  570. if ($command == "notice") {
  571. list ($nick, $message) = explode(" ", $arguments, 2);
  572. push_message($connection_id, $channel,
  573. "$message", false, MSGT_NOTICE, $nick);
  574. }
  575. switch ($command) {
  576. case "query":
  577. $dbh->beginTransaction();
  578. $sth = $dbh->prepare("SELECT id FROM ttirc_channels WHERE
  579. LOWER(channel) = LOWER(?) AND connection_id = ?");
  580. $sth->execute([strip_tags($arguments), $connection_id]);
  581. if (!$sth->fetch()) {
  582. $sth = $dbh->prepare("INSERT INTO ttirc_channels
  583. (channel, connection_id, chan_type) VALUES
  584. (?, ?, ?)");
  585. $sth->execute([$arguments, $connection_id, CT_PRIVATE]);
  586. }
  587. $dbh->commit();
  588. break;
  589. case "part":
  590. if (!$arguments) $arguments = $channel;
  591. $dbh->beginTransaction();
  592. $sth = $dbh->prepare("SELECT
  593. chan_type, title = '_ttirc_instance' AS is_instance
  594. FROM ttirc_channels ch, ttirc_connections cs WHERE
  595. ch.connection_id = cs.id AND
  596. LOWER(channel) = LOWER(?) AND
  597. connection_id = ?");
  598. $sth->execute([$arguments, $connection_id]);
  599. if ($row = $sth->fetch()) {
  600. $chan_type = $row['chan_type'];
  601. $is_instance = $row['is_instance'];
  602. if ($chan_type == CT_PRIVATE || $is_instance) {
  603. $sth = $dbh->prepare("DELETE FROM ttirc_channels WHERE
  604. LOWER(channel) = LOWER(?) AND connection_id = ?");
  605. $sth->execute([$arguments, $connection_id]);
  606. if ($is_instance) {
  607. relay_others($connection_id, $arguments,
  608. "PART:" . get_nick($connection_id) . ":",
  609. MSGT_EVENT, "---");
  610. $sth = $dbh->prepare("DELETE FROM ttirc_messages WHERE
  611. connection_id = ? AND LOWER(channel) = LOWER(?)");
  612. $sth->execute([$connection_id, $arguments]);
  613. }
  614. } else {
  615. push_message($connection_id, $channel,
  616. "$command:$arguments", false, MSGT_COMMAND);
  617. }
  618. }
  619. $dbh->commit();
  620. break;
  621. default:
  622. if (is_instance($connection_id)) {
  623. if (!get_nick($connection_id) && $command != "nick") {
  624. push_message($connection_id, "---",
  625. "Please set your nickname first (/nick ...)", true, MSGT_SYSTEM);
  626. return;
  627. }
  628. switch ($command) {
  629. case "msg":
  630. list($channel, $message) = explode(" ", $arguments, 2);
  631. if ($channel && $message) {
  632. relay_message($connection_id, strip_tags($channel),
  633. $message);
  634. }
  635. break;
  636. case "join":
  637. $dbh->beginTransaction();
  638. $arguments = strip_tags($arguments);
  639. if ($arguments[0] == "#") {
  640. $sth = $dbh->prepare("SELECT id FROM ttirc_channels WHERE
  641. LOWER(channel) = LOWER(?) AND connection_id = ?");
  642. $sth->execute([$arguments, $connection_id]);
  643. if (!$sth->fetch()) {
  644. $sth = $dbh->prepare("INSERT INTO ttirc_channels (channel, connection_id, chan_type) VALUES
  645. (?, ?, ?)");
  646. $res = $sth->execute([$arguments, $connection_id, CT_CHANNEL]);
  647. sync_channel_topic($connection_id, $arguments);
  648. $userhost = mb_substr($_SESSION['name'] . '@local', 0, 119);
  649. relay_message($connection_id, $arguments,
  650. "JOIN:" . get_nick($connection_id) . ":$userhost",
  651. MSGT_EVENT);
  652. #push_message($connection_id, CT_PRIVATE, "---", "---", MSGT_SYSTEM);
  653. }
  654. }
  655. $dbh->commit();
  656. break;
  657. case "nick":
  658. $new_nick = validate_nick($arguments);
  659. $old_nick = get_nick($connection_id);
  660. $dbh->beginTransaction();
  661. if ($new_nick && $new_nick != $old_nick) {
  662. $sth = $dbh->prepare("SELECT id FROM ttirc_connections
  663. WHERE title = '_ttirc_instance' AND LOWER(active_nick) = LOWER(?)");
  664. $sth->execute([$new_nick]);
  665. if (!$sth->fetch()) {
  666. $sth = $dbh->prepare("SELECT channel FROM ttirc_channels WHERE
  667. connection_id = ? AND chan_type = ?");
  668. $sth->execute([$connection_id, CT_CHANNEL]);
  669. while ($line = $sth->fetch()) {
  670. relay_message($connection_id, $line["channel"], "NICK:$new_nick",
  671. MSGT_EVENT);
  672. }
  673. $sth = $dbh->prepare("UPDATE ttirc_connections SET active_nick = ?
  674. WHERE id = ?");
  675. $sth->execute([$new_nick, $connection_id]);
  676. unset($connection_nick_cache[$connection_id]);
  677. $sth = $dbh->prepare("UPDATE ttirc_channels
  678. SET channel = ?
  679. WHERE id IN (SELECT ch.id
  680. FROM ttirc_channels ch, ttirc_connections cs
  681. WHERE ch.connection_id = cs.id
  682. AND cs.title = '_ttirc_instance'
  683. AND LOWER(channel) = LOWER(?))");
  684. $sth->execute([$new_nick, $old_nick]);
  685. } else {
  686. push_message($connection_id, "---",
  687. "Duplicate nick not allowed: $arguments", true, MSGT_SYSTEM);
  688. }
  689. }
  690. $dbh->commit();
  691. break;
  692. case "topic":
  693. if ($arguments) {
  694. $dbh->beginTransaction();
  695. relay_message($connection_id, $channel, "TOPIC:$arguments",
  696. MSGT_EVENT);
  697. $sth = $dbh->prepare("SELECT cs.id FROM ttirc_connections cs, ttirc_channels ch WHERE
  698. ch.connection_id = cs.id AND LOWER(channel) = LOWER(?) AND title = '_ttirc_instance'");
  699. $sth->execute([$channel]);
  700. $mynick = get_nick($connection_id);
  701. while ($line = $sth->fetch()) {
  702. $usth = $dbh->prepare("UPDATE ttirc_channels SET topic = ?,
  703. topic_owner = ?, topic_set = NOW()
  704. WHERE connection_id = ? AND LOWER(channel) = LOWER(?)");
  705. $usth->execute([$arguments, $mynick, $line['id'], $channel]);
  706. }
  707. $dbh->commit();
  708. } else {
  709. $sth = $dbh->prepare("SELECT topic, topic_owner, topic_set FROM ttirc_channels WHERE
  710. LOWER(channel) = LOWER(?) AND connection_id = ?");
  711. $sth->execute([$channel, $connection_id]);
  712. if ($row = $sth->fetch()) {
  713. $message = sprintf("Topic for %s is: %s", $channel, $row['topic']);
  714. push_message($connection_id, $channel, $message,
  715. true, MSGT_SYSTEM);
  716. $message = sprintf("Topic for %s set by %s at %s",
  717. $channel, $row['topic_owner'], mb_substr($row['topic_set'], 0, 16));
  718. push_message($connection_id, $channel, $message,
  719. true, MSGT_SYSTEM);
  720. }
  721. }
  722. break;
  723. case "action":
  724. break;
  725. default:
  726. push_message($connection_id, "---",
  727. "Command not understood: $command ($arguments)", true, MSGT_SYSTEM);
  728. }
  729. } else {
  730. push_message($connection_id, $channel,
  731. "$command:$arguments", false, MSGT_COMMAND);
  732. }
  733. break;
  734. }
  735. return $last_id;
  736. }
  737. function push_message($connection_id, $channel, $message,
  738. $incoming = false, $message_type = MSGT_PRIVMSG, $from_nick = false) {
  739. if ($message === "") return false;
  740. $dbh = DB::get();
  741. $incoming = bool_to_sql_bool($incoming);
  742. if ($channel != "---") {
  743. $my_nick = get_nick($connection_id);
  744. } else {
  745. $my_nick = "---";
  746. }
  747. if ($from_nick) $my_nick = $from_nick;
  748. if (is_instance($connection_id)) {
  749. $userhost = mb_substr($_SESSION['name'] . '@local', 0, 119);
  750. } else {
  751. $userhost = null;
  752. }
  753. $sth = $dbh->prepare("INSERT INTO ttirc_messages
  754. (incoming, connection_id, channel, sender, message, message_type, ts, userhost) VALUES
  755. (?, ?, ?, ?, ?, ?, NOW(), ?)");
  756. $sth->execute([$incoming, $connection_id, $channel, $my_nick, $message,
  757. $message_type, $userhost]);
  758. }
  759. function get_initial_last_id($offset = 0) {
  760. $dbh = DB::get();
  761. if (!$offset) $offset = DEFAULT_BUFFER_SIZE;
  762. $sth = $dbh->prepare("SELECT ttirc_messages.id
  763. FROM ttirc_messages, ttirc_connections
  764. WHERE
  765. owner_uid = ? AND
  766. connection_id = ttirc_connections.id AND
  767. channel != '---'
  768. ORDER BY id DESC LIMIT 1 OFFSET $offset");
  769. $sth->execute([$_SESSION['uid']]);
  770. if ($row = $sth->fetch()) {
  771. return $row['id'];
  772. } else {
  773. return 0;
  774. }
  775. }
  776. function num_new_lines($last_id, $bufsize) {
  777. $dbh = DB::get();
  778. if (!$last_id) $last_id = get_initial_last_id($bufsize);
  779. if (!$bufsize) $bufsize = DEFAULT_BUFFER_SIZE;
  780. $sth = $dbh->prepare("SELECT COUNT(*) AS cl
  781. FROM ttirc_messages, ttirc_connections WHERE
  782. connection_id = ttirc_connections.id AND
  783. message_type != ? AND
  784. ttirc_messages.id > ? AND
  785. owner_uid = ? LIMIT $bufsize");
  786. $sth->execute([MSGT_COMMAND, $last_id, $_SESSION['uid']]);
  787. if ($row = $sth->fetch()) {
  788. return $row['cl'];
  789. } else {
  790. return -1;
  791. }
  792. }
  793. function get_history_lines($connection_id, $channel) {
  794. $dbh = DB::get();
  795. $sth = $dbh->prepare("SELECT * FROM (SELECT ttirc_messages.id,
  796. message_type, sender, channel, connection_id, incoming,
  797. userhost, message, ".SUBSTRING_FOR_DATE."(ts,1,19) AS ts
  798. FROM ttirc_messages, ttirc_connections WHERE
  799. connection_id = ttirc_connections.id AND
  800. connection_id = ? AND
  801. channel = ? AND
  802. message_type != ? AND
  803. owner_uid = ? ORDER BY id DESC LIMIT ".DEFAULT_BUFFER_SIZE.") AS iq ORDER BY id");
  804. $sth->execute([$connection_id, $channel, MSGT_COMMAND, $_SESSION['uid']]);
  805. $lines = [];
  806. while ($line = $sth->fetch(PDO::FETCH_ASSOC)) {
  807. $line["sender_color"] = color_of($line["sender"]);
  808. $line["incoming"] = sql_bool_to_bool($line["incoming"]);
  809. array_push($lines, $line);
  810. }
  811. return $lines;
  812. }
  813. function get_new_lines($last_id, $bufsize = DEFAULT_BUFFER_SIZE) {
  814. if (!$last_id) $last_id = get_initial_last_id($bufsize);
  815. $dbh = DB::get();
  816. $sth = $dbh->prepare("SELECT ttirc_messages.id,
  817. message_type, sender, channel, connection_id, incoming,
  818. userhost, message, ".SUBSTRING_FOR_DATE."(ts,1,19) AS ts
  819. FROM ttirc_messages, ttirc_connections WHERE
  820. connection_id = ttirc_connections.id AND
  821. message_type != ? AND
  822. ttirc_messages.id > ? AND
  823. owner_uid = ? ORDER BY id LIMIT ?");
  824. $sth->execute([MSGT_COMMAND, $last_id, $_SESSION['uid'], $bufsize]);
  825. $lines = [];
  826. while ($line = $sth->fetch(PDO::FETCH_ASSOC)) {
  827. $line["sender_color"] = color_of($line["sender"]);
  828. $line["incoming"] = sql_bool_to_bool($line["incoming"]);
  829. array_push($lines, $line);
  830. }
  831. return $lines;
  832. }
  833. function get_chan_data() {
  834. $dbh = DB::get();
  835. $sth = $dbh->prepare("SELECT nicklist,channel,connection_id,
  836. chan_type,topic,ttirc_channels.muted,
  837. topic_owner,".SUBSTRING_FOR_DATE."(topic_set,1,16) AS topic_set
  838. FROM ttirc_channels, ttirc_connections
  839. WHERE connection_id = ttirc_connections.id AND
  840. visible = true AND
  841. owner_uid = ?");
  842. $sth->execute([$_SESSION['uid']]);
  843. $rv = [];
  844. while ($line = $sth->fetch(PDO::FETCH_ASSOC)) {
  845. $chan = $line["channel"];
  846. $re = array();
  847. $re["chan_type"] = $line["chan_type"];
  848. if (is_instance($line["connection_id"])) {
  849. $re["users"] = instance_nicklist($line["channel"]);
  850. } else {
  851. $re["users"] = sort_nicklist(json_decode($line["nicklist"]));
  852. }
  853. $re["muted"] = sql_bool_to_bool($line["muted"]);
  854. $re["topic"] = array(
  855. $line["topic"], $line["topic_owner"], $line["topic_set"]);
  856. $rv[$line["connection_id"]][$chan] = $re;
  857. }
  858. return $rv;
  859. }
  860. function get_conn_info() {
  861. $dbh = DB::get();
  862. $sth = $dbh->prepare("SELECT id,active_server,active_nick,status,muted,
  863. title,userhosts
  864. FROM ttirc_connections
  865. WHERE visible = true AND owner_uid = ?
  866. ORDER BY title != '_ttirc_instance', title");
  867. $sth->execute([$_SESSION['uid']]);
  868. $conn = array();
  869. while ($line = $sth->fetch(PDO::FETCH_ASSOC)) {
  870. $line["muted"] = sql_bool_to_bool($line["muted"]);
  871. if ($line["title"] == "_ttirc_instance") {
  872. $line["status"] = CS_CONNECTED;
  873. $line["title"] = __("Local");
  874. $line["active_server"] = __("<local>");
  875. $line["is_instance"] = true;
  876. $line['userhosts'] = instance_userhosts($line["id"]);
  877. } else {
  878. if ($line['userhosts']) $line['userhosts'] = json_decode($line['userhosts']);
  879. }
  880. array_push($conn, $line);
  881. }
  882. return $conn;
  883. }
  884. function sql_bool_to_string($s) {
  885. if ($s == "t" || $s == "1" || strtolower($s) == "true") {
  886. return "true";
  887. } else {
  888. return "false";
  889. }
  890. }
  891. function sql_bool_to_bool($s) {
  892. if ($s == "t" || $s == "1" || strtolower($s) == "true") {
  893. return true;
  894. } else {
  895. return false;
  896. }
  897. }
  898. function bool_to_sql_bool($s) {
  899. if ($s) {
  900. return "true";
  901. } else {
  902. return "false";
  903. }
  904. }
  905. function sanity_check() {
  906. global $ERRORS;
  907. $dbh = DB::get();
  908. $error_code = 0;
  909. $schema_version = get_schema_version();
  910. if ($schema_version != SCHEMA_VERSION) {
  911. $error_code = 5;
  912. }
  913. $row = $dbh->query("SELECT value FROM ttirc_system WHERE ".DB_KEY_FIELD." = 'MASTER_RUNNING'")->fetch();
  914. $master_running = $row['value'] == 'true'; # text field
  915. if (!$master_running) {
  916. $error_code = 13;
  917. }
  918. if ($error_code != 0) {
  919. print json_encode(array("error" => $error_code,
  920. "errormsg" => $ERRORS[$error_code]));
  921. return false;
  922. } else {
  923. return true;
  924. }
  925. }
  926. function get_random_server($connection_id) {
  927. $dbh = DB::get();
  928. $sth = $dbh->prepare("SELECT * FROM ttirc_servers WHERE
  929. connection_id = ? ORDER BY RANDOM() LIMIT 1");
  930. $sth->execute([$connection_id]);
  931. if ($row = $sth->fetch()) {
  932. return $row;
  933. } else {
  934. return false;
  935. }
  936. }
  937. // shamelessly stolen from xchat source code
  938. function color_of($name) {
  939. $rcolors = array( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
  940. 13, 14, 15 );
  941. $i = 0;
  942. $sum = 0;
  943. for ($i = 0; $i < strlen($name); $i++) {
  944. $sum += ord($name{$i});
  945. }
  946. $sum %= count($rcolors);
  947. return $rcolors[$sum];
  948. }
  949. function update_last_message() {
  950. $dbh = DB::get();
  951. if (time() - $_SESSION["message_last"] > 180) {
  952. $sth = $dbh->prepare("UPDATE ttirc_users SET last_message = NOW()
  953. WHERE id = ?");
  954. $sth->execute([$_SESSION['uid']]);
  955. $_SESSION["message_last"] = time();
  956. }
  957. }
  958. function update_heartbeat() {
  959. $dbh = DB::get();
  960. if (time() - $_SESSION["heartbeat_last"] > 180) {
  961. $sth = $dbh->prepare("SELECT id FROM
  962. ttirc_users WHERE id = ? AND heartbeat < NOW() - interval '15 min'");
  963. $sth->execute([$_SESSION['uid']]);
  964. if ($sth->fetch()) {
  965. $sth = $dbh->prepare("SELECT channel, connection_id, active_nick
  966. FROM ttirc_channels ch, ttirc_connections cs WHERE
  967. ch.connection_id = cs.id AND
  968. cs.owner_uid = ? AND
  969. title = '_ttirc_instance' AND
  970. chan_type = ?");
  971. $sth->execute([$_SESSION['uid'], CT_CHANNEL]);
  972. while ($line = $sth->fetch()) {
  973. relay_others($line['connection_id'], $line["channel"],
  974. "UNAWAY:" . $line['active_nick'],
  975. MSGT_EVENT);
  976. }
  977. }
  978. $sth = $dbh->prepare("UPDATE ttirc_users SET heartbeat = NOW()
  979. WHERE id = ?");
  980. $sth->execute([$_SESSION['uid']]);
  981. $_SESSION["heartbeat_last"] = time();
  982. }
  983. }
  984. function initialize_user_prefs($uid, $profile = false) {
  985. $dbh = DB::get();
  986. $dbh->beginTransaction();
  987. $sth = $dbh->query("SELECT pref_name,def_value FROM ttirc_prefs");
  988. $usth = $dbh->prepare("SELECT pref_name FROM ttirc_user_prefs WHERE owner_uid = ?");
  989. $usth->execute([$uid]);
  990. $active_prefs = array();
  991. while ($line = $usth->fetch()) {
  992. array_push($active_prefs, $line["pref_name"]);
  993. }
  994. while ($line = $sth->fetch()) {
  995. if (array_search($line["pref_name"], $active_prefs) === FALSE) {
  996. // print "adding " . $line["pref_name"] . "<br>";
  997. $isth = $dbh->prepare("INSERT INTO ttirc_user_prefs
  998. (owner_uid,pref_name,value, profile) VALUES
  999. (?, ?, ?, null)");
  1000. $isth->execute([$uid, $line['pref_name'], $line['def_value']]);
  1001. }
  1002. }
  1003. $sth = $dbh->prepare("SELECT id FROM ttirc_connections WHERE
  1004. title = '_ttirc_instance' AND owner_uid = ?");
  1005. $sth->execute([$uid]);
  1006. if (!$sth->fetch()) {
  1007. $sth = $dbh->prepare("INSERT INTO ttirc_connections (title, owner_uid)
  1008. VALUES ('_ttirc_instance', ?)");
  1009. $sth->execute([$uid]);
  1010. }
  1011. $dbh->commit();
  1012. }
  1013. function valid_connection($connection_id) {
  1014. $dbh = DB::get();
  1015. $sth = $dbh->prepare("SELECT id FROM ttirc_connections
  1016. WHERE id = ? AND owner_uid = ?");
  1017. $sth->execute([$connection_id, $_SESSION["uid"]]);
  1018. return $sth->fetch();
  1019. }
  1020. function make_password($length = 8) {
  1021. $password = "";
  1022. $possible = "0123456789abcdfghjkmnpqrstvwxyzABCDFGHJKMNPQRSTVWXYZ";
  1023. $i = 0;
  1024. while ($i < $length) {
  1025. $char = substr($possible, mt_rand(0, strlen($possible)-1), 1);
  1026. if (!strstr($password, $char)) {
  1027. $password .= $char;
  1028. $i++;
  1029. }
  1030. }
  1031. return $password;
  1032. }
  1033. function get_user_host($nick) {
  1034. $dbh = DB::get();
  1035. $sth = $dbh->prepare("SELECT login FROM ttirc_connections cs, ttirc_users u
  1036. WHERE cs.owner_uid = u.id AND LOWER(active_nick) = LOWER(?) LIMIT 1");
  1037. $sth->execute([$nick]);
  1038. if ($row = $sth->fetch()) {
  1039. return $row['nick'] . '@local';
  1040. } else {
  1041. return false;
  1042. }
  1043. }
  1044. function get_user_login($uid) {
  1045. $dbh = DB::get();
  1046. $sth = $dbh->prepare("SELECT login FROM ttirc_users WHERE id = ?");
  1047. $sth->execute([$uid]);
  1048. if ($row = $sth->fetch()) {
  1049. return $row['login'];
  1050. } else {
  1051. return false;
  1052. }
  1053. }
  1054. function get_iconv_encodings() {
  1055. return explode("\n", file_get_contents("lib/iconv.txt"));
  1056. }
  1057. function print_theme_select() {
  1058. $user_theme = get_pref("USER_THEME");
  1059. $themes = get_all_themes();
  1060. usort($themes, function($a,$b) {
  1061. return strcmp($a['name'], $b['name']);
  1062. });
  1063. print "<select class=\"form-control\" name=\"theme\">";
  1064. print "<option value=''>".__('Default')."</option>";
  1065. print "<option disabled>--------</option>";
  1066. foreach ($themes as $t) {
  1067. $base = $t['base'];
  1068. $name = $t['name'];
  1069. if ($base == $user_theme) {
  1070. $selected = "selected=\"1\"";
  1071. } else {
  1072. $selected = "";
  1073. }
  1074. print "<option $selected value='$base'>".htmlspecialchars($name)."</option>";
  1075. }
  1076. print "</select>";
  1077. }
  1078. function print_user_css() {
  1079. $user_css = get_pref("USER_STYLESHEET");
  1080. if ($user_css) {
  1081. print "<style type=\"text/css\">$user_css</style>";
  1082. }
  1083. }
  1084. function get_misc_params($uniqid = false) {
  1085. if (!$uniqid) $uniqid = uniqid();
  1086. $dbh = DB::get();
  1087. $notify_on = json_decode(get_pref("NOTIFY_ON"));
  1088. if (!is_array($notify_on)) $notify_on = array();
  1089. $notify_events = array();
  1090. foreach ($notify_on as $no) {
  1091. $notify_events[$no] = true;
  1092. }
  1093. $sth = $dbh->prepare("SELECT hide_join_part FROM ttirc_users WHERE id = ?");
  1094. $sth->execute([$_SESSION['uid']]);
  1095. $row = $sth->fetch();
  1096. $hide_join_part = $row["hide_join_part"];
  1097. $rv = array(
  1098. "hide_join_part" => $hide_join_part,
  1099. "uniqid" => $uniqid,
  1100. "fetch_url_titles" => defined('FETCH_URL_TITLES') && FETCH_URL_TITLES,
  1101. "disable_image_preview" => get_pref("DISABLE_IMAGE_PREVIEW"),
  1102. "highlight_on" => explode(",", get_pref("HIGHLIGHT_ON")),
  1103. "notify_events" => $notify_events);
  1104. return $rv;
  1105. }
  1106. function get_self_url_prefix() {
  1107. $url_path = "";
  1108. if ($_SERVER['HTTPS'] != "on") {
  1109. $url_path = "http://";
  1110. } else {
  1111. $url_path = "https://";
  1112. }
  1113. $url_path .= $_SERVER['HTTP_HOST'].dirname($_SERVER['PHP_SELF']);
  1114. return $url_path;
  1115. }
  1116. function truncate_string($str, $max_len) {
  1117. if (mb_strlen($str, "utf-8") > $max_len - 3) {
  1118. return trim(mb_substr($str, 0, $max_len, "utf-8")) . "...";
  1119. } else {
  1120. return $str;
  1121. }
  1122. }
  1123. function get_random_bytes($length) {
  1124. if (function_exists('openssl_random_pseudo_bytes')) {
  1125. return openssl_random_pseudo_bytes($length);
  1126. } else {
  1127. $output = "";
  1128. for ($i = 0; $i < $length; $i++)
  1129. $output .= chr(mt_rand(0, 255));
  1130. return $output;
  1131. }
  1132. }
  1133. function cleanup_session_cache() {
  1134. if (is_array($_SESSION["cache"])) {
  1135. foreach (array_keys($_SESSION["cache"]) as $uniqid) {
  1136. if (time() - $_SESSION["cache"][$uniqid]["last"] > 120) {
  1137. unset($_SESSION["cache"][$uniqid]);
  1138. }
  1139. }
  1140. }
  1141. }
  1142. function get_emoticons_flat() {
  1143. $map = get_emoticons_map();
  1144. $tmp = [];
  1145. foreach ($map as $sn => $sv) {
  1146. $tmp = array_merge($tmp, $sv);
  1147. }
  1148. return $tmp;
  1149. }
  1150. function get_emoticons_map() {
  1151. if (file_exists(EMOTICONS_MAP)) {
  1152. $obj = json_decode(file_get_contents(EMOTICONS_MAP), true);
  1153. if (!$obj) return [];
  1154. return $obj;
  1155. } else {
  1156. return [];
  1157. }
  1158. }
  1159. function render_emoticons_full() {
  1160. ?>
  1161. <!DOCTYPE html>
  1162. <html>
  1163. <head>
  1164. <title>Tiny Tiny IRC</title>
  1165. <?php echo stylesheet_tag("css/default.css") ?>
  1166. <?php stylesheet_tag("lib/bootstrap/css/bootstrap.min.css") ?>
  1167. <?php stylesheet_tag("lib/bootstrap/css/bootstrap-theme.min.css") ?>
  1168. <?php javascript_tag("js/functions.js") ?>
  1169. <?php javascript_tag("lib/bootstrap/js/jquery.js") ?>
  1170. <?php javascript_tag("lib/localforage.min.js") ?>
  1171. </head>
  1172. <body>
  1173. <script type="text/javascript">
  1174. var emoticons_map = {};
  1175. var emoticons_full = {};
  1176. function render_section(section, query) {
  1177. var map = emoticons_full[section];
  1178. var list = $(".em-list")
  1179. .html("");
  1180. if (query) map = emoticons_map;
  1181. var sections = Object.keys(map).sort();
  1182. $.each(sections, function(i, k) {
  1183. var v = map[k];
  1184. if (!query || k.match(query) || (v.hint && v.hint.match(query))) {
  1185. var img = $("<img>")
  1186. .attr('src', 'emoticons/' + v.file)
  1187. .attr('alt', k)
  1188. .attr('title', k + (v.hint ? '\n' + v.hint : ''))
  1189. .on('click', function() {
  1190. window.opener.inject_text(k);
  1191. });
  1192. var cell = $("<li>").attr('data-title', v.hint)
  1193. .append($("<div class='wrapper'>")
  1194. .append(img));
  1195. list.append(cell);
  1196. }
  1197. });
  1198. }
  1199. function init() {
  1200. $(".em-search").on('change', function() {
  1201. var q = $(this).val();
  1202. var s = $(".em-sections").val();
  1203. window.setTimeout(function() {
  1204. render_section(s, q);
  1205. }, 10);
  1206. });
  1207. $(".em-sections").on('change', function() {
  1208. $(".em-search").val('');
  1209. var s = $(this).val();
  1210. window.setTimeout(function() {
  1211. render_section(s, '');
  1212. }, 10);
  1213. });
  1214. $.each(Object.keys(emoticons_full).sort(), function (i, k) {
  1215. $(".em-sections").append(
  1216. $("<option>").html(k));
  1217. });
  1218. $(".em-sections").val('emoticons');
  1219. render_section('emoticons', '');
  1220. }
  1221. $(document).ready(function() {
  1222. $("#search").focus();
  1223. localforage.getItem("ttirc.emoticons-cache").then(function(obj) {
  1224. if (obj) {
  1225. emoticons_full = obj.fullmap;
  1226. emoticons_map = obj.map;
  1227. init();
  1228. } else {
  1229. update_emoticons();
  1230. }
  1231. });
  1232. });
  1233. </script>
  1234. <div class="panel panel-default">
  1235. <div class="panel-heading">
  1236. <form class="form form-inline pull-right" onsubmit="return false">
  1237. <input type="search" class="em-search form-control"
  1238. placeholder="<?php echo __("Search... ")?>">
  1239. </form>
  1240. <form class="form form-inline" onsubmit="return false">
  1241. <select class="em-sections form-control"></select>
  1242. </form>
  1243. </div> <!-- /heading -->
  1244. <ul class="panel-body emoticons-long em-list">
  1245. <?php
  1246. ksort($map[$section]);
  1247. foreach ($map[$section] as $k => $e) {
  1248. $title = $k . "\n" . $e['hint'];
  1249. print "<li data-title=\"$title\"><div class=\"wrapper\">";
  1250. print "<img onclick=\"window.opener.inject_text('$k')\" title=\"$title\" src=\"emoticons/{$e['file']}\">";
  1251. print "</div>$k";
  1252. print "</li>";
  1253. }
  1254. ?>
  1255. </ul>
  1256. </div> <!-- /body -->
  1257. </body>
  1258. </html>
  1259. <?php
  1260. }
  1261. function render_emoticons() {
  1262. $emoticons_map = get_emoticons_flat();
  1263. $dbh = DB::get();
  1264. $tmp = "";
  1265. $sth = $dbh->prepare("SELECT emoticon FROM ttirc_emoticons_popcon
  1266. WHERE owner_uid = ? ORDER BY times_used DESC LIMIT 30");
  1267. $sth->execute([$_SESSION['uid']]);
  1268. $num_favs = 0;
  1269. while ($line = $sth->fetch()) {
  1270. ++$num_favs;
  1271. $k = $line["emoticon"];
  1272. $v = $emoticons_map[$k];
  1273. $tmp .= "<div class=\"wrapper\"><img onclick=\"inject_text('$k')\" title=\"$k\" src=\"emoticons/{$v['file']}\"></div>";
  1274. }
  1275. $num_more = count($emoticons_map) - $num_favs;
  1276. if ($num_more > 0) {
  1277. $tmp .= "<br clear='both'><p style='text-align : center'>
  1278. <a href=\"#\" onclick=\"return emoticons_popup()\">$num_more more...</a></p>";
  1279. }
  1280. return $tmp;
  1281. }
  1282. function stylesheet_tag($filename) {
  1283. $timestamp = filemtime($filename);
  1284. echo "<link media=\"screen\" rel=\"stylesheet\" type=\"text/css\" href=\"$filename?$timestamp\"/>\n";
  1285. }
  1286. function javascript_tag($filename) {
  1287. $query = "";
  1288. if (!(strpos($filename, "?") === FALSE)) {
  1289. $query = substr($filename, strpos($filename, "?")+1);
  1290. $filename = substr($filename, 0, strpos($filename, "?"));
  1291. }
  1292. $timestamp = filemtime($filename);
  1293. if ($query) $timestamp .= "&$query";
  1294. echo "<script type=\"text/javascript\" charset=\"utf-8\" src=\"$filename?$timestamp\"></script>\n";
  1295. }
  1296. function get_minified_js($files) {
  1297. require_once 'lib/jshrink/Minifier.php';
  1298. $rv = '';
  1299. foreach ($files as $js) {
  1300. if (!isset($_GET['debug'])) {
  1301. $cached_file = "cache/js/$js.js";
  1302. if (file_exists($cached_file) &&
  1303. is_readable($cached_file) &&
  1304. filemtime($cached_file) >= filemtime("js/$js.js")) {
  1305. $rv .= file_get_contents($cached_file);
  1306. } else {
  1307. $minified = JShrink\Minifier::minify(file_get_contents("js/$js.js"));
  1308. file_put_contents($cached_file, $minified);
  1309. $rv .= $minified;
  1310. }
  1311. } else {
  1312. $rv .= file_get_contents("js/$js.js");
  1313. }
  1314. }
  1315. return $rv;
  1316. }
  1317. function init_js_translations() {
  1318. print 'var T_messages = new Object();
  1319. function __(msg) {
  1320. if (T_messages[msg]) {
  1321. return T_messages[msg];
  1322. } else {
  1323. return msg;
  1324. }
  1325. }
  1326. function ngettext(msg1, msg2, n) {
  1327. return (parseInt(n) > 1) ? msg2 : msg1;
  1328. }';
  1329. if (ENABLE_TRANSLATIONS == true) { // If translations are enabled.
  1330. $l10n = _get_reader();
  1331. for ($i = 0; $i < $l10n->total; $i++) {
  1332. $orig = $l10n->get_original_string($i);
  1333. $translation = __($orig);
  1334. print T_js_decl($orig, $translation);
  1335. }
  1336. }
  1337. }
  1338. function strip_prefix($nick) {
  1339. return preg_replace("/^[\+\@]/", "", $nick);
  1340. }
  1341. function nicklist_sort_callback($o1, $o2) {
  1342. $c1 = $o1[0];
  1343. $c2 = $o2[0];
  1344. if ($c1 == '@' && $c2 != '@') {
  1345. return -1;
  1346. }
  1347. if ($c1 != '@' && $c2 == '@') {
  1348. return 1;
  1349. }
  1350. if ($c1 == '+' && $c2 != '+') {
  1351. return -1;
  1352. }
  1353. if ($c1 != '+' && $c2 == '+') {
  1354. return 1;
  1355. }
  1356. return strip_prefix($o1) < strip_prefix($o2) ? -1 : 1;
  1357. }
  1358. function sort_nicklist($nicklist) {
  1359. if (is_array($nicklist)) usort($nicklist, "nicklist_sort_callback");
  1360. return $nicklist;
  1361. }
  1362. function build_url($parts) {
  1363. return $parts['scheme'] . "://" . $parts['host'] . $parts['path'];
  1364. }
  1365. /**
  1366. * Converts a (possibly) relative URL to a absolute one.
  1367. *
  1368. * @param string $url Base URL (i.e. from where the document is)
  1369. * @param string $rel_url Possibly relative URL in the document
  1370. *
  1371. * @return string Absolute URL
  1372. */
  1373. function rewrite_relative_url($url, $rel_url) {
  1374. if (strpos($rel_url, "://") !== false) {
  1375. return $rel_url;
  1376. } else if (strpos($rel_url, "//") === 0) {
  1377. # protocol-relative URL (rare but they exist)
  1378. return $rel_url;
  1379. } else if (preg_match("/^[a-z]+:/i", $rel_url)) {
  1380. # magnet:, feed:, etc
  1381. return $rel_url;
  1382. } else if (strpos($rel_url, "/") === 0) {
  1383. $parts = parse_url($url);
  1384. $parts['path'] = $rel_url;
  1385. return build_url($parts);
  1386. } else {
  1387. $parts = parse_url($url);
  1388. if (!isset($parts['path'])) {
  1389. $parts['path'] = '/';
  1390. }
  1391. $dir = $parts['path'];
  1392. if (substr($dir, -1) !== '/') {
  1393. $dir = dirname($parts['path']);
  1394. $dir !== '/' && $dir .= '/';
  1395. }
  1396. $parts['path'] = $dir . $rel_url;
  1397. return build_url($parts);
  1398. }
  1399. }
  1400. function shorten_urls($line) {
  1401. if (!function_exists("shorten_url")) return $line;
  1402. $urls = null;
  1403. if (preg_match_all(URL_REGEXP, $line, $urls) > 0) {
  1404. foreach ($urls as $url) {
  1405. if (mb_strlen($url[0]) > 130) {
  1406. $shorturl = shorten_url($url[0]);
  1407. if ($shorturl != $url[0] && $shorturl) {
  1408. $line = str_replace($url[0], $shorturl, $line);
  1409. }
  1410. }
  1411. }
  1412. }
  1413. return $line;
  1414. }
  1415. function arr_qmarks($arr) {
  1416. return str_repeat('?,', count($arr) - 1) . '?';
  1417. }
  1418. function make_thumbnail($data, $dim_max_x = 600, $dim_max_y = 600) {
  1419. $o_im = @imagecreatefromstring($data);
  1420. if ($o_im) {
  1421. $imageinfo = @getimagesizefromstring($data);
  1422. $o_width = imagesx($o_im) ;
  1423. $o_height = imagesy($o_im) ;
  1424. if (max($o_width, $o_height) < max($dim_max_x, $dim_max_y)) {
  1425. $t_height = $o_height;
  1426. $t_width = $o_width;
  1427. } else {
  1428. if ($o_height > $o_width) {
  1429. $t_height = $dim_max_x;
  1430. $t_width = round($o_width/$o_height * $t_height);
  1431. } else {
  1432. $t_width = $dim_max_y;
  1433. $t_height = round($o_height/$o_width * $t_width);
  1434. }
  1435. }
  1436. // print "$o_file : $t_file : $o_height * $o_width -> $t_height * $t_width<br>";
  1437. $t_im = imageCreateTrueColor($t_width, $t_height);
  1438. imageFill($t_im, 0, 0, 0xffffff);
  1439. imageCopyResampled($t_im, $o_im, 0, 0, 0, 0,
  1440. $t_width, $t_height, $o_width, $o_height);
  1441. $exif = @exif_read_data($o_file);
  1442. if ($exif) {
  1443. $ori = @$exif['IFD0']['Orientation'];
  1444. if (!$ori) $ori = @$exif['Orientation'];
  1445. if ($ori) {
  1446. switch ($ori) {
  1447. case 3:
  1448. $angle = -180;
  1449. break;
  1450. case 6:
  1451. $angle = -90;
  1452. break;
  1453. case 8:
  1454. $angle = 90;
  1455. break;
  1456. default:
  1457. $angle = 0;
  1458. }
  1459. if ($angle != 0) $t_im = imagerotate($t_im, $angle, 0xffffff);
  1460. }
  1461. }
  1462. if ($imageinfo && $imageinfo["mime"] == "image/gif") {
  1463. $stamp = imagecreatefrompng('images/play-outline.png');
  1464. if ($stamp) {
  1465. $sx = imagesx($stamp);
  1466. $sy = imagesy($stamp);
  1467. imagecopy($t_im, $stamp,
  1468. imagesx($t_im)/2 - $sx/2,
  1469. imagesy($t_im)/2 - $sy/2,
  1470. 0, 0,
  1471. imagesx($stamp), imagesy($stamp));
  1472. }
  1473. }
  1474. /* Dirty, dirty hack */
  1475. $t_width = imagesx($t_im) ;
  1476. $t_height = imagesy($t_im) ;
  1477. $tmp = tempnam(sys_get_temp_dir(), 'ggx');
  1478. if ($tmp) {
  1479. imageJpeg($t_im, $tmp, 85);
  1480. $thumb_binary = file_get_contents($tmp);
  1481. unlink($tmp);
  1482. } else {
  1483. $thumb_binary = '';
  1484. }
  1485. imageDestroy($o_im);
  1486. imageDestroy($t_im);
  1487. //return array($thumb_binary, $o_width, $o_height, $t_width, $t_height);
  1488. return $thumb_binary;
  1489. }
  1490. return false;
  1491. }
  1492. ?>