1
0

ControllerTest.php 36 KB

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