ControllerTest.php 36 KB

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