The version of vichan running on lainchan.org
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

12 роки тому
10 роки тому
12 роки тому
12 роки тому
13 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
13 роки тому
12 роки тому
13 роки тому
12 роки тому
12 роки тому
12 роки тому
10 роки тому
12 роки тому
12 роки тому
12 роки тому
13 роки тому
12 роки тому
12 роки тому
13 роки тому
10 роки тому
10 роки тому
12 роки тому
13 роки тому
10 роки тому
12 роки тому
13 роки тому
12 роки тому
12 роки тому
12 роки тому
13 роки тому
12 роки тому
12 роки тому
13 роки тому
12 роки тому
13 роки тому
12 роки тому
12 роки тому
10 роки тому
12 роки тому
10 роки тому
12 роки тому
12 роки тому
12 роки тому
13 роки тому
12 роки тому
13 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
10 роки тому
10 роки тому
12 роки тому
10 роки тому
10 роки тому
10 роки тому
10 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
10 роки тому
12 роки тому
12 роки тому
12 роки тому
10 роки тому
12 роки тому
12 роки тому
9 роки тому
12 роки тому
10 роки тому
12 роки тому
12 роки тому
13 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
13 роки тому
13 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
10 роки тому
9 роки тому
10 роки тому
10 роки тому
12 роки тому
12 роки тому
12 роки тому
13 роки тому
12 роки тому
12 роки тому
12 роки тому
10 роки тому
10 роки тому
12 роки тому
10 роки тому
12 роки тому
12 роки тому
13 роки тому
12 роки тому
12 роки тому
12 роки тому
10 роки тому
12 роки тому
12 роки тому
10 роки тому
10 роки тому
12 роки тому
12 роки тому
10 роки тому
10 роки тому
12 роки тому
10 роки тому
10 роки тому
10 роки тому
11 роки тому
12 роки тому
12 роки тому
10 роки тому
10 роки тому
10 роки тому
9 роки тому
9 роки тому
10 роки тому
10 роки тому
12 роки тому
10 роки тому
12 роки тому
12 роки тому
8 роки тому
10 роки тому
9 роки тому
10 роки тому
10 роки тому
10 роки тому
10 роки тому
10 роки тому
8 роки тому
12 роки тому
12 роки тому
12 роки тому
10 роки тому
12 роки тому
10 роки тому
10 роки тому
10 роки тому
10 роки тому
10 роки тому
10 роки тому
10 роки тому
10 роки тому
10 роки тому
12 роки тому
10 роки тому
12 роки тому
12 роки тому
10 роки тому
12 роки тому
12 роки тому
10 роки тому
12 роки тому
10 роки тому
12 роки тому
12 роки тому
10 роки тому
12 роки тому
10 роки тому
12 роки тому
10 роки тому
12 роки тому
10 роки тому
12 роки тому
10 роки тому
12 роки тому
10 роки тому
12 роки тому
12 роки тому
10 роки тому
10 роки тому
10 роки тому
12 роки тому
13 роки тому
12 роки тому
10 роки тому
10 роки тому
13 роки тому
8 роки тому
10 роки тому
10 роки тому
10 роки тому
10 роки тому
10 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
13 роки тому
12 роки тому
13 роки тому
12 роки тому
12 роки тому
10 роки тому
12 роки тому
10 роки тому
10 роки тому
12 роки тому
10 роки тому
12 роки тому
12 роки тому
9 роки тому
12 роки тому
9 роки тому
12 роки тому
12 роки тому
12 роки тому
10 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
10 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
10 роки тому
10 роки тому
10 роки тому
12 роки тому
12 роки тому
12 роки тому
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424
  1. <?php
  2. /*
  3. * Copyright (c) 2010-2014 Tinyboard Development Group
  4. */
  5. require_once 'inc/functions.php';
  6. require_once 'inc/anti-bot.php';
  7. require_once 'inc/bans.php';
  8. // Fix for magic quotes
  9. if (get_magic_quotes_gpc()) {
  10. function strip_array($var) {
  11. return is_array($var) ? array_map('strip_array', $var) : stripslashes($var);
  12. }
  13. $_GET = strip_array($_GET);
  14. $_POST = strip_array($_POST);
  15. }
  16. if ((!isset($_POST['mod']) || !$_POST['mod'])
  17. && ($config['board_locked']===true
  18. || (is_array($config['board_locked']) && in_array(strtolower($_POST['board']), $config['board_locked'])))){
  19. error("Board is locked");
  20. }
  21. $dropped_post = false;
  22. // Is it a post coming from NNTP? Let's extract it and pretend it's a normal post.
  23. if (isset($_GET['Newsgroups']) && $config['nntpchan']['enabled']) {
  24. if ($_SERVER['REMOTE_ADDR'] != $config['nntpchan']['trusted_peer']) {
  25. error("NNTPChan: Forbidden. $_SERVER[REMOTE_ADDR] is not a trusted peer");
  26. }
  27. $_POST = array();
  28. $_POST['json_response'] = true;
  29. $headers = json_encode($_GET);
  30. if (!isset ($_GET['Message-Id'])) {
  31. if (!isset ($_GET['Message-ID'])) {
  32. error("NNTPChan: No message ID");
  33. }
  34. else $msgid = $_GET['Message-ID'];
  35. }
  36. else $msgid = $_GET['Message-Id'];
  37. $groups = preg_split("/,\s*/", $_GET['Newsgroups']);
  38. if (count($groups) != 1) {
  39. error("NNTPChan: Messages can go to only one newsgroup");
  40. }
  41. $group = $groups[0];
  42. if (!isset($config['nntpchan']['dispatch'][$group])) {
  43. error("NNTPChan: We don't synchronize $group");
  44. }
  45. $xboard = $config['nntpchan']['dispatch'][$group];
  46. $ref = null;
  47. if (isset ($_GET['References'])) {
  48. $refs = preg_split("/,\s*/", $_GET['References']);
  49. if (count($refs) > 1) {
  50. error("NNTPChan: We don't support multiple references");
  51. }
  52. $ref = $refs[0];
  53. $query = prepare("SELECT `board`,`id` FROM ``nntp_references`` WHERE `message_id` = :ref");
  54. $query->bindValue(':ref', $ref);
  55. $query->execute() or error(db_error($query));
  56. $ary = $query->fetchAll(PDO::FETCH_ASSOC);
  57. if (count($ary) == 0) {
  58. error("NNTPChan: We don't have $ref that $msgid references");
  59. }
  60. $p_id = $ary[0]['id'];
  61. $p_board = $ary[0]['board'];
  62. if ($p_board != $xboard) {
  63. error("NNTPChan: Cross board references not allowed. Tried to reference $p_board on $xboard");
  64. }
  65. $_POST['thread'] = $p_id;
  66. }
  67. $date = isset($_GET['Date']) ? strtotime($_GET['Date']) : time();
  68. list($ct) = explode('; ', $_GET['Content-Type']);
  69. $query = prepare("SELECT COUNT(*) AS `c` FROM ``nntp_references`` WHERE `message_id` = :msgid");
  70. $query->bindValue(":msgid", $msgid);
  71. $query->execute() or error(db_error($query));
  72. $a = $query->fetch(PDO::FETCH_ASSOC);
  73. if ($a['c'] > 0) {
  74. error("NNTPChan: We already have this post. Post discarded.");
  75. }
  76. if ($ct == 'text/plain') {
  77. $content = file_get_contents("php://input");
  78. }
  79. elseif ($ct == 'multipart/mixed' || $ct == 'multipart/form-data') {
  80. _syslog(LOG_INFO, "MM: Files: ".print_r($GLOBALS, true)); // Debug
  81. $content = '';
  82. $newfiles = array();
  83. foreach ($_FILES['attachment']['error'] as $id => $error) {
  84. if ($_FILES['attachment']['type'][$id] == 'text/plain') {
  85. $content .= file_get_contents($_FILES['attachment']['tmp_name'][$id]);
  86. }
  87. elseif ($_FILES['attachment']['type'][$id] == 'message/rfc822') { // Signed message, ignore for now
  88. }
  89. else { // A real attachment :^)
  90. $file = array();
  91. $file['name'] = $_FILES['attachment']['name'][$id];
  92. $file['type'] = $_FILES['attachment']['type'][$id];
  93. $file['size'] = $_FILES['attachment']['size'][$id];
  94. $file['tmp_name'] = $_FILES['attachment']['tmp_name'][$id];
  95. $file['error'] = $_FILES['attachment']['error'][$id];
  96. $newfiles["file$id"] = $file;
  97. }
  98. }
  99. $_FILES = $newfiles;
  100. }
  101. else {
  102. error("NNTPChan: Wrong mime type: $ct");
  103. }
  104. $_POST['subject'] = isset($_GET['Subject']) ? ($_GET['Subject'] == 'None' ? '' : $_GET['Subject']) : '';
  105. $_POST['board'] = $xboard;
  106. if (isset ($_GET['From'])) {
  107. list($name, $mail) = explode(" <", $_GET['From'], 2);
  108. $mail = preg_replace('/>\s+$/', '', $mail);
  109. $_POST['name'] = $name;
  110. //$_POST['email'] = $mail;
  111. $_POST['email'] = '';
  112. }
  113. if (isset ($_GET['X_Sage'])) {
  114. $_POST['email'] = 'sage';
  115. }
  116. $content = preg_replace_callback('/>>([0-9a-fA-F]{6,})/', function($id) use ($xboard) {
  117. $id = $id[1];
  118. $query = prepare("SELECT `board`,`id` FROM ``nntp_references`` WHERE `message_id_digest` LIKE :rule");
  119. $idx = $id . "%";
  120. $query->bindValue(':rule', $idx);
  121. $query->execute() or error(db_error($query));
  122. $ary = $query->fetchAll(PDO::FETCH_ASSOC);
  123. if (count($ary) == 0) {
  124. return ">>>>$id";
  125. }
  126. else {
  127. $ret = array();
  128. foreach ($ary as $v) {
  129. if ($v['board'] != $xboard) {
  130. $ret[] = ">>>/".$v['board']."/".$v['id'];
  131. }
  132. else {
  133. $ret[] = ">>".$v['id'];
  134. }
  135. }
  136. return implode($ret, ", ");
  137. }
  138. }, $content);
  139. $_POST['body'] = $content;
  140. $dropped_post = array(
  141. 'date' => $date,
  142. 'board' => $xboard,
  143. 'msgid' => $msgid,
  144. 'headers' => $headers,
  145. 'from_nntp' => true,
  146. );
  147. }
  148. elseif (isset($_GET['Newsgroups'])) {
  149. error("NNTPChan: NNTPChan support is disabled");
  150. }
  151. if (isset($_POST['delete'])) {
  152. // Delete
  153. if (!isset($_POST['board'], $_POST['password']))
  154. error($config['error']['bot']);
  155. $password = &$_POST['password'];
  156. if ($password == '')
  157. error($config['error']['invalidpassword']);
  158. $delete = array();
  159. foreach ($_POST as $post => $value) {
  160. if (preg_match('/^delete_(\d+)$/', $post, $m)) {
  161. $delete[] = (int)$m[1];
  162. }
  163. }
  164. checkDNSBL();
  165. // Check if board exists
  166. if (!openBoard($_POST['board']))
  167. error($config['error']['noboard']);
  168. // Check if banned
  169. checkBan($board['uri']);
  170. // Check if deletion enabled
  171. if (!$config['allow_delete'])
  172. error(_('Post deletion is not allowed!'));
  173. if (empty($delete))
  174. error($config['error']['nodelete']);
  175. foreach ($delete as &$id) {
  176. $query = prepare(sprintf("SELECT `thread`, `time`,`password` FROM ``posts_%s`` WHERE `id` = :id", $board['uri']));
  177. $query->bindValue(':id', $id, PDO::PARAM_INT);
  178. $query->execute() or error(db_error($query));
  179. if ($post = $query->fetch(PDO::FETCH_ASSOC)) {
  180. $thread = false;
  181. if ($config['user_moderation'] && $post['thread']) {
  182. $thread_query = prepare(sprintf("SELECT `time`,`password` FROM ``posts_%s`` WHERE `id` = :id", $board['uri']));
  183. $thread_query->bindValue(':id', $post['thread'], PDO::PARAM_INT);
  184. $thread_query->execute() or error(db_error($query));
  185. $thread = $thread_query->fetch(PDO::FETCH_ASSOC);
  186. }
  187. if ($password != '' && $post['password'] != $password && (!$thread || $thread['password'] != $password))
  188. error($config['error']['invalidpassword']);
  189. if ($post['time'] > time() - $config['delete_time'] && (!$thread || $thread['password'] != $password)) {
  190. error(sprintf($config['error']['delete_too_soon'], until($post['time'] + $config['delete_time'])));
  191. }
  192. if (isset($_POST['file'])) {
  193. // Delete just the file
  194. deleteFile($id);
  195. modLog("User deleted file from his own post #$id");
  196. } else {
  197. // Delete entire post
  198. deletePost($id);
  199. modLog("User deleted his own post #$id");
  200. }
  201. _syslog(LOG_INFO, 'Deleted post: ' .
  202. '/' . $board['dir'] . $config['dir']['res'] . sprintf($config['file_page'], $post['thread'] ? $post['thread'] : $id) . ($post['thread'] ? '#' . $id : '')
  203. );
  204. }
  205. }
  206. buildIndex();
  207. $is_mod = isset($_POST['mod']) && $_POST['mod'];
  208. $root = $is_mod ? $config['root'] . $config['file_mod'] . '?/' : $config['root'];
  209. if (!isset($_POST['json_response'])) {
  210. header('Location: ' . $root . $board['dir'] . $config['file_index'], true, $config['redirect_http']);
  211. } else {
  212. header('Content-Type: text/json');
  213. echo json_encode(array('success' => true));
  214. }
  215. // We are already done, let's continue our heavy-lifting work in the background (if we run off FastCGI)
  216. if (function_exists('fastcgi_finish_request'))
  217. @fastcgi_finish_request();
  218. rebuildThemes('post-delete', $board['uri']);
  219. } elseif (isset($_POST['report'])) {
  220. if (!isset($_POST['board'], $_POST['reason']))
  221. error($config['error']['bot']);
  222. $report = array();
  223. foreach ($_POST as $post => $value) {
  224. if (preg_match('/^delete_(\d+)$/', $post, $m)) {
  225. $report[] = (int)$m[1];
  226. }
  227. }
  228. checkDNSBL();
  229. // Check if board exists
  230. if (!openBoard($_POST['board']))
  231. error($config['error']['noboard']);
  232. // Check if banned
  233. checkBan($board['uri']);
  234. if (empty($report))
  235. error($config['error']['noreport']);
  236. if (count($report) > $config['report_limit'])
  237. error($config['error']['toomanyreports']);
  238. if ($config['report_captcha'] && !isset($_POST['captcha_text'], $_POST['captcha_cookie'])) {
  239. error($config['error']['bot']);
  240. }
  241. if ($config['report_captcha']) {
  242. $resp = file_get_contents($config['captcha']['provider_check'] . "?" . http_build_query([
  243. 'mode' => 'check',
  244. 'text' => $_POST['captcha_text'],
  245. 'extra' => $config['captcha']['extra'],
  246. 'cookie' => $_POST['captcha_cookie']
  247. ]));
  248. if ($resp !== '1') {
  249. error($config['error']['captcha']);
  250. }
  251. }
  252. $reason = escape_markup_modifiers($_POST['reason']);
  253. markup($reason);
  254. foreach ($report as &$id) {
  255. $query = prepare(sprintf("SELECT `id`,`thread` , `body_nomarkup` FROM ``posts_%s`` WHERE `id` = :id", $board['uri']));
  256. $query->bindValue(':id', $id, PDO::PARAM_INT);
  257. $query->execute() or error(db_error($query));
  258. $thread = $query->fetch(PDO::FETCH_ASSOC);
  259. if ($config['syslog'])
  260. _syslog(LOG_INFO, 'Reported post: ' .
  261. '/' . $board['dir'] . $config['dir']['res'] . link_for($thread) . ($thread['thread'] ? '#' . $id : '') .
  262. ' for "' . $reason . '"'
  263. );
  264. $query = prepare("INSERT INTO ``reports`` VALUES (NULL, :time, :ip, :board, :post, :reason)");
  265. $query->bindValue(':time', time(), PDO::PARAM_INT);
  266. $query->bindValue(':ip', $_SERVER['REMOTE_ADDR'], PDO::PARAM_STR);
  267. $query->bindValue(':board', $board['uri'], PDO::PARAM_INT);
  268. $query->bindValue(':post', $id, PDO::PARAM_INT);
  269. $query->bindValue(':reason', $reason, PDO::PARAM_STR);
  270. $query->execute() or error(db_error($query));
  271. if ($config['slack'])
  272. {
  273. function slack($message, $room = "reports", $icon = ":no_entry_sign:")
  274. {
  275. $room = ($room) ? $room : "reports";
  276. $data = "payload=" . json_encode(array(
  277. "channel" => "#{$room}",
  278. "text" => urlencode($message),
  279. "icon_emoji" => $icon
  280. ));
  281. // You can get your webhook endpoint from your Slack settings
  282. // For some reason using the configuration key doesn't work
  283. $ch = curl_init($config['slack_incoming_webhook_endpoint']);
  284. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  285. curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
  286. curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
  287. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  288. $result = curl_exec($ch);
  289. curl_close($ch);
  290. return $result;
  291. }
  292. $postcontent = mb_substr($thread['body_nomarkup'], 0, 120) . '... _*(POST TRIMMED)*_';
  293. $slackmessage = '<' .$config['domain'] . "/mod.php?/" . $board['dir'] . $config['dir']['res'] . ( $thread['thread'] ? $thread['thread'] : $id ) . ".html" . ($thread['thread'] ? '#' . $id : '') . '> \n ' . $reason . '\n ' . $postcontent . '\n';
  294. $slackresult = slack($slackmessage, $config['slack_channel']);
  295. }
  296. }
  297. $is_mod = isset($_POST['mod']) && $_POST['mod'];
  298. $root = $is_mod ? $config['root'] . $config['file_mod'] . '?/' : $config['root'];
  299. if (!isset($_POST['json_response'])) {
  300. $index = $root . $board['dir'] . $config['file_index'];
  301. $reported_post = $root . $board['dir'] . $config['dir']['res'] . ( $thread['thread'] ? $thread['thread'] : $id ) . ".html" . ($thread['thread'] ? '#' . $id : '') ;
  302. header('Location: ' . $reported_post);
  303. //echo Element('page.html', array('config' => $config, 'body' => '<div style="text-align:center"><a href="javascript:window.close()">[ ' . _('Close window') ." ]</a> <a href='$index'>[ " . _('Return') . ' ]</a></div>', 'title' => _('Report submitted!')));
  304. } else {
  305. header('Content-Type: text/json');
  306. echo json_encode(array('success' => true));
  307. }
  308. } elseif (isset($_POST['post']) || $dropped_post) {
  309. if (!isset($_POST['body'], $_POST['board']) && !$dropped_post)
  310. error($config['error']['bot']);
  311. $post = array('board' => $_POST['board'], 'files' => array());
  312. // Check if board exists
  313. if (!openBoard($post['board']))
  314. error($config['error']['noboard']);
  315. if (!isset($_POST['name']))
  316. $_POST['name'] = $config['anonymous'];
  317. if (!isset($_POST['email']))
  318. $_POST['email'] = '';
  319. if (!isset($_POST['subject']))
  320. $_POST['subject'] = '';
  321. if (!isset($_POST['password']))
  322. $_POST['password'] = '';
  323. if (isset($_POST['thread'])) {
  324. $post['op'] = false;
  325. $post['thread'] = round($_POST['thread']);
  326. } else
  327. $post['op'] = true;
  328. if (!$dropped_post) {
  329. // Check for CAPTCHA right after opening the board so the "return" link is in there
  330. if ($config['recaptcha']) {
  331. if (!isset($_POST['recaptcha_challenge_field']) || !isset($_POST['recaptcha_response_field']))
  332. error($config['error']['bot']);
  333. // Check what reCAPTCHA has to say...
  334. $resp = recaptcha_check_answer($config['recaptcha_private'],
  335. $_SERVER['REMOTE_ADDR'],
  336. $_POST['recaptcha_challenge_field'],
  337. $_POST['recaptcha_response_field']);
  338. if (!$resp->is_valid) {
  339. error($config['error']['captcha']);
  340. }
  341. }
  342. if (!(($post['op'] && $_POST['post'] == $config['button_newtopic']) ||
  343. (!$post['op'] && $_POST['post'] == $config['button_reply'])))
  344. error($config['error']['bot']);
  345. // Check the referrer
  346. if ($config['referer_match'] !== false &&
  347. (!isset($_SERVER['HTTP_REFERER']) || !preg_match($config['referer_match'], rawurldecode($_SERVER['HTTP_REFERER']))))
  348. error($config['error']['referer']);
  349. checkDNSBL();
  350. // Check if banned
  351. checkBan($board['uri']);
  352. if ($post['mod'] = isset($_POST['mod']) && $_POST['mod']) {
  353. check_login(false);
  354. if (!$mod) {
  355. // Liar. You're not a mod.
  356. error($config['error']['notamod']);
  357. }
  358. $post['sticky'] = $post['op'] && isset($_POST['sticky']);
  359. $post['locked'] = $post['op'] && isset($_POST['lock']);
  360. $post['raw'] = isset($_POST['raw']);
  361. if ($post['sticky'] && !hasPermission($config['mod']['sticky'], $board['uri']))
  362. error($config['error']['noaccess']);
  363. if ($post['locked'] && !hasPermission($config['mod']['lock'], $board['uri']))
  364. error($config['error']['noaccess']);
  365. if ($post['raw'] && !hasPermission($config['mod']['rawhtml'], $board['uri']))
  366. error($config['error']['noaccess']);
  367. }
  368. if (!$post['mod']) {
  369. $post['antispam_hash'] = checkSpam(array($board['uri'], isset($post['thread']) ? $post['thread'] : ($config['try_smarter'] && isset($_POST['page']) ? 0 - (int)$_POST['page'] : null)));
  370. if ($post['antispam_hash'] === true)
  371. error($config['error']['spam']);
  372. }
  373. if ($config['robot_enable'] && $config['robot_mute']) {
  374. checkMute();
  375. }
  376. }
  377. else {
  378. $mod = $post['mod'] = false;
  379. }
  380. //Check if thread exists
  381. if (!$post['op']) {
  382. $query = prepare(sprintf("SELECT `sticky`,`locked`,`cycle`,`sage`,`slug` FROM ``posts_%s`` WHERE `id` = :id AND `thread` IS NULL LIMIT 1", $board['uri']));
  383. $query->bindValue(':id', $post['thread'], PDO::PARAM_INT);
  384. $query->execute() or error(db_error());
  385. if (!$thread = $query->fetch(PDO::FETCH_ASSOC)) {
  386. // Non-existant
  387. error($config['error']['nonexistant']);
  388. }
  389. }
  390. else {
  391. $thread = false;
  392. }
  393. // Check for an embed field
  394. if ($config['enable_embedding'] && isset($_POST['embed']) && !empty($_POST['embed'])) {
  395. // yep; validate it
  396. $value = $_POST['embed'];
  397. foreach ($config['embedding'] as &$embed) {
  398. if (preg_match($embed[0], $value)) {
  399. // Valid link
  400. $post['embed'] = $value;
  401. // This is bad, lol.
  402. $post['no_longer_require_an_image_for_op'] = true;
  403. break;
  404. }
  405. }
  406. if (!isset($post['embed'])) {
  407. error($config['error']['invalid_embed']);
  408. }
  409. }
  410. if (!hasPermission($config['mod']['bypass_field_disable'], $board['uri'])) {
  411. if ($config['field_disable_name'])
  412. $_POST['name'] = $config['anonymous']; // "forced anonymous"
  413. if ($config['field_disable_email'])
  414. $_POST['email'] = '';
  415. if ($config['field_disable_password'])
  416. $_POST['password'] = '';
  417. if ($config['field_disable_subject'] || (!$post['op'] && $config['field_disable_reply_subject']))
  418. $_POST['subject'] = '';
  419. }
  420. if ($config['allow_upload_by_url'] && isset($_POST['file_url1']) && !empty($_POST['file_url1'])) {
  421. function unlink_tmp_file($file) {
  422. @unlink($file);
  423. fatal_error_handler();
  424. }
  425. function upload_by_url($config,$post,$url) {
  426. $post['file_url'] = $url;
  427. if (!preg_match('@^https?://@', $post['file_url']))
  428. error($config['error']['invalidimg']);
  429. if (mb_strpos($post['file_url'], '?') !== false)
  430. $url_without_params = mb_substr($post['file_url'], 0, mb_strpos($post['file_url'], '?'));
  431. else
  432. $url_without_params = $post['file_url'];
  433. $post['extension'] = strtolower(mb_substr($url_without_params, mb_strrpos($url_without_params, '.') + 1));
  434. if ($post['op'] && $config['allowed_ext_op']) {
  435. if (!in_array($post['extension'], $config['allowed_ext_op']))
  436. error($config['error']['unknownext']);
  437. }
  438. else if (!in_array($post['extension'], $config['allowed_ext']) && !in_array($post['extension'], $config['allowed_ext_files']))
  439. error($config['error']['unknownext']);
  440. $post['file_tmp'] = tempnam($config['tmp'], 'url');
  441. register_shutdown_function('unlink_tmp_file', $post['file_tmp']);
  442. $fp = fopen($post['file_tmp'], 'w');
  443. $curl = curl_init();
  444. curl_setopt($curl, CURLOPT_URL, $post['file_url']);
  445. curl_setopt($curl, CURLOPT_FAILONERROR, true);
  446. curl_setopt($curl, CURLOPT_FOLLOWLOCATION, false);
  447. curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 5);
  448. curl_setopt($curl, CURLOPT_TIMEOUT, $config['upload_by_url_timeout']);
  449. curl_setopt($curl, CURLOPT_USERAGENT, 'Tinyboard');
  450. curl_setopt($curl, CURLOPT_BINARYTRANSFER, true);
  451. curl_setopt($curl, CURLOPT_FILE, $fp);
  452. curl_setopt($curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
  453. if (curl_exec($curl) === false)
  454. error($config['error']['nomove'] . '<br/>Curl says: ' . curl_error($curl));
  455. curl_close($curl);
  456. fclose($fp);
  457. $_FILES[$post['file_tmp']] = array(
  458. 'name' => basename($url_without_params),
  459. 'tmp_name' => $post['file_tmp'],
  460. 'file_tmp' => true,
  461. 'error' => 0,
  462. 'size' => filesize($post['file_tmp'])
  463. );
  464. }
  465. for( $counter = 1; $counter <= $config['max_images']; $counter++ ) {
  466. $varname = "file_url". $counter;
  467. if (isset($_POST[$varname]) && !empty($_POST[$varname])){
  468. upload_by_url($config,$post,$_POST[$varname]);
  469. }
  470. }
  471. }
  472. $post['name'] = $_POST['name'] != '' ? $_POST['name'] : $config['anonymous'];
  473. $post['subject'] = $_POST['subject'];
  474. $post['email'] = str_replace(' ', '%20', htmlspecialchars($_POST['email']));
  475. $post['body'] = $_POST['body'];
  476. $post['password'] = $_POST['password'];
  477. $post['has_file'] = (!isset($post['embed']) && (($post['op'] && !isset($post['no_longer_require_an_image_for_op']) && $config['force_image_op']) || count($_FILES) > 0));
  478. if (!$dropped_post) {
  479. if (!($post['has_file'] || isset($post['embed'])) || (($post['op'] && $config['force_body_op']) || (!$post['op'] && $config['force_body']))) {
  480. $stripped_whitespace = preg_replace('/[\s]/u', '', $post['body']);
  481. if ($stripped_whitespace == '') {
  482. error($config['error']['tooshort_body']);
  483. }
  484. }
  485. if (!$post['op']) {
  486. // Check if thread is locked
  487. // but allow mods to post
  488. if ($thread['locked'] && !hasPermission($config['mod']['postinlocked'], $board['uri']))
  489. error($config['error']['locked']);
  490. $numposts = numPosts($post['thread']);
  491. $replythreshold = isset($thread['cycle']) && $thread['cycle'] ? $numposts['replies'] - 1 : $numposts['replies'];
  492. $imagethreshold = isset($thread['cycle']) && $thread['cycle'] ? $numposts['images'] - 1 : $numposts['images'];
  493. if ($config['reply_hard_limit'] != 0 && $config['reply_hard_limit'] <= $replythreshold)
  494. error($config['error']['reply_hard_limit']);
  495. if ($post['has_file'] && $config['image_hard_limit'] != 0 && $config['image_hard_limit'] <= $imagethreshold)
  496. error($config['error']['image_hard_limit']);
  497. }
  498. }
  499. else {
  500. if (!$post['op']) {
  501. $numposts = numPosts($post['thread']);
  502. }
  503. }
  504. if ($post['has_file']) {
  505. // Determine size sanity
  506. $size = 0;
  507. if ($config['multiimage_method'] == 'split') {
  508. foreach ($_FILES as $key => $file) {
  509. $size += $file['size'];
  510. }
  511. } elseif ($config['multiimage_method'] == 'each') {
  512. foreach ($_FILES as $key => $file) {
  513. if ($file['size'] > $size) {
  514. $size = $file['size'];
  515. }
  516. }
  517. } else {
  518. error(_('Unrecognized file size determination method.'));
  519. }
  520. $max_size = $config['max_filesize'];
  521. if (array_key_exists('board_specific',$config)){
  522. if (array_key_exists($board['uri'],$config['board_specific'])){
  523. if (array_key_exists('max_filesize',$config['board_specific'][$board['uri']])){
  524. $max_size = $config['board_specific'][$board['uri']]['max_filesize'];
  525. }
  526. }
  527. }
  528. if ($size > $max_size)
  529. error(sprintf3($config['error']['filesize'], array(
  530. 'sz' => number_format($size),
  531. 'filesz' => number_format($size),
  532. 'maxsz' => number_format($config['max_filesize'])
  533. )));
  534. $post['filesize'] = $size;
  535. }
  536. $post['capcode'] = false;
  537. if ($mod && preg_match('/^((.+) )?## (.+)$/', $post['name'], $matches)) {
  538. $name = $matches[2] != '' ? $matches[2] : $config['anonymous'];
  539. $cap = $matches[3];
  540. if (isset($config['mod']['capcode'][$mod['type']])) {
  541. if ( $config['mod']['capcode'][$mod['type']] === true ||
  542. (is_array($config['mod']['capcode'][$mod['type']]) &&
  543. in_array($cap, $config['mod']['capcode'][$mod['type']])
  544. )) {
  545. $post['capcode'] = utf8tohtml($cap);
  546. $post['name'] = $name;
  547. }
  548. }
  549. }
  550. else if ($config['joke_capcode']) {
  551. if (strtolower($post['email']) == 'joke') {
  552. if (isset($config['joke_capcode_default'])){
  553. $cap = $config['joke_capcode_default'];
  554. }
  555. else {
  556. $cap = "joke";
  557. }
  558. $post['capcode'] = utf8tohtml($cap);
  559. $post['email'] = '';
  560. }
  561. }
  562. $trip = generate_tripcode($post['name']);
  563. $post['name'] = $trip[0];
  564. $post['trip'] = isset($trip[1]) ? $trip[1] : ''; // XX: Dropped posts and tripcodes
  565. $noko = false;
  566. if (strtolower($post['email']) == 'noko') {
  567. $noko = true;
  568. $post['email'] = '';
  569. } elseif (strtolower($post['email']) == 'nonoko'){
  570. $noko = false;
  571. $post['email'] = '';
  572. } else $noko = $config['always_noko'];
  573. if ($post['has_file']) {
  574. $i = 0;
  575. foreach ($_FILES as $key => $file) {
  576. if ($file['size'] && $file['tmp_name']) {
  577. $file['filename'] = urldecode($file['name']);
  578. $file['extension'] = strtolower(mb_substr($file['filename'], mb_strrpos($file['filename'], '.') + 1));
  579. if (isset($config['filename_func']))
  580. $file['file_id'] = $config['filename_func']($file);
  581. else
  582. $file['file_id'] = time() . substr(microtime(), 2, 3);
  583. if (sizeof($_FILES) > 1)
  584. $file['file_id'] .= "-$i";
  585. $file['file'] = $board['dir'] . $config['dir']['img'] . $file['file_id'] . '.' . $file['extension'];
  586. $file['thumb'] = $board['dir'] . $config['dir']['thumb'] . $file['file_id'] . '.' . ($config['thumb_ext'] ? $config['thumb_ext'] : $file['extension']);
  587. $post['files'][] = $file;
  588. $i++;
  589. }
  590. }
  591. }
  592. if (empty($post['files'])) $post['has_file'] = false;
  593. if (!$dropped_post) {
  594. // Check for a file
  595. if ($post['op'] && !isset($post['no_longer_require_an_image_for_op'])) {
  596. if (!$post['has_file'] && $config['force_image_op'])
  597. error($config['error']['noimage']);
  598. }
  599. // Check for too many files
  600. if (sizeof($post['files']) > $config['max_images'])
  601. error($config['error']['toomanyimages']);
  602. }
  603. if ($config['strip_combining_chars']) {
  604. $post['name'] = strip_combining_chars($post['name']);
  605. $post['email'] = strip_combining_chars($post['email']);
  606. $post['subject'] = strip_combining_chars($post['subject']);
  607. $post['body'] = strip_combining_chars($post['body']);
  608. }
  609. if (!$dropped_post) {
  610. // Check string lengths
  611. if (mb_strlen($post['name']) > 35)
  612. error(sprintf($config['error']['toolong'], 'name'));
  613. if (mb_strlen($post['email']) > 40)
  614. error(sprintf($config['error']['toolong'], 'email'));
  615. if (mb_strlen($post['subject']) > 100)
  616. error(sprintf($config['error']['toolong'], 'subject'));
  617. if (!$mod && mb_strlen($post['body']) > $config['max_body'])
  618. error($config['error']['toolong_body']);
  619. if (!$mod && mb_strlen($post['body']) > 0 && (mb_strlen($post['body']) < $config['min_body']))
  620. error($config['error']['tooshort_body']);
  621. if (mb_strlen($post['password']) > 20)
  622. error(sprintf($config['error']['toolong'], 'password'));
  623. }
  624. wordfilters($post['body']);
  625. $post['body'] = escape_markup_modifiers($post['body']);
  626. if ($mod && isset($post['raw']) && $post['raw']) {
  627. $post['body'] .= "\n<tinyboard raw html>1</tinyboard>";
  628. }
  629. if (!$dropped_post)
  630. if (($config['country_flags'] && !$config['allow_no_country']) || ($config['country_flags'] && $config['allow_no_country'] && !isset($_POST['no_country']))) {
  631. require 'inc/lib/geoip/geoip.inc';
  632. $gi=geoip\geoip_open('inc/lib/geoip/GeoIPv6.dat', GEOIP_STANDARD);
  633. function ipv4to6($ip) {
  634. if (strpos($ip, ':') !== false) {
  635. if (strpos($ip, '.') > 0)
  636. $ip = substr($ip, strrpos($ip, ':')+1);
  637. else return $ip; //native ipv6
  638. }
  639. $iparr = array_pad(explode('.', $ip), 4, 0);
  640. $part7 = base_convert(($iparr[0] * 256) + $iparr[1], 10, 16);
  641. $part8 = base_convert(($iparr[2] * 256) + $iparr[3], 10, 16);
  642. return '::ffff:'.$part7.':'.$part8;
  643. }
  644. if ($country_code = geoip\geoip_country_code_by_addr_v6($gi, ipv4to6($_SERVER['REMOTE_ADDR']))) {
  645. if (!in_array(strtolower($country_code), array('eu', 'ap', 'o1', 'a1', 'a2')))
  646. $post['body'] .= "\n<tinyboard flag>".strtolower($country_code)."</tinyboard>".
  647. "\n<tinyboard flag alt>".geoip\geoip_country_name_by_addr_v6($gi, ipv4to6($_SERVER['REMOTE_ADDR']))."</tinyboard>";
  648. }
  649. }
  650. if ($config['user_flag'] && isset($_POST['user_flag']))
  651. if (!empty($_POST['user_flag']) ){
  652. $user_flag = $_POST['user_flag'];
  653. if (!isset($config['user_flags'][$user_flag]))
  654. error(_('Invalid flag selection!'));
  655. $flag_alt = isset($user_flag_alt) ? $user_flag_alt : $config['user_flags'][$user_flag];
  656. $post['body'] .= "\n<tinyboard flag>" . strtolower($user_flag) . "</tinyboard>" .
  657. "\n<tinyboard flag alt>" . $flag_alt . "</tinyboard>";
  658. }
  659. if ($config['allowed_tags'] && $post['op'] && isset($_POST['tag']) && isset($config['allowed_tags'][$_POST['tag']])) {
  660. $post['body'] .= "\n<tinyboard tag>" . $_POST['tag'] . "</tinyboard>";
  661. }
  662. if (!$dropped_post)
  663. if ($config['proxy_save'] && isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
  664. $proxy = preg_replace("/[^0-9a-fA-F.,: ]/", '', $_SERVER['HTTP_X_FORWARDED_FOR']);
  665. $post['body'] .= "\n<tinyboard proxy>".$proxy."</tinyboard>";
  666. }
  667. if (mysql_version() >= 50503) {
  668. $post['body_nomarkup'] = $post['body']; // Assume we're using the utf8mb4 charset
  669. } else {
  670. // MySQL's `utf8` charset only supports up to 3-byte symbols
  671. // Remove anything >= 0x010000
  672. $chars = preg_split('//u', $post['body'], -1, PREG_SPLIT_NO_EMPTY);
  673. $post['body_nomarkup'] = '';
  674. foreach ($chars as $char) {
  675. $o = 0;
  676. $ord = ordutf8($char, $o);
  677. if ($ord >= 0x010000)
  678. continue;
  679. $post['body_nomarkup'] .= $char;
  680. }
  681. }
  682. $post['tracked_cites'] = markup($post['body'], true);
  683. if ($post['has_file']) {
  684. $md5cmd = false;
  685. if ($config['bsd_md5']) $md5cmd = '/sbin/md5 -r';
  686. if ($config['gnu_md5']) $md5cmd = 'md5sum';
  687. $allhashes = '';
  688. foreach ($post['files'] as $key => &$file) {
  689. if ($post['op'] && $config['allowed_ext_op']) {
  690. if (!in_array($file['extension'], $config['allowed_ext_op']))
  691. error($config['error']['unknownext']);
  692. }
  693. elseif (!in_array($file['extension'], $config['allowed_ext']) && !in_array($file['extension'], $config['allowed_ext_files']))
  694. error($config['error']['unknownext']);
  695. $file['is_an_image'] = !in_array($file['extension'], $config['allowed_ext_files']);
  696. // Truncate filename if it is too long
  697. $file['filename'] = mb_substr($file['filename'], 0, $config['max_filename_len']);
  698. $upload = $file['tmp_name'];
  699. if (!is_readable($upload))
  700. error($config['error']['nomove']);
  701. if ($md5cmd) {
  702. $output = shell_exec_error($md5cmd . " " . escapeshellarg($upload));
  703. $output = explode(' ', $output);
  704. $hash = $output[0];
  705. }
  706. else {
  707. $hash = md5_file($upload);
  708. }
  709. $file['hash'] = $hash;
  710. $allhashes .= $hash;
  711. }
  712. if (count ($post['files']) == 1) {
  713. $post['filehash'] = $hash;
  714. }
  715. else {
  716. $post['filehash'] = md5($allhashes);
  717. }
  718. }
  719. if (!hasPermission($config['mod']['bypass_filters'], $board['uri']) && !$dropped_post) {
  720. require_once 'inc/filters.php';
  721. do_filters($post);
  722. }
  723. if ($post['has_file']) {
  724. foreach ($post['files'] as $key => &$file) {
  725. if ($file['is_an_image']) {
  726. if ($config['ie_mime_type_detection'] !== false) {
  727. // Check IE MIME type detection XSS exploit
  728. $buffer = file_get_contents($upload, null, null, null, 255);
  729. if (preg_match($config['ie_mime_type_detection'], $buffer)) {
  730. undoImage($post);
  731. error($config['error']['mime_exploit']);
  732. }
  733. }
  734. require_once 'inc/image.php';
  735. // find dimensions of an image using GD
  736. if (!$size = @getimagesize($file['tmp_name'])) {
  737. error($config['error']['invalidimg']);
  738. }
  739. if (!in_array($size[2], array(IMAGETYPE_PNG, IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_BMP))) {
  740. error($config['error']['invalidimg']);
  741. }
  742. if ($size[0] > $config['max_width'] || $size[1] > $config['max_height']) {
  743. error($config['error']['maxsize']);
  744. }
  745. if ($config['convert_auto_orient'] && ($file['extension'] == 'jpg' || $file['extension'] == 'jpeg')) {
  746. // The following code corrects the image orientation.
  747. // Currently only works with the 'convert' option selected but it could easily be expanded to work with the rest if you can be bothered.
  748. if (!($config['redraw_image'] || (($config['strip_exif'] && !$config['use_exiftool']) && ($file['extension'] == 'jpg' || $file['extension'] == 'jpeg')))) {
  749. if (in_array($config['thumb_method'], array('convert', 'convert+gifsicle', 'gm', 'gm+gifsicle'))) {
  750. $exif = @exif_read_data($file['tmp_name']);
  751. $gm = in_array($config['thumb_method'], array('gm', 'gm+gifsicle'));
  752. if (isset($exif['Orientation']) && $exif['Orientation'] != 1) {
  753. if ($config['convert_manual_orient']) {
  754. $error = shell_exec_error(($gm ? 'gm ' : '') . 'convert ' .
  755. escapeshellarg($file['tmp_name']) . ' ' .
  756. ImageConvert::jpeg_exif_orientation(false, $exif) . ' ' .
  757. ($config['strip_exif'] ? '+profile "*"' :
  758. ($config['use_exiftool'] ? '' : '+profile "*"')
  759. ) . ' ' .
  760. escapeshellarg($file['tmp_name']));
  761. if ($config['use_exiftool'] && !$config['strip_exif']) {
  762. if ($exiftool_error = shell_exec_error(
  763. 'exiftool -overwrite_original -q -q -orientation=1 -n ' .
  764. escapeshellarg($file['tmp_name'])))
  765. error(_('exiftool failed!'), null, $exiftool_error);
  766. } else {
  767. // TODO: Find another way to remove the Orientation tag from the EXIF profile
  768. // without needing `exiftool`.
  769. }
  770. } else {
  771. $error = shell_exec_error(($gm ? 'gm ' : '') . 'convert ' .
  772. escapeshellarg($file['tmp_name']) . ' -auto-orient ' . escapeshellarg($upload));
  773. }
  774. if ($error)
  775. error(_('Could not auto-orient image!'), null, $error);
  776. $size = @getimagesize($file['tmp_name']);
  777. if ($config['strip_exif'])
  778. $file['exif_stripped'] = true;
  779. }
  780. }
  781. }
  782. }
  783. // create image object
  784. $image = new Image($file['tmp_name'], $file['extension'], $size);
  785. if ($image->size->width > $config['max_width'] || $image->size->height > $config['max_height']) {
  786. $image->delete();
  787. error($config['error']['maxsize']);
  788. }
  789. $file['width'] = $image->size->width;
  790. $file['height'] = $image->size->height;
  791. if ($config['spoiler_images'] && isset($_POST['spoiler'])) {
  792. $file['thumb'] = 'spoiler';
  793. $size = @getimagesize($config['spoiler_image']);
  794. $file['thumbwidth'] = $size[0];
  795. $file['thumbheight'] = $size[1];
  796. } elseif ($config['minimum_copy_resize'] &&
  797. $image->size->width <= $config['thumb_width'] &&
  798. $image->size->height <= $config['thumb_height'] &&
  799. $file['extension'] == ($config['thumb_ext'] ? $config['thumb_ext'] : $file['extension'])) {
  800. // Copy, because there's nothing to resize
  801. copy($file['tmp_name'], $file['thumb']);
  802. $file['thumbwidth'] = $image->size->width;
  803. $file['thumbheight'] = $image->size->height;
  804. } else {
  805. $thumb = $image->resize(
  806. $config['thumb_ext'] ? $config['thumb_ext'] : $file['extension'],
  807. $post['op'] ? $config['thumb_op_width'] : $config['thumb_width'],
  808. $post['op'] ? $config['thumb_op_height'] : $config['thumb_height']
  809. );
  810. $thumb->to($file['thumb']);
  811. $file['thumbwidth'] = $thumb->width;
  812. $file['thumbheight'] = $thumb->height;
  813. $thumb->_destroy();
  814. }
  815. if ($config['redraw_image'] || (!@$file['exif_stripped'] && $config['strip_exif'] && ($file['extension'] == 'jpg' || $file['extension'] == 'jpeg'))) {
  816. if (!$config['redraw_image'] && $config['use_exiftool']) {
  817. if($error = shell_exec_error('exiftool -overwrite_original -ignoreMinorErrors -q -q -all= ' .
  818. escapeshellarg($file['tmp_name'])))
  819. error(_('Could not strip EXIF metadata!'), null, $error);
  820. } else {
  821. $image->to($file['file']);
  822. $dont_copy_file = true;
  823. }
  824. }
  825. $image->destroy();
  826. } else {
  827. if (($file['extension'] == "pdf" && $config['pdf_file_thumbnail']) ||
  828. ($file['extension'] == "djvu" && $config['djvu_file_thumbnail']) ){
  829. $path = $file['thumb'];
  830. $error = shell_exec_error( 'convert -thumbnail x300 -background white -alpha remove ' .
  831. escapeshellarg($file['tmp_name']. '[0]') . ' ' .
  832. escapeshellarg($file['thumb']));
  833. if ($error){
  834. $path = sprintf($config['file_thumb'],isset($config['file_icons'][$file['extension']]) ? $config['file_icons'][$file['extension']] : $config['file_icons']['default']);
  835. }
  836. $file['thumb'] = basename($file['thumb']);
  837. $size = @getimagesize($path);
  838. $file['thumbwidth'] = $size[0];
  839. $file['thumbheight'] = $size[1];
  840. $file['width'] = $size[0];
  841. $file['height'] = $size[1];
  842. }
  843. /*if (($file['extension'] == "epub" && $config['epub_file_thumbnail'])){
  844. $path = $file['thumb'];
  845. // Open epub
  846. // Get file list
  847. // Check if cover file exists according to regex if it does use it
  848. // Otherwise check if metadata file exists, and if does get rootfile and search for manifest for cover file name
  849. // Otherwise Check if other image files exist and use them, based on criteria to pick the best one.
  850. // Once we have filename extract said file from epub to file['thumb'] location.
  851. $zip = new ZipArchive();
  852. if(@$zip->open($path)){
  853. $filename = "";
  854. // Go looking for a file name, current implementation just uses regex but should fallback to
  855. // getting all images and then choosing one.
  856. for( $i = 0; $i < $zip->numFiles; $i++ ){
  857. $stat = $zip->statIndex( $i );
  858. $matches = array();
  859. if (preg_match('/.*cover.*\.(jpg|jpeg|png)/', $stat['name'], $matches)) {
  860. $filename = $matches[0];
  861. break;
  862. }
  863. }
  864. // We have a cover filename to extract.
  865. if (strlen($filename) > 0){
  866. //$zip->extractTo(dirname($file['thumb']), array($filename));
  867. }
  868. else {
  869. $error = 1;
  870. }
  871. }
  872. else {
  873. $error = 1;
  874. }
  875. if ($error){
  876. $path = sprintf($config['file_thumb'],isset($config['file_icons'][$file['extension']]) ? $config['file_icons'][$file['extension']] : $config['file_icons']['default']);
  877. }
  878. $file['thumb'] = basename($file['thumb']);
  879. $size = @getimagesize($path);
  880. $file['thumbwidth'] = $size[0];
  881. $file['thumbheight'] = $size[1];
  882. $file['width'] = $size[0];
  883. $file['height'] = $size[1];
  884. }*/
  885. else if ($file['extension'] == "txt" && $config['txt_file_thumbnail']){
  886. $path = $file['thumb'];
  887. $error = shell_exec_error( 'convert -thumbnail x300 xc:white -font "FreeMono" -pointsize 12 -fill black -annotate +15+15 ' .
  888. escapeshellarg( '@' . $file['tmp_name']) . ' ' .
  889. escapeshellarg($file['thumb']));
  890. if ($error){
  891. $path = sprintf($config['file_thumb'],isset($config['file_icons'][$file['extension']]) ? $config['file_icons'][$file['extension']] : $config['file_icons']['default']);
  892. }
  893. $file['thumb'] = basename($file['thumb']);
  894. $size = @getimagesize($path);
  895. $file['thumbwidth'] = $size[0];
  896. $file['thumbheight'] = $size[1];
  897. $file['width'] = $size[0];
  898. $file['height'] = $size[1];
  899. }
  900. else if ($file['extension'] == "svg"){
  901. // Copy, because there's nothing to resize
  902. $file['thumb'] = substr_replace($file['thumb'] , $file['extension'], strrpos($file['thumb'] , '.') +1);
  903. copy($file['tmp_name'], $file['thumb']);
  904. $file['thumbwidth'] = $config['thumb_width'];
  905. $file['thumbheight'] = $config['thumb_height'];
  906. $file['thumb'] = basename($file['thumb']);
  907. }
  908. else {
  909. // not an image
  910. //copy($config['file_thumb'], $post['thumb']);
  911. $file['thumb'] = 'file';
  912. $size = @getimagesize(sprintf($config['file_thumb'],
  913. isset($config['file_icons'][$file['extension']]) ?
  914. $config['file_icons'][$file['extension']] : $config['file_icons']['default']));
  915. $file['thumbwidth'] = $size[0];
  916. $file['thumbheight'] = $size[1];
  917. }
  918. }
  919. if ($config['tesseract_ocr'] && $file['thumb'] != 'file') { // Let's OCR it!
  920. $fname = $file['tmp_name'];
  921. if ($file['height'] > 500 || $file['width'] > 500) {
  922. $fname = $file['thumb'];
  923. }
  924. if ($fname == 'spoiler') { // We don't have that much CPU time, do we?
  925. }
  926. else {
  927. $tmpname = __DIR__ . "/tmp/tesseract/".rand(0,10000000);
  928. // Preprocess command is an ImageMagick b/w quantization
  929. $error = shell_exec_error(sprintf($config['tesseract_preprocess_command'], escapeshellarg($fname)) . " | " .
  930. 'tesseract stdin '.escapeshellarg($tmpname).' '.$config['tesseract_params']);
  931. $tmpname .= ".txt";
  932. $value = @file_get_contents($tmpname);
  933. @unlink($tmpname);
  934. if ($value && trim($value)) {
  935. // This one has an effect, that the body is appended to a post body. So you can write a correct
  936. // spamfilter.
  937. $post['body_nomarkup'] .= "<tinyboard ocr image $key>".htmlspecialchars($value)."</tinyboard>";
  938. }
  939. }
  940. }
  941. if (!isset($dont_copy_file) || !$dont_copy_file) {
  942. if (isset($file['file_tmp'])) {
  943. if (!@rename($file['tmp_name'], $file['file']))
  944. error($config['error']['nomove']);
  945. chmod($file['file'], 0644);
  946. } elseif (!@move_uploaded_file($file['tmp_name'], $file['file']))
  947. error($config['error']['nomove']);
  948. }
  949. }
  950. if ($config['image_reject_repost']) {
  951. if ($p = getPostByHash($post['filehash'])) {
  952. undoImage($post);
  953. error(sprintf($config['error']['fileexists'],
  954. ($post['mod'] ? $config['root'] . $config['file_mod'] . '?/' : $config['root']) .
  955. ($board['dir'] . $config['dir']['res'] .
  956. ($p['thread'] ?
  957. $p['thread'] . '.html#' . $p['id']
  958. :
  959. $p['id'] . '.html'
  960. ))
  961. ));
  962. }
  963. } else if (!$post['op'] && $config['image_reject_repost_in_thread']) {
  964. if ($p = getPostByHashInThread($post['filehash'], $post['thread'])) {
  965. undoImage($post);
  966. error(sprintf($config['error']['fileexistsinthread'],
  967. ($post['mod'] ? $config['root'] . $config['file_mod'] . '?/' : $config['root']) .
  968. ($board['dir'] . $config['dir']['res'] .
  969. ($p['thread'] ?
  970. $p['thread'] . '.html#' . $p['id']
  971. :
  972. $p['id'] . '.html'
  973. ))
  974. ));
  975. }
  976. }
  977. }
  978. // Do filters again if OCRing
  979. if ($config['tesseract_ocr'] && !hasPermission($config['mod']['bypass_filters'], $board['uri']) && !$dropped_post) {
  980. do_filters($post);
  981. }
  982. if (!hasPermission($config['mod']['postunoriginal'], $board['uri']) && $config['robot_enable'] && checkRobot($post['body_nomarkup']) && !$dropped_post) {
  983. undoImage($post);
  984. if ($config['robot_mute']) {
  985. error(sprintf($config['error']['muted'], mute()));
  986. } else {
  987. error($config['error']['unoriginal']);
  988. }
  989. }
  990. // Remove board directories before inserting them into the database.
  991. if ($post['has_file']) {
  992. foreach ($post['files'] as $key => &$file) {
  993. $file['file_path'] = $file['file'];
  994. $file['thumb_path'] = $file['thumb'];
  995. $file['file'] = mb_substr($file['file'], mb_strlen($board['dir'] . $config['dir']['img']));
  996. if ($file['is_an_image'] && $file['thumb'] != 'spoiler')
  997. $file['thumb'] = mb_substr($file['thumb'], mb_strlen($board['dir'] . $config['dir']['thumb']));
  998. }
  999. }
  1000. $post = (object)$post;
  1001. $post->files = array_map(function($a) { return (object)$a; }, $post->files);
  1002. $error = event('post', $post);
  1003. $post->files = array_map(function($a) { return (array)$a; }, $post->files);
  1004. if ($error) {
  1005. undoImage((array)$post);
  1006. error($error);
  1007. }
  1008. $post = (array)$post;
  1009. if ($post['files'])
  1010. $post['files'] = $post['files'];
  1011. $post['num_files'] = sizeof($post['files']);
  1012. $post['id'] = $id = post($post);
  1013. $post['slug'] = slugify($post);
  1014. if ($dropped_post && $dropped_post['from_nntp']) {
  1015. $query = prepare("INSERT INTO ``nntp_references`` (`board`, `id`, `message_id`, `message_id_digest`, `own`, `headers`) VALUES ".
  1016. "(:board , :id , :message_id , :message_id_digest , false, :headers)");
  1017. $query->bindValue(':board', $dropped_post['board']);
  1018. $query->bindValue(':id', $id);
  1019. $query->bindValue(':message_id', $dropped_post['msgid']);
  1020. $query->bindValue(':message_id_digest', sha1($dropped_post['msgid']));
  1021. $query->bindValue(':headers', $dropped_post['headers']);
  1022. $query->execute() or error(db_error($query));
  1023. } // ^^^^^ For inbound posts ^^^^^
  1024. elseif ($config['nntpchan']['enabled'] && $config['nntpchan']['group']) {
  1025. // vvvvv For outbound posts vvvvv
  1026. require_once('inc/nntpchan/nntpchan.php');
  1027. $msgid = gen_msgid($post['board'], $post['id']);
  1028. list($headers, $files) = post2nntp($post, $msgid);
  1029. $message = gen_nntp($headers, $files);
  1030. $query = prepare("INSERT INTO ``nntp_references`` (`board`, `id`, `message_id`, `message_id_digest`, `own`, `headers`) VALUES ".
  1031. "(:board , :id , :message_id , :message_id_digest , true , :headers)");
  1032. $query->bindValue(':board', $post['board']);
  1033. $query->bindValue(':id', $post['id']);
  1034. $query->bindValue(':message_id', $msgid);
  1035. $query->bindValue(':message_id_digest', sha1($msgid));
  1036. $query->bindValue(':headers', json_encode($headers));
  1037. $query->execute() or error(db_error($query));
  1038. // Let's broadcast it!
  1039. nntp_publish($message, $msgid);
  1040. }
  1041. insertFloodPost($post);
  1042. // Handle cyclical threads
  1043. if (!$post['op'] && isset($thread['cycle']) && $thread['cycle']) {
  1044. // Query is a bit weird due to "This version of MariaDB doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'" (MariaDB Ver 15.1 Distrib 10.0.17-MariaDB, for Linux (x86_64))
  1045. $query = prepare(sprintf('DELETE FROM ``posts_%s`` WHERE `thread` = :thread AND `id` NOT IN (SELECT `id` FROM (SELECT `id` FROM ``posts_%s`` WHERE `thread` = :thread ORDER BY `id` DESC LIMIT :limit) i)', $board['uri'], $board['uri']));
  1046. $query->bindValue(':thread', $post['thread']);
  1047. $query->bindValue(':limit', $config['cycle_limit'], PDO::PARAM_INT);
  1048. $query->execute() or error(db_error($query));
  1049. }
  1050. if (isset($post['antispam_hash'])) {
  1051. incrementSpamHash($post['antispam_hash']);
  1052. }
  1053. if (isset($post['tracked_cites']) && !empty($post['tracked_cites'])) {
  1054. $insert_rows = array();
  1055. foreach ($post['tracked_cites'] as $cite) {
  1056. $insert_rows[] = '(' .
  1057. $pdo->quote($board['uri']) . ', ' . (int)$id . ', ' .
  1058. $pdo->quote($cite[0]) . ', ' . (int)$cite[1] . ')';
  1059. }
  1060. query('INSERT INTO ``cites`` VALUES ' . implode(', ', $insert_rows)) or error(db_error());
  1061. }
  1062. if (!$post['op'] && strtolower($post['email']) != 'sage' && !$thread['sage'] && ($config['reply_limit'] == 0 || $numposts['replies']+1 < $config['reply_limit'])) {
  1063. bumpThread($post['thread']);
  1064. }
  1065. if (isset($_SERVER['HTTP_REFERER'])) {
  1066. // Tell Javascript that we posted successfully
  1067. if (isset($_COOKIE[$config['cookies']['js']]))
  1068. $js = json_decode($_COOKIE[$config['cookies']['js']]);
  1069. else
  1070. $js = (object) array();
  1071. // Tell it to delete the cached post for referer
  1072. $js->{$_SERVER['HTTP_REFERER']} = true;
  1073. // Encode and set cookie
  1074. setcookie($config['cookies']['js'], json_encode($js), 0, $config['cookies']['jail'] ? $config['cookies']['path'] : '/', null, false, false);
  1075. }
  1076. $root = $post['mod'] ? $config['root'] . $config['file_mod'] . '?/' : $config['root'];
  1077. if ($noko) {
  1078. $redirect = $root . $board['dir'] . $config['dir']['res'] .
  1079. link_for($post, false, false, $thread) . (!$post['op'] ? '#' . $id : '');
  1080. if (!$post['op'] && isset($_SERVER['HTTP_REFERER'])) {
  1081. $regex = array(
  1082. 'board' => str_replace('%s', '(\w{1,8})', preg_quote($config['board_path'], '/')),
  1083. 'page' => str_replace('%d', '(\d+)', preg_quote($config['file_page'], '/')),
  1084. 'page50' => '(' . str_replace('%d', '(\d+)', preg_quote($config['file_page50'], '/')) . '|' .
  1085. str_replace(array('%d', '%s'), array('(\d+)', '[a-z0-9-]+'), preg_quote($config['file_page50_slug'], '/')) . ')',
  1086. 'res' => preg_quote($config['dir']['res'], '/'),
  1087. );
  1088. if (preg_match('/\/' . $regex['board'] . $regex['res'] . $regex['page50'] . '([?&].*)?$/', $_SERVER['HTTP_REFERER'])) {
  1089. $redirect = $root . $board['dir'] . $config['dir']['res'] .
  1090. link_for($post, true, false, $thread) . (!$post['op'] ? '#' . $id : '');
  1091. }
  1092. }
  1093. } else {
  1094. $redirect = $root . $board['dir'] . $config['file_index'];
  1095. }
  1096. buildThread($post['op'] ? $id : $post['thread']);
  1097. if ($config['syslog'])
  1098. _syslog(LOG_INFO, 'New post: /' . $board['dir'] . $config['dir']['res'] .
  1099. link_for($post) . (!$post['op'] ? '#' . $id : ''));
  1100. if (!$post['mod']) header('X-Associated-Content: "' . $redirect . '"');
  1101. if (!isset($_POST['json_response'])) {
  1102. header('Location: ' . $redirect, true, $config['redirect_http']);
  1103. } else {
  1104. header('Content-Type: text/json; charset=utf-8');
  1105. echo json_encode(array(
  1106. 'redirect' => $redirect,
  1107. 'noko' => $noko,
  1108. 'id' => $id
  1109. ));
  1110. }
  1111. if ($config['try_smarter'] && $post['op'])
  1112. $build_pages = range(1, $config['max_pages']);
  1113. if ($post['op'])
  1114. clean($id);
  1115. event('post-after', $post);
  1116. buildIndex();
  1117. // We are already done, let's continue our heavy-lifting work in the background (if we run off FastCGI)
  1118. if (function_exists('fastcgi_finish_request'))
  1119. @fastcgi_finish_request();
  1120. if ($post['op'])
  1121. rebuildThemes('post-thread', $board['uri']);
  1122. else
  1123. rebuildThemes('post', $board['uri']);
  1124. } elseif (isset($_POST['appeal'])) {
  1125. if (!isset($_POST['ban_id']))
  1126. error($config['error']['bot']);
  1127. $ban_id = (int)$_POST['ban_id'];
  1128. $bans = Bans::find($_SERVER['REMOTE_ADDR']);
  1129. foreach ($bans as $_ban) {
  1130. if ($_ban['id'] == $ban_id) {
  1131. $ban = $_ban;
  1132. break;
  1133. }
  1134. }
  1135. if (!isset($ban)) {
  1136. error(_("That ban doesn't exist or is not for you."));
  1137. }
  1138. if ($ban['expires'] && $ban['expires'] - $ban['created'] <= $config['ban_appeals_min_length']) {
  1139. error(_("You cannot appeal a ban of this length."));
  1140. }
  1141. $query = query("SELECT `denied` FROM ``ban_appeals`` WHERE `ban_id` = $ban_id") or error(db_error());
  1142. $ban_appeals = $query->fetchAll(PDO::FETCH_COLUMN);
  1143. if (count($ban_appeals) >= $config['ban_appeals_max']) {
  1144. error(_("You cannot appeal this ban again."));
  1145. }
  1146. foreach ($ban_appeals as $is_denied) {
  1147. if (!$is_denied)
  1148. error(_("There is already a pending appeal for this ban."));
  1149. }
  1150. $query = prepare("INSERT INTO ``ban_appeals`` VALUES (NULL, :ban_id, :time, :message, 0)");
  1151. $query->bindValue(':ban_id', $ban_id, PDO::PARAM_INT);
  1152. $query->bindValue(':time', time(), PDO::PARAM_INT);
  1153. $query->bindValue(':message', $_POST['appeal']);
  1154. $query->execute() or error(db_error($query));
  1155. displayBan($ban);
  1156. } else {
  1157. if (!file_exists($config['has_installed'])) {
  1158. header('Location: install.php', true, $config['redirect_http']);
  1159. } else {
  1160. // They opened post.php in their browser manually.
  1161. error($config['error']['nopost']);
  1162. }
  1163. }