ControllerTest.php 36 KB

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