ControllerTest.php 36 KB

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