ControllerTest.php 37 KB

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