ControllerTest.php 40 KB

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