ControllerTest.php 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936
  1. <?php
  2. use PHPUnit\Framework\TestCase;
  3. use PrivateBin\Controller;
  4. use PrivateBin\Data\Filesystem;
  5. use PrivateBin\Persistence\ServerSalt;
  6. use PrivateBin\Persistence\TrafficLimiter;
  7. use PrivateBin\Request;
  8. class ControllerTest extends TestCase
  9. {
  10. protected $_data;
  11. protected $_path;
  12. public function setUp(): void
  13. {
  14. /* Setup Routine */
  15. $this->_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'privatebin_data';
  16. $this->_data = Filesystem::getInstance(array('dir' => $this->_path));
  17. $this->reset();
  18. }
  19. public function tearDown(): void
  20. {
  21. /* Tear Down Routine */
  22. unlink(CONF);
  23. Helper::confRestore();
  24. Helper::rmDir($this->_path);
  25. }
  26. public function reset()
  27. {
  28. $_POST = array();
  29. $_GET = array();
  30. $_SERVER = array();
  31. if ($this->_data->exists(Helper::getPasteId())) {
  32. $this->_data->delete(Helper::getPasteId());
  33. }
  34. $options = parse_ini_file(CONF_SAMPLE, true);
  35. $options['purge']['dir'] = $this->_path;
  36. $options['traffic']['dir'] = $this->_path;
  37. $options['model_options']['dir'] = $this->_path;
  38. Helper::createIniFile(CONF, $options);
  39. ServerSalt::setPath($this->_path);
  40. }
  41. /**
  42. * @runInSeparateProcess
  43. */
  44. public function testView()
  45. {
  46. ob_start();
  47. new Controller;
  48. $content = ob_get_contents();
  49. ob_end_clean();
  50. $this->assertStringContainsString(
  51. '<title>PrivateBin</title>',
  52. $content,
  53. 'outputs title correctly'
  54. );
  55. $this->assertStringNotContainsString(
  56. 'id="shortenbutton"',
  57. $content,
  58. 'doesn\'t output shortener button'
  59. );
  60. }
  61. /**
  62. * @runInSeparateProcess
  63. */
  64. public function testViewLanguageSelection()
  65. {
  66. $options = parse_ini_file(CONF, true);
  67. $options['main']['languageselection'] = true;
  68. Helper::createIniFile(CONF, $options);
  69. $_COOKIE['lang'] = 'de';
  70. ob_start();
  71. new Controller;
  72. $content = ob_get_contents();
  73. ob_end_clean();
  74. $this->assertStringContainsString(
  75. '<title>PrivateBin</title>',
  76. $content,
  77. 'outputs title correctly'
  78. );
  79. }
  80. /**
  81. * @runInSeparateProcess
  82. */
  83. public function testViewForceLanguageDefault()
  84. {
  85. $options = parse_ini_file(CONF, true);
  86. $options['main']['languageselection'] = false;
  87. $options['main']['languagedefault'] = 'fr';
  88. Helper::createIniFile(CONF, $options);
  89. $_COOKIE['lang'] = 'de';
  90. ob_start();
  91. new Controller;
  92. $content = ob_get_contents();
  93. ob_end_clean();
  94. $this->assertStringContainsString(
  95. '<title>PrivateBin</title>',
  96. $content,
  97. 'outputs title correctly'
  98. );
  99. }
  100. /**
  101. * @runInSeparateProcess
  102. */
  103. public function testViewUrlShortener()
  104. {
  105. $shortener = 'https://shortener.example.com/api?link=';
  106. $options = parse_ini_file(CONF, true);
  107. $options['main']['urlshortener'] = $shortener;
  108. Helper::createIniFile(CONF, $options);
  109. $_COOKIE['lang'] = 'de';
  110. ob_start();
  111. new Controller;
  112. $content = ob_get_contents();
  113. ob_end_clean();
  114. $this->assertMatchesRegularExpression(
  115. '#id="shortenbutton"[^>]*data-shortener="' . preg_quote($shortener) . '"#',
  116. $content,
  117. 'outputs configured shortener URL correctly'
  118. );
  119. }
  120. /**
  121. * @runInSeparateProcess
  122. */
  123. public function testHtaccess()
  124. {
  125. $htaccess = $this->_path . DIRECTORY_SEPARATOR . '.htaccess';
  126. @unlink($htaccess);
  127. $paste = Helper::getPasteJson();
  128. $file = tempnam(sys_get_temp_dir(), 'FOO');
  129. file_put_contents($file, $paste);
  130. Request::setInputStream($file);
  131. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  132. $_SERVER['REQUEST_METHOD'] = 'POST';
  133. $_SERVER['REMOTE_ADDR'] = '::1';
  134. ob_start();
  135. new Controller;
  136. ob_end_clean();
  137. $this->assertFileExists($htaccess, 'htaccess recreated');
  138. }
  139. public function testConf()
  140. {
  141. file_put_contents(CONF, '');
  142. $this->expectException(Exception::class);
  143. $this->expectExceptionCode(2);
  144. new Controller;
  145. }
  146. /**
  147. * @runInSeparateProcess
  148. */
  149. public function testCreate()
  150. {
  151. $options = parse_ini_file(CONF, true);
  152. $options['traffic']['limit'] = 0;
  153. Helper::createIniFile(CONF, $options);
  154. $paste = Helper::getPasteJson();
  155. $file = tempnam(sys_get_temp_dir(), 'FOO');
  156. file_put_contents($file, $paste);
  157. Request::setInputStream($file);
  158. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  159. $_SERVER['REQUEST_METHOD'] = 'POST';
  160. $_SERVER['REMOTE_ADDR'] = '::1';
  161. ob_start();
  162. new Controller;
  163. $content = ob_get_contents();
  164. ob_end_clean();
  165. $response = json_decode($content, true);
  166. $this->assertEquals(0, $response['status'], 'outputs status');
  167. $this->assertTrue($this->_data->exists($response['id']), 'paste exists after posting data');
  168. $paste = $this->_data->read($response['id']);
  169. $this->assertEquals(
  170. hash_hmac('sha256', $response['id'], $paste['meta']['salt']),
  171. $response['deletetoken'],
  172. 'outputs valid delete token'
  173. );
  174. }
  175. /**
  176. * @runInSeparateProcess
  177. */
  178. public function testCreateInvalidTimelimit()
  179. {
  180. $options = parse_ini_file(CONF, true);
  181. $options['traffic']['limit'] = 0;
  182. Helper::createIniFile(CONF, $options);
  183. $paste = Helper::getPasteJson(2, array('expire' => 25));
  184. $file = tempnam(sys_get_temp_dir(), 'FOO');
  185. file_put_contents($file, $paste);
  186. Request::setInputStream($file);
  187. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  188. $_SERVER['REQUEST_METHOD'] = 'POST';
  189. $_SERVER['REMOTE_ADDR'] = '::1';
  190. TrafficLimiter::canPass();
  191. ob_start();
  192. new Controller;
  193. $content = ob_get_contents();
  194. ob_end_clean();
  195. $response = json_decode($content, true);
  196. $this->assertEquals(0, $response['status'], 'outputs status');
  197. $this->assertTrue($this->_data->exists($response['id']), 'paste exists after posting data');
  198. $paste = $this->_data->read($response['id']);
  199. $this->assertEquals(
  200. hash_hmac('sha256', $response['id'], $paste['meta']['salt']),
  201. $response['deletetoken'],
  202. 'outputs valid delete token'
  203. );
  204. }
  205. /**
  206. * @runInSeparateProcess
  207. */
  208. public function testCreateInvalidSize()
  209. {
  210. $options = parse_ini_file(CONF, true);
  211. $options['main']['sizelimit'] = 10;
  212. $options['traffic']['limit'] = 0;
  213. Helper::createIniFile(CONF, $options);
  214. $paste = Helper::getPasteJson();
  215. $file = tempnam(sys_get_temp_dir(), 'FOO');
  216. file_put_contents($file, $paste);
  217. Request::setInputStream($file);
  218. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  219. $_SERVER['REQUEST_METHOD'] = 'POST';
  220. $_SERVER['REMOTE_ADDR'] = '::1';
  221. ob_start();
  222. new Controller;
  223. $content = ob_get_contents();
  224. ob_end_clean();
  225. $response = json_decode($content, true);
  226. $this->assertEquals(1, $response['status'], 'outputs error status');
  227. $this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste exists after posting data');
  228. }
  229. /**
  230. * @runInSeparateProcess
  231. */
  232. public function testCreateProxyHeader()
  233. {
  234. $options = parse_ini_file(CONF, true);
  235. $options['traffic']['header'] = 'X_FORWARDED_FOR';
  236. Helper::createIniFile(CONF, $options);
  237. $paste = Helper::getPasteJson();
  238. $file = tempnam(sys_get_temp_dir(), 'FOO');
  239. file_put_contents($file, $paste);
  240. Request::setInputStream($file);
  241. $_SERVER['HTTP_X_FORWARDED_FOR'] = '::2';
  242. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  243. $_SERVER['REQUEST_METHOD'] = 'POST';
  244. $_SERVER['REMOTE_ADDR'] = '::1';
  245. ob_start();
  246. new Controller;
  247. $content = ob_get_contents();
  248. ob_end_clean();
  249. $response = json_decode($content, true);
  250. $this->assertEquals(0, $response['status'], 'outputs status');
  251. $this->assertTrue($this->_data->exists($response['id']), 'paste exists after posting data');
  252. $paste = $this->_data->read($response['id']);
  253. $this->assertEquals(
  254. hash_hmac('sha256', $response['id'], $paste['meta']['salt']),
  255. $response['deletetoken'],
  256. 'outputs valid delete token'
  257. );
  258. }
  259. /**
  260. * @runInSeparateProcess
  261. */
  262. public function testCreateDuplicateId()
  263. {
  264. $options = parse_ini_file(CONF, true);
  265. $options['traffic']['limit'] = 0;
  266. Helper::createIniFile(CONF, $options);
  267. $this->_data->create(Helper::getPasteId(), Helper::getPaste());
  268. $paste = Helper::getPasteJson();
  269. $file = tempnam(sys_get_temp_dir(), 'FOO');
  270. file_put_contents($file, $paste);
  271. Request::setInputStream($file);
  272. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  273. $_SERVER['REQUEST_METHOD'] = 'POST';
  274. $_SERVER['REMOTE_ADDR'] = '::1';
  275. ob_start();
  276. new Controller;
  277. $content = ob_get_contents();
  278. ob_end_clean();
  279. $response = json_decode($content, true);
  280. $this->assertEquals(1, $response['status'], 'outputs error status');
  281. $this->assertTrue($this->_data->exists(Helper::getPasteId()), 'paste exists after posting data');
  282. }
  283. /**
  284. * @runInSeparateProcess
  285. */
  286. public function testCreateValidExpire()
  287. {
  288. $options = parse_ini_file(CONF, true);
  289. $options['traffic']['limit'] = 0;
  290. Helper::createIniFile(CONF, $options);
  291. $paste = Helper::getPasteJson();
  292. $file = tempnam(sys_get_temp_dir(), 'FOO');
  293. file_put_contents($file, $paste);
  294. Request::setInputStream($file);
  295. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  296. $_SERVER['REQUEST_METHOD'] = 'POST';
  297. $_SERVER['REMOTE_ADDR'] = '::1';
  298. $time = time();
  299. ob_start();
  300. new Controller;
  301. $content = ob_get_contents();
  302. ob_end_clean();
  303. $response = json_decode($content, true);
  304. $this->assertEquals(0, $response['status'], 'outputs status');
  305. $this->assertTrue($this->_data->exists($response['id']), 'paste exists after posting data');
  306. $paste = $this->_data->read($response['id']);
  307. $this->assertEquals(
  308. hash_hmac('sha256', $response['id'], $paste['meta']['salt']),
  309. $response['deletetoken'],
  310. 'outputs valid delete token'
  311. );
  312. $this->assertGreaterThanOrEqual($time + 300, $paste['meta']['expire_date'], 'time is set correctly');
  313. }
  314. /**
  315. * @runInSeparateProcess
  316. */
  317. public function testCreateValidExpireWithDiscussion()
  318. {
  319. $options = parse_ini_file(CONF, true);
  320. $options['traffic']['limit'] = 0;
  321. Helper::createIniFile(CONF, $options);
  322. $paste = Helper::getPasteJson();
  323. $file = tempnam(sys_get_temp_dir(), 'FOO');
  324. file_put_contents($file, $paste);
  325. Request::setInputStream($file);
  326. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  327. $_SERVER['REQUEST_METHOD'] = 'POST';
  328. $_SERVER['REMOTE_ADDR'] = '::1';
  329. $time = time();
  330. ob_start();
  331. new Controller;
  332. $content = ob_get_contents();
  333. ob_end_clean();
  334. $response = json_decode($content, true);
  335. $this->assertEquals(0, $response['status'], 'outputs status');
  336. $this->assertTrue($this->_data->exists($response['id']), 'paste exists after posting data');
  337. $paste = $this->_data->read($response['id']);
  338. $this->assertEquals(
  339. hash_hmac('sha256', $response['id'], $paste['meta']['salt']),
  340. $response['deletetoken'],
  341. 'outputs valid delete token'
  342. );
  343. $this->assertGreaterThanOrEqual($time + 300, $paste['meta']['expire_date'], 'time is set correctly');
  344. $this->assertEquals(1, $paste['adata'][2], 'discussion is enabled');
  345. }
  346. /**
  347. * @runInSeparateProcess
  348. */
  349. public function testCreateInvalidExpire()
  350. {
  351. $options = parse_ini_file(CONF, true);
  352. $options['traffic']['limit'] = 0;
  353. Helper::createIniFile(CONF, $options);
  354. $paste = Helper::getPasteJson(2, array('expire' => 'foo'));
  355. $file = tempnam(sys_get_temp_dir(), 'FOO');
  356. file_put_contents($file, $paste);
  357. Request::setInputStream($file);
  358. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  359. $_SERVER['REQUEST_METHOD'] = 'POST';
  360. $_SERVER['REMOTE_ADDR'] = '::1';
  361. ob_start();
  362. new Controller;
  363. $content = ob_get_contents();
  364. ob_end_clean();
  365. $response = json_decode($content, true);
  366. $this->assertEquals(0, $response['status'], 'outputs status');
  367. $this->assertTrue($this->_data->exists($response['id']), 'paste exists after posting data');
  368. $paste = $this->_data->read($response['id']);
  369. $this->assertEquals(
  370. hash_hmac('sha256', $response['id'], $paste['meta']['salt']),
  371. $response['deletetoken'],
  372. 'outputs valid delete token'
  373. );
  374. }
  375. /**
  376. * @runInSeparateProcess
  377. */
  378. public function testCreateInvalidBurn()
  379. {
  380. $options = parse_ini_file(CONF, true);
  381. $options['traffic']['limit'] = 0;
  382. Helper::createIniFile(CONF, $options);
  383. $paste = Helper::getPastePost();
  384. $paste['adata'][3] = 'neither 1 nor 0';
  385. $file = tempnam(sys_get_temp_dir(), 'FOO');
  386. file_put_contents($file, json_encode($paste));
  387. Request::setInputStream($file);
  388. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  389. $_SERVER['REQUEST_METHOD'] = 'POST';
  390. $_SERVER['REMOTE_ADDR'] = '::1';
  391. ob_start();
  392. new Controller;
  393. $content = ob_get_contents();
  394. ob_end_clean();
  395. $response = json_decode($content, true);
  396. $this->assertEquals(1, $response['status'], 'outputs error status');
  397. $this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste exists after posting data');
  398. }
  399. /**
  400. * @runInSeparateProcess
  401. */
  402. public function testCreateInvalidOpenDiscussion()
  403. {
  404. $options = parse_ini_file(CONF, true);
  405. $options['traffic']['limit'] = 0;
  406. Helper::createIniFile(CONF, $options);
  407. $paste = Helper::getPastePost();
  408. $paste['adata'][2] = 'neither 1 nor 0';
  409. $file = tempnam(sys_get_temp_dir(), 'FOO');
  410. file_put_contents($file, json_encode($paste));
  411. Request::setInputStream($file);
  412. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  413. $_SERVER['REQUEST_METHOD'] = 'POST';
  414. $_SERVER['REMOTE_ADDR'] = '::1';
  415. ob_start();
  416. new Controller;
  417. $content = ob_get_contents();
  418. ob_end_clean();
  419. $response = json_decode($content, true);
  420. $this->assertEquals(1, $response['status'], 'outputs error status');
  421. $this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste exists after posting data');
  422. }
  423. /**
  424. * In some webserver setups (found with Suhosin) overly long POST params are
  425. * silently removed, check that this case is handled
  426. *
  427. * @runInSeparateProcess
  428. */
  429. public function testCreateBrokenUpload()
  430. {
  431. $paste = substr(Helper::getPasteJson(), 0, -10);
  432. $file = tempnam(sys_get_temp_dir(), 'FOO');
  433. file_put_contents($file, $paste);
  434. Request::setInputStream($file);
  435. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  436. $_SERVER['REQUEST_METHOD'] = 'POST';
  437. $_SERVER['REMOTE_ADDR'] = '::1';
  438. $this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste does not exists before posting data');
  439. $this->expectException(Exception::class);
  440. $this->expectExceptionCode(90);
  441. new Controller;
  442. $this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste exists after posting data');
  443. }
  444. /**
  445. * @runInSeparateProcess
  446. */
  447. public function testCreateTooSoon()
  448. {
  449. $paste = Helper::getPasteJson();
  450. $file = tempnam(sys_get_temp_dir(), 'FOO');
  451. file_put_contents($file, $paste);
  452. Request::setInputStream($file);
  453. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  454. $_SERVER['REQUEST_METHOD'] = 'POST';
  455. $_SERVER['REMOTE_ADDR'] = '::1';
  456. ob_start();
  457. new Controller;
  458. ob_end_clean();
  459. $this->_data->delete(Helper::getPasteId());
  460. ob_start();
  461. new Controller;
  462. $content = ob_get_contents();
  463. ob_end_clean();
  464. $response = json_decode($content, true);
  465. $this->assertEquals(1, $response['status'], 'outputs error status');
  466. $this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste exists after posting data');
  467. }
  468. /**
  469. * @runInSeparateProcess
  470. */
  471. public function testCreateComment()
  472. {
  473. $options = parse_ini_file(CONF, true);
  474. $options['traffic']['limit'] = 0;
  475. Helper::createIniFile(CONF, $options);
  476. $comment = Helper::getCommentJson();
  477. $file = tempnam(sys_get_temp_dir(), 'FOO');
  478. file_put_contents($file, $comment);
  479. Request::setInputStream($file);
  480. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  481. $_SERVER['REQUEST_METHOD'] = 'POST';
  482. $_SERVER['REMOTE_ADDR'] = '::1';
  483. $this->_data->create(Helper::getPasteId(), Helper::getPaste());
  484. ob_start();
  485. new Controller;
  486. $content = ob_get_contents();
  487. ob_end_clean();
  488. $response = json_decode($content, true);
  489. $this->assertEquals(0, $response['status'], 'outputs status');
  490. $this->assertTrue($this->_data->existsComment(Helper::getPasteId(), Helper::getPasteId(), $response['id']), 'paste exists after posting data');
  491. }
  492. /**
  493. * @runInSeparateProcess
  494. */
  495. public function testCreateInvalidComment()
  496. {
  497. $options = parse_ini_file(CONF, true);
  498. $options['traffic']['limit'] = 0;
  499. Helper::createIniFile(CONF, $options);
  500. $comment = Helper::getCommentPost();
  501. $comment['parentid'] = 'foo';
  502. $file = tempnam(sys_get_temp_dir(), 'FOO');
  503. file_put_contents($file, json_encode($comment));
  504. Request::setInputStream($file);
  505. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  506. $_SERVER['REQUEST_METHOD'] = 'POST';
  507. $_SERVER['REMOTE_ADDR'] = '::1';
  508. $this->_data->create(Helper::getPasteId(), Helper::getPaste());
  509. ob_start();
  510. new Controller;
  511. $content = ob_get_contents();
  512. ob_end_clean();
  513. $response = json_decode($content, true);
  514. $this->assertEquals(1, $response['status'], 'outputs error status');
  515. $this->assertFalse($this->_data->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'paste exists after posting data');
  516. }
  517. /**
  518. * @runInSeparateProcess
  519. */
  520. public function testCreateCommentDiscussionDisabled()
  521. {
  522. $options = parse_ini_file(CONF, true);
  523. $options['traffic']['limit'] = 0;
  524. Helper::createIniFile(CONF, $options);
  525. $comment = Helper::getCommentJson();
  526. $file = tempnam(sys_get_temp_dir(), 'FOO');
  527. file_put_contents($file, $comment);
  528. Request::setInputStream($file);
  529. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  530. $_SERVER['REQUEST_METHOD'] = 'POST';
  531. $_SERVER['REMOTE_ADDR'] = '::1';
  532. $paste = Helper::getPaste();
  533. $paste['adata'][2] = 0;
  534. $this->_data->create(Helper::getPasteId(), $paste);
  535. ob_start();
  536. new Controller;
  537. $content = ob_get_contents();
  538. ob_end_clean();
  539. $response = json_decode($content, true);
  540. $this->assertEquals(1, $response['status'], 'outputs error status');
  541. $this->assertFalse($this->_data->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'paste exists after posting data');
  542. }
  543. /**
  544. * @runInSeparateProcess
  545. */
  546. public function testCreateCommentInvalidPaste()
  547. {
  548. $options = parse_ini_file(CONF, true);
  549. $options['traffic']['limit'] = 0;
  550. Helper::createIniFile(CONF, $options);
  551. $comment = Helper::getCommentJson();
  552. $file = tempnam(sys_get_temp_dir(), 'FOO');
  553. file_put_contents($file, $comment);
  554. Request::setInputStream($file);
  555. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  556. $_SERVER['REQUEST_METHOD'] = 'POST';
  557. $_SERVER['REMOTE_ADDR'] = '::1';
  558. ob_start();
  559. new Controller;
  560. $content = ob_get_contents();
  561. ob_end_clean();
  562. $response = json_decode($content, true);
  563. $this->assertEquals(1, $response['status'], 'outputs error status');
  564. $this->assertFalse($this->_data->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'paste exists after posting data');
  565. }
  566. /**
  567. * @runInSeparateProcess
  568. */
  569. public function testCreateDuplicateComment()
  570. {
  571. $options = parse_ini_file(CONF, true);
  572. $options['traffic']['limit'] = 0;
  573. Helper::createIniFile(CONF, $options);
  574. $this->_data->create(Helper::getPasteId(), Helper::getPaste());
  575. $this->_data->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getPasteId(), Helper::getComment());
  576. $this->assertTrue($this->_data->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getPasteId()), 'comment exists before posting data');
  577. $comment = Helper::getCommentJson();
  578. $file = tempnam(sys_get_temp_dir(), 'FOO');
  579. file_put_contents($file, $comment);
  580. Request::setInputStream($file);
  581. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  582. $_SERVER['REQUEST_METHOD'] = 'POST';
  583. $_SERVER['REMOTE_ADDR'] = '::1';
  584. ob_start();
  585. new Controller;
  586. $content = ob_get_contents();
  587. ob_end_clean();
  588. $response = json_decode($content, true);
  589. $this->assertEquals(1, $response['status'], 'outputs error status');
  590. $this->assertTrue($this->_data->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getPasteId()), 'paste exists after posting data');
  591. }
  592. /**
  593. * @runInSeparateProcess
  594. */
  595. public function testReadInvalidId()
  596. {
  597. $_SERVER['QUERY_STRING'] = 'foo';
  598. $_GET['foo'] = '';
  599. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  600. ob_start();
  601. new Controller;
  602. $content = ob_get_contents();
  603. ob_end_clean();
  604. $response = json_decode($content, true);
  605. $this->assertEquals(1, $response['status'], 'outputs error status');
  606. $this->assertEquals('Invalid paste ID.', $response['message'], 'outputs error message');
  607. }
  608. /**
  609. * @runInSeparateProcess
  610. */
  611. public function testReadNonexisting()
  612. {
  613. $_SERVER['QUERY_STRING'] = Helper::getPasteId();
  614. $_GET[Helper::getPasteId()] = '';
  615. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  616. ob_start();
  617. new Controller;
  618. $content = ob_get_contents();
  619. ob_end_clean();
  620. $response = json_decode($content, true);
  621. $this->assertEquals(1, $response['status'], 'outputs error status');
  622. $this->assertEquals('Paste does not exist, has expired or has been deleted.', $response['message'], 'outputs error message');
  623. }
  624. /**
  625. * @runInSeparateProcess
  626. */
  627. public function testReadExpired()
  628. {
  629. $expiredPaste = Helper::getPaste(2, array('expire_date' => 1344803344));
  630. $this->_data->create(Helper::getPasteId(), $expiredPaste);
  631. $_SERVER['QUERY_STRING'] = Helper::getPasteId();
  632. $_GET[Helper::getPasteId()] = '';
  633. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  634. ob_start();
  635. new Controller;
  636. $content = ob_get_contents();
  637. ob_end_clean();
  638. $response = json_decode($content, true);
  639. $this->assertEquals(1, $response['status'], 'outputs error status');
  640. $this->assertEquals('Paste does not exist, has expired or has been deleted.', $response['message'], 'outputs error message');
  641. }
  642. /**
  643. * @runInSeparateProcess
  644. */
  645. public function testReadBurn()
  646. {
  647. $paste = Helper::getPaste();
  648. $paste['adata'][3] = 1;
  649. $this->_data->create(Helper::getPasteId(), $paste);
  650. $_SERVER['QUERY_STRING'] = Helper::getPasteId();
  651. $_GET[Helper::getPasteId()] = '';
  652. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  653. ob_start();
  654. new Controller;
  655. $content = ob_get_contents();
  656. ob_end_clean();
  657. $response = json_decode($content, true);
  658. $this->assertEquals(0, $response['status'], 'outputs success status');
  659. $this->assertEquals(Helper::getPasteId(), $response['id'], 'outputs data correctly');
  660. $this->assertStringEndsWith('?' . $response['id'], $response['url'], 'returned URL points to new paste');
  661. $this->assertEquals($paste['ct'], $response['ct'], 'outputs ct correctly');
  662. $this->assertEquals($paste['adata'][1], $response['adata'][1], 'outputs formatter correctly');
  663. $this->assertEquals($paste['adata'][2], $response['adata'][2], 'outputs opendiscussion correctly');
  664. $this->assertEquals($paste['adata'][3], $response['adata'][3], 'outputs burnafterreading correctly');
  665. $this->assertEquals($paste['meta']['created'], $response['meta']['created'], 'outputs created correctly');
  666. $this->assertEquals(0, $response['comment_count'], 'outputs comment_count correctly');
  667. $this->assertEquals(0, $response['comment_offset'], 'outputs comment_offset correctly');
  668. // by default it will be deleted instantly after it is read
  669. $this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste exists after reading');
  670. }
  671. /**
  672. * @runInSeparateProcess
  673. */
  674. public function testReadJson()
  675. {
  676. $paste = Helper::getPaste();
  677. $this->_data->create(Helper::getPasteId(), $paste);
  678. $_SERVER['QUERY_STRING'] = Helper::getPasteId();
  679. $_GET[Helper::getPasteId()] = '';
  680. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  681. ob_start();
  682. new Controller;
  683. $content = ob_get_contents();
  684. ob_end_clean();
  685. $response = json_decode($content, true);
  686. $this->assertEquals(0, $response['status'], 'outputs success status');
  687. $this->assertEquals(Helper::getPasteId(), $response['id'], 'outputs data correctly');
  688. $this->assertStringEndsWith('?' . $response['id'], $response['url'], 'returned URL points to new paste');
  689. $this->assertEquals($paste['ct'], $response['ct'], 'outputs ct correctly');
  690. $this->assertEquals($paste['adata'][1], $response['adata'][1], 'outputs formatter correctly');
  691. $this->assertEquals($paste['adata'][2], $response['adata'][2], 'outputs opendiscussion correctly');
  692. $this->assertEquals($paste['adata'][3], $response['adata'][3], 'outputs burnafterreading correctly');
  693. $this->assertEquals($paste['meta']['created'], $response['meta']['created'], 'outputs created correctly');
  694. $this->assertEquals(0, $response['comment_count'], 'outputs comment_count correctly');
  695. $this->assertEquals(0, $response['comment_offset'], 'outputs comment_offset correctly');
  696. }
  697. /**
  698. * @runInSeparateProcess
  699. */
  700. public function testReadOldSyntax()
  701. {
  702. $paste = Helper::getPaste(1);
  703. $paste['meta'] = array(
  704. 'syntaxcoloring' => true,
  705. 'postdate' => $paste['meta']['postdate'],
  706. 'opendiscussion' => $paste['meta']['opendiscussion'],
  707. );
  708. $this->_data->create(Helper::getPasteId(), $paste);
  709. $_SERVER['QUERY_STRING'] = Helper::getPasteId();
  710. $_GET[Helper::getPasteId()] = '';
  711. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  712. ob_start();
  713. new Controller;
  714. $content = ob_get_contents();
  715. ob_end_clean();
  716. $response = json_decode($content, true);
  717. $this->assertEquals(0, $response['status'], 'outputs success status');
  718. $this->assertEquals(Helper::getPasteId(), $response['id'], 'outputs data correctly');
  719. $this->assertStringEndsWith('?' . $response['id'], $response['url'], 'returned URL points to new paste');
  720. $this->assertEquals($paste['data'], $response['data'], 'outputs data correctly');
  721. $this->assertEquals('syntaxhighlighting', $response['meta']['formatter'], 'outputs format correctly');
  722. $this->assertEquals($paste['meta']['postdate'], $response['meta']['postdate'], 'outputs postdate correctly');
  723. $this->assertEquals($paste['meta']['opendiscussion'], $response['meta']['opendiscussion'], 'outputs opendiscussion correctly');
  724. $this->assertEquals(0, $response['comment_count'], 'outputs comment_count correctly');
  725. $this->assertEquals(0, $response['comment_offset'], 'outputs comment_offset correctly');
  726. }
  727. /**
  728. * @runInSeparateProcess
  729. */
  730. public function testReadBurnAfterReading()
  731. {
  732. $burnPaste = Helper::getPaste();
  733. $burnPaste['adata'][3] = 1;
  734. $this->_data->create(Helper::getPasteId(), $burnPaste);
  735. $this->assertTrue($this->_data->exists(Helper::getPasteId()), 'paste exists before deleting data');
  736. $_SERVER['QUERY_STRING'] = Helper::getPasteId();
  737. $_GET[Helper::getPasteId()] = '';
  738. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  739. ob_start();
  740. new Controller;
  741. $content = ob_get_contents();
  742. ob_end_clean();
  743. $response = json_decode($content, true);
  744. $this->assertEquals(0, $response['status'], 'outputs status');
  745. $this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste successfully deleted');
  746. }
  747. /**
  748. * @runInSeparateProcess
  749. */
  750. public function testDelete()
  751. {
  752. $this->_data->create(Helper::getPasteId(), Helper::getPaste());
  753. $this->assertTrue($this->_data->exists(Helper::getPasteId()), 'paste exists before deleting data');
  754. $paste = $this->_data->read(Helper::getPasteId());
  755. $_GET['pasteid'] = Helper::getPasteId();
  756. $_GET['deletetoken'] = hash_hmac('sha256', Helper::getPasteId(), $paste['meta']['salt']);
  757. ob_start();
  758. new Controller;
  759. $content = ob_get_contents();
  760. ob_end_clean();
  761. $this->assertMatchesRegularExpression(
  762. '#<div[^>]*id="status"[^>]*>.*Paste was properly deleted\.#s',
  763. $content,
  764. 'outputs deleted status correctly'
  765. );
  766. $this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste successfully deleted');
  767. }
  768. /**
  769. * @runInSeparateProcess
  770. */
  771. public function testDeleteInvalidId()
  772. {
  773. $this->_data->create(Helper::getPasteId(), Helper::getPaste());
  774. $_GET['pasteid'] = 'foo';
  775. $_GET['deletetoken'] = 'bar';
  776. ob_start();
  777. new Controller;
  778. $content = ob_get_contents();
  779. ob_end_clean();
  780. $this->assertMatchesRegularExpression(
  781. '#<div[^>]*id="errormessage"[^>]*>.*Invalid paste ID\.#s',
  782. $content,
  783. 'outputs delete error correctly'
  784. );
  785. $this->assertTrue($this->_data->exists(Helper::getPasteId()), 'paste exists after failing to delete data');
  786. }
  787. /**
  788. * @runInSeparateProcess
  789. */
  790. public function testDeleteInexistantId()
  791. {
  792. $_GET['pasteid'] = Helper::getPasteId();
  793. $_GET['deletetoken'] = 'bar';
  794. ob_start();
  795. new Controller;
  796. $content = ob_get_contents();
  797. ob_end_clean();
  798. $this->assertMatchesRegularExpression(
  799. '#<div[^>]*id="errormessage"[^>]*>.*Paste does not exist, has expired or has been deleted\.#s',
  800. $content,
  801. 'outputs delete error correctly'
  802. );
  803. }
  804. /**
  805. * @runInSeparateProcess
  806. */
  807. public function testDeleteInvalidToken()
  808. {
  809. $this->_data->create(Helper::getPasteId(), Helper::getPaste());
  810. $_GET['pasteid'] = Helper::getPasteId();
  811. $_GET['deletetoken'] = 'bar';
  812. ob_start();
  813. new Controller;
  814. $content = ob_get_contents();
  815. ob_end_clean();
  816. $this->assertMatchesRegularExpression(
  817. '#<div[^>]*id="errormessage"[^>]*>.*Wrong deletion token\. Paste was not deleted\.#s',
  818. $content,
  819. 'outputs delete error correctly'
  820. );
  821. $this->assertTrue($this->_data->exists(Helper::getPasteId()), 'paste exists after failing to delete data');
  822. }
  823. /**
  824. * @runInSeparateProcess
  825. */
  826. public function testDeleteInvalidBurnAfterReading()
  827. {
  828. $this->_data->create(Helper::getPasteId(), Helper::getPaste());
  829. $this->assertTrue($this->_data->exists(Helper::getPasteId()), 'paste exists before deleting data');
  830. $file = tempnam(sys_get_temp_dir(), 'FOO');
  831. file_put_contents($file, json_encode(array(
  832. 'deletetoken' => 'burnafterreading',
  833. )));
  834. Request::setInputStream($file);
  835. $_SERVER['QUERY_STRING'] = Helper::getPasteId();
  836. $_GET[Helper::getPasteId()] = '';
  837. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  838. $_SERVER['REQUEST_METHOD'] = 'POST';
  839. ob_start();
  840. new Controller;
  841. $content = ob_get_contents();
  842. ob_end_clean();
  843. $response = json_decode($content, true);
  844. $this->assertEquals(1, $response['status'], 'outputs status');
  845. $this->assertTrue($this->_data->exists(Helper::getPasteId()), 'paste exists after failing to delete data');
  846. }
  847. /**
  848. * @runInSeparateProcess
  849. */
  850. public function testDeleteExpired()
  851. {
  852. $expiredPaste = Helper::getPaste(2, array('expire_date' => 1000));
  853. $this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste does not exist before being created');
  854. $this->_data->create(Helper::getPasteId(), $expiredPaste);
  855. $this->assertTrue($this->_data->exists(Helper::getPasteId()), 'paste exists before deleting data');
  856. $_GET['pasteid'] = Helper::getPasteId();
  857. $_GET['deletetoken'] = 'does not matter in this context, but has to be set';
  858. ob_start();
  859. new Controller;
  860. $content = ob_get_contents();
  861. ob_end_clean();
  862. $this->assertMatchesRegularExpression(
  863. '#<div[^>]*id="errormessage"[^>]*>.*Paste does not exist, has expired or has been deleted\.#s',
  864. $content,
  865. 'outputs error correctly'
  866. );
  867. $this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste successfully deleted');
  868. }
  869. /**
  870. * @runInSeparateProcess
  871. */
  872. public function testDeleteMissingPerPasteSalt()
  873. {
  874. $paste = Helper::getPaste();
  875. unset($paste['meta']['salt']);
  876. $this->_data->create(Helper::getPasteId(), $paste);
  877. $this->assertTrue($this->_data->exists(Helper::getPasteId()), 'paste exists before deleting data');
  878. $_GET['pasteid'] = Helper::getPasteId();
  879. $_GET['deletetoken'] = hash_hmac('sha256', Helper::getPasteId(), ServerSalt::get());
  880. ob_start();
  881. new Controller;
  882. $content = ob_get_contents();
  883. ob_end_clean();
  884. $this->assertMatchesRegularExpression(
  885. '#<div[^>]*id="status"[^>]*>.*Paste was properly deleted\.#s',
  886. $content,
  887. 'outputs deleted status correctly'
  888. );
  889. $this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste successfully deleted');
  890. }
  891. }