ControllerTest.php 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937
  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 testCreateInvalidExpire()
  351. {
  352. $options = parse_ini_file(CONF, true);
  353. $options['traffic']['limit'] = 0;
  354. Helper::createIniFile(CONF, $options);
  355. $paste = Helper::getPasteJson(2, array('expire' => 'foo'));
  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(0, $response['status'], 'outputs status');
  368. $this->assertTrue($this->_data->exists($response['id']), 'paste exists after posting data');
  369. $paste = $this->_data->read($response['id']);
  370. $this->assertEquals(
  371. hash_hmac('sha256', $response['id'], $paste['meta']['salt']),
  372. $response['deletetoken'],
  373. 'outputs valid delete token'
  374. );
  375. }
  376. /**
  377. * @runInSeparateProcess
  378. */
  379. public function testCreateInvalidBurn()
  380. {
  381. $options = parse_ini_file(CONF, true);
  382. $options['traffic']['limit'] = 0;
  383. Helper::createIniFile(CONF, $options);
  384. $paste = Helper::getPastePost();
  385. $paste['adata'][3] = 'neither 1 nor 0';
  386. $file = tempnam(sys_get_temp_dir(), 'FOO');
  387. file_put_contents($file, json_encode($paste));
  388. Request::setInputStream($file);
  389. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  390. $_SERVER['REQUEST_METHOD'] = 'POST';
  391. $_SERVER['REMOTE_ADDR'] = '::1';
  392. ob_start();
  393. new Controller;
  394. $content = ob_get_contents();
  395. ob_end_clean();
  396. $response = json_decode($content, true);
  397. $this->assertEquals(1, $response['status'], 'outputs error status');
  398. $this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste exists after posting data');
  399. }
  400. /**
  401. * @runInSeparateProcess
  402. */
  403. public function testCreateInvalidOpenDiscussion()
  404. {
  405. $options = parse_ini_file(CONF, true);
  406. $options['traffic']['limit'] = 0;
  407. Helper::createIniFile(CONF, $options);
  408. $paste = Helper::getPastePost();
  409. $paste['adata'][2] = 'neither 1 nor 0';
  410. $file = tempnam(sys_get_temp_dir(), 'FOO');
  411. file_put_contents($file, json_encode($paste));
  412. Request::setInputStream($file);
  413. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  414. $_SERVER['REQUEST_METHOD'] = 'POST';
  415. $_SERVER['REMOTE_ADDR'] = '::1';
  416. ob_start();
  417. new Controller;
  418. $content = ob_get_contents();
  419. ob_end_clean();
  420. $response = json_decode($content, true);
  421. $this->assertEquals(1, $response['status'], 'outputs error status');
  422. $this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste exists after posting data');
  423. }
  424. /**
  425. * In some webserver setups (found with Suhosin) overly long POST params are
  426. * silently removed, check that this case is handled
  427. *
  428. * @runInSeparateProcess
  429. * @expectedException Exception
  430. * @expectedExceptionCode 90
  431. */
  432. public function testCreateBrokenUpload()
  433. {
  434. $paste = substr(Helper::getPasteJson(), 0, -10);
  435. $file = tempnam(sys_get_temp_dir(), 'FOO');
  436. file_put_contents($file, $paste);
  437. Request::setInputStream($file);
  438. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  439. $_SERVER['REQUEST_METHOD'] = 'POST';
  440. $_SERVER['REMOTE_ADDR'] = '::1';
  441. $this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste does not exists before posting data');
  442. new Controller;
  443. $this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste exists after posting data');
  444. }
  445. /**
  446. * @runInSeparateProcess
  447. */
  448. public function testCreateTooSoon()
  449. {
  450. $paste = Helper::getPasteJson();
  451. $file = tempnam(sys_get_temp_dir(), 'FOO');
  452. file_put_contents($file, $paste);
  453. Request::setInputStream($file);
  454. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  455. $_SERVER['REQUEST_METHOD'] = 'POST';
  456. $_SERVER['REMOTE_ADDR'] = '::1';
  457. ob_start();
  458. new Controller;
  459. ob_end_clean();
  460. $this->_data->delete(Helper::getPasteId());
  461. ob_start();
  462. new Controller;
  463. $content = ob_get_contents();
  464. ob_end_clean();
  465. $response = json_decode($content, true);
  466. $this->assertEquals(1, $response['status'], 'outputs error status');
  467. $this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste exists after posting data');
  468. }
  469. /**
  470. * @runInSeparateProcess
  471. */
  472. public function testCreateComment()
  473. {
  474. $options = parse_ini_file(CONF, true);
  475. $options['traffic']['limit'] = 0;
  476. Helper::createIniFile(CONF, $options);
  477. $comment = Helper::getCommentJson();
  478. $file = tempnam(sys_get_temp_dir(), 'FOO');
  479. file_put_contents($file, $comment);
  480. Request::setInputStream($file);
  481. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  482. $_SERVER['REQUEST_METHOD'] = 'POST';
  483. $_SERVER['REMOTE_ADDR'] = '::1';
  484. $this->_data->create(Helper::getPasteId(), Helper::getPaste());
  485. ob_start();
  486. new Controller;
  487. $content = ob_get_contents();
  488. ob_end_clean();
  489. $response = json_decode($content, true);
  490. $this->assertEquals(0, $response['status'], 'outputs status');
  491. $this->assertTrue($this->_data->existsComment(Helper::getPasteId(), Helper::getPasteId(), $response['id']), 'paste exists after posting data');
  492. }
  493. /**
  494. * @runInSeparateProcess
  495. */
  496. public function testCreateInvalidComment()
  497. {
  498. $options = parse_ini_file(CONF, true);
  499. $options['traffic']['limit'] = 0;
  500. Helper::createIniFile(CONF, $options);
  501. $comment = Helper::getCommentPost();
  502. $comment['parentid'] = 'foo';
  503. $file = tempnam(sys_get_temp_dir(), 'FOO');
  504. file_put_contents($file, json_encode($comment));
  505. Request::setInputStream($file);
  506. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  507. $_SERVER['REQUEST_METHOD'] = 'POST';
  508. $_SERVER['REMOTE_ADDR'] = '::1';
  509. $this->_data->create(Helper::getPasteId(), Helper::getPaste());
  510. ob_start();
  511. new Controller;
  512. $content = ob_get_contents();
  513. ob_end_clean();
  514. $response = json_decode($content, true);
  515. $this->assertEquals(1, $response['status'], 'outputs error status');
  516. $this->assertFalse($this->_data->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'paste exists after posting data');
  517. }
  518. /**
  519. * @runInSeparateProcess
  520. */
  521. public function testCreateCommentDiscussionDisabled()
  522. {
  523. $options = parse_ini_file(CONF, true);
  524. $options['traffic']['limit'] = 0;
  525. Helper::createIniFile(CONF, $options);
  526. $comment = Helper::getCommentJson();
  527. $file = tempnam(sys_get_temp_dir(), 'FOO');
  528. file_put_contents($file, $comment);
  529. Request::setInputStream($file);
  530. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  531. $_SERVER['REQUEST_METHOD'] = 'POST';
  532. $_SERVER['REMOTE_ADDR'] = '::1';
  533. $paste = Helper::getPaste();
  534. $paste['adata'][2] = 0;
  535. $this->_data->create(Helper::getPasteId(), $paste);
  536. ob_start();
  537. new Controller;
  538. $content = ob_get_contents();
  539. ob_end_clean();
  540. $response = json_decode($content, true);
  541. $this->assertEquals(1, $response['status'], 'outputs error status');
  542. $this->assertFalse($this->_data->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'paste exists after posting data');
  543. }
  544. /**
  545. * @runInSeparateProcess
  546. */
  547. public function testCreateCommentInvalidPaste()
  548. {
  549. $options = parse_ini_file(CONF, true);
  550. $options['traffic']['limit'] = 0;
  551. Helper::createIniFile(CONF, $options);
  552. $comment = Helper::getCommentJson();
  553. $file = tempnam(sys_get_temp_dir(), 'FOO');
  554. file_put_contents($file, $comment);
  555. Request::setInputStream($file);
  556. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  557. $_SERVER['REQUEST_METHOD'] = 'POST';
  558. $_SERVER['REMOTE_ADDR'] = '::1';
  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 testCreateDuplicateComment()
  571. {
  572. $options = parse_ini_file(CONF, true);
  573. $options['traffic']['limit'] = 0;
  574. Helper::createIniFile(CONF, $options);
  575. $this->_data->create(Helper::getPasteId(), Helper::getPaste());
  576. $this->_data->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getPasteId(), Helper::getComment());
  577. $this->assertTrue($this->_data->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getPasteId()), 'comment exists before posting data');
  578. $comment = Helper::getCommentJson();
  579. $file = tempnam(sys_get_temp_dir(), 'FOO');
  580. file_put_contents($file, $comment);
  581. Request::setInputStream($file);
  582. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  583. $_SERVER['REQUEST_METHOD'] = 'POST';
  584. $_SERVER['REMOTE_ADDR'] = '::1';
  585. ob_start();
  586. new Controller;
  587. $content = ob_get_contents();
  588. ob_end_clean();
  589. $response = json_decode($content, true);
  590. $this->assertEquals(1, $response['status'], 'outputs error status');
  591. $this->assertTrue($this->_data->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getPasteId()), 'paste exists after posting data');
  592. }
  593. /**
  594. * @runInSeparateProcess
  595. */
  596. public function testReadInvalidId()
  597. {
  598. $_SERVER['QUERY_STRING'] = 'foo';
  599. $_GET['foo'] = '';
  600. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  601. ob_start();
  602. new Controller;
  603. $content = ob_get_contents();
  604. ob_end_clean();
  605. $response = json_decode($content, true);
  606. $this->assertEquals(1, $response['status'], 'outputs error status');
  607. $this->assertEquals('Invalid paste ID.', $response['message'], 'outputs error message');
  608. }
  609. /**
  610. * @runInSeparateProcess
  611. */
  612. public function testReadNonexisting()
  613. {
  614. $_SERVER['QUERY_STRING'] = Helper::getPasteId();
  615. $_GET[Helper::getPasteId()] = '';
  616. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  617. ob_start();
  618. new Controller;
  619. $content = ob_get_contents();
  620. ob_end_clean();
  621. $response = json_decode($content, true);
  622. $this->assertEquals(1, $response['status'], 'outputs error status');
  623. $this->assertEquals('Paste does not exist, has expired or has been deleted.', $response['message'], 'outputs error message');
  624. }
  625. /**
  626. * @runInSeparateProcess
  627. */
  628. public function testReadExpired()
  629. {
  630. $expiredPaste = Helper::getPaste(2, array('expire_date' => 1344803344));
  631. $this->_data->create(Helper::getPasteId(), $expiredPaste);
  632. $_SERVER['QUERY_STRING'] = Helper::getPasteId();
  633. $_GET[Helper::getPasteId()] = '';
  634. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  635. ob_start();
  636. new Controller;
  637. $content = ob_get_contents();
  638. ob_end_clean();
  639. $response = json_decode($content, true);
  640. $this->assertEquals(1, $response['status'], 'outputs error status');
  641. $this->assertEquals('Paste does not exist, has expired or has been deleted.', $response['message'], 'outputs error message');
  642. }
  643. /**
  644. * @runInSeparateProcess
  645. */
  646. public function testReadBurn()
  647. {
  648. $paste = Helper::getPaste();
  649. $paste['adata'][3] = 1;
  650. $this->_data->create(Helper::getPasteId(), $paste);
  651. $_SERVER['QUERY_STRING'] = Helper::getPasteId();
  652. $_GET[Helper::getPasteId()] = '';
  653. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  654. ob_start();
  655. new Controller;
  656. $content = ob_get_contents();
  657. ob_end_clean();
  658. $response = json_decode($content, true);
  659. $this->assertEquals(0, $response['status'], 'outputs success status');
  660. $this->assertEquals(Helper::getPasteId(), $response['id'], 'outputs data correctly');
  661. $this->assertStringEndsWith('?' . $response['id'], $response['url'], 'returned URL points to new paste');
  662. $this->assertEquals($paste['ct'], $response['ct'], 'outputs ct correctly');
  663. $this->assertEquals($paste['adata'][1], $response['adata'][1], 'outputs formatter correctly');
  664. $this->assertEquals($paste['adata'][2], $response['adata'][2], 'outputs opendiscussion correctly');
  665. $this->assertEquals($paste['adata'][3], $response['adata'][3], 'outputs burnafterreading correctly');
  666. $this->assertEquals($paste['meta']['created'], $response['meta']['created'], 'outputs created correctly');
  667. $this->assertEquals(0, $response['comment_count'], 'outputs comment_count correctly');
  668. $this->assertEquals(0, $response['comment_offset'], 'outputs comment_offset correctly');
  669. // by default it will be deleted instantly after it is read
  670. $this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste exists after reading');
  671. }
  672. /**
  673. * @runInSeparateProcess
  674. */
  675. public function testReadJson()
  676. {
  677. $paste = Helper::getPaste();
  678. $this->_data->create(Helper::getPasteId(), $paste);
  679. $_SERVER['QUERY_STRING'] = Helper::getPasteId();
  680. $_GET[Helper::getPasteId()] = '';
  681. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  682. ob_start();
  683. new Controller;
  684. $content = ob_get_contents();
  685. ob_end_clean();
  686. $response = json_decode($content, true);
  687. $this->assertEquals(0, $response['status'], 'outputs success status');
  688. $this->assertEquals(Helper::getPasteId(), $response['id'], 'outputs data correctly');
  689. $this->assertStringEndsWith('?' . $response['id'], $response['url'], 'returned URL points to new paste');
  690. $this->assertEquals($paste['ct'], $response['ct'], 'outputs ct correctly');
  691. $this->assertEquals($paste['adata'][1], $response['adata'][1], 'outputs formatter correctly');
  692. $this->assertEquals($paste['adata'][2], $response['adata'][2], 'outputs opendiscussion correctly');
  693. $this->assertEquals($paste['adata'][3], $response['adata'][3], 'outputs burnafterreading correctly');
  694. $this->assertEquals($paste['meta']['created'], $response['meta']['created'], 'outputs created correctly');
  695. $this->assertEquals(0, $response['comment_count'], 'outputs comment_count correctly');
  696. $this->assertEquals(0, $response['comment_offset'], 'outputs comment_offset correctly');
  697. }
  698. /**
  699. * @runInSeparateProcess
  700. */
  701. public function testReadOldSyntax()
  702. {
  703. $paste = Helper::getPaste(1);
  704. $paste['meta'] = array(
  705. 'syntaxcoloring' => true,
  706. 'postdate' => $paste['meta']['postdate'],
  707. 'opendiscussion' => $paste['meta']['opendiscussion'],
  708. );
  709. $this->_data->create(Helper::getPasteId(), $paste);
  710. $_SERVER['QUERY_STRING'] = Helper::getPasteId();
  711. $_GET[Helper::getPasteId()] = '';
  712. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  713. ob_start();
  714. new Controller;
  715. $content = ob_get_contents();
  716. ob_end_clean();
  717. $response = json_decode($content, true);
  718. $this->assertEquals(0, $response['status'], 'outputs success status');
  719. $this->assertEquals(Helper::getPasteId(), $response['id'], 'outputs data correctly');
  720. $this->assertStringEndsWith('?' . $response['id'], $response['url'], 'returned URL points to new paste');
  721. $this->assertEquals($paste['data'], $response['data'], 'outputs data correctly');
  722. $this->assertEquals('syntaxhighlighting', $response['meta']['formatter'], 'outputs format correctly');
  723. $this->assertEquals($paste['meta']['postdate'], $response['meta']['postdate'], 'outputs postdate correctly');
  724. $this->assertEquals($paste['meta']['opendiscussion'], $response['meta']['opendiscussion'], 'outputs opendiscussion correctly');
  725. $this->assertEquals(0, $response['comment_count'], 'outputs comment_count correctly');
  726. $this->assertEquals(0, $response['comment_offset'], 'outputs comment_offset correctly');
  727. }
  728. /**
  729. * @runInSeparateProcess
  730. */
  731. public function testReadBurnAfterReading()
  732. {
  733. $burnPaste = Helper::getPaste();
  734. $burnPaste['adata'][3] = 1;
  735. $this->_data->create(Helper::getPasteId(), $burnPaste);
  736. $this->assertTrue($this->_data->exists(Helper::getPasteId()), 'paste exists before deleting data');
  737. $_SERVER['QUERY_STRING'] = Helper::getPasteId();
  738. $_GET[Helper::getPasteId()] = '';
  739. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  740. ob_start();
  741. new Controller;
  742. $content = ob_get_contents();
  743. ob_end_clean();
  744. $response = json_decode($content, true);
  745. $this->assertEquals(0, $response['status'], 'outputs status');
  746. $this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste successfully deleted');
  747. }
  748. /**
  749. * @runInSeparateProcess
  750. */
  751. public function testDelete()
  752. {
  753. $this->_data->create(Helper::getPasteId(), Helper::getPaste());
  754. $this->assertTrue($this->_data->exists(Helper::getPasteId()), 'paste exists before deleting data');
  755. $paste = $this->_data->read(Helper::getPasteId());
  756. $_GET['pasteid'] = Helper::getPasteId();
  757. $_GET['deletetoken'] = hash_hmac('sha256', Helper::getPasteId(), $paste['meta']['salt']);
  758. ob_start();
  759. new Controller;
  760. $content = ob_get_contents();
  761. ob_end_clean();
  762. $this->assertRegExp(
  763. '#<div[^>]*id="status"[^>]*>.*Paste was properly deleted\.#s',
  764. $content,
  765. 'outputs deleted status correctly'
  766. );
  767. $this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste successfully deleted');
  768. }
  769. /**
  770. * @runInSeparateProcess
  771. */
  772. public function testDeleteInvalidId()
  773. {
  774. $this->_data->create(Helper::getPasteId(), Helper::getPaste());
  775. $_GET['pasteid'] = 'foo';
  776. $_GET['deletetoken'] = 'bar';
  777. ob_start();
  778. new Controller;
  779. $content = ob_get_contents();
  780. ob_end_clean();
  781. $this->assertRegExp(
  782. '#<div[^>]*id="errormessage"[^>]*>.*Invalid paste ID\.#s',
  783. $content,
  784. 'outputs delete error correctly'
  785. );
  786. $this->assertTrue($this->_data->exists(Helper::getPasteId()), 'paste exists after failing to delete data');
  787. }
  788. /**
  789. * @runInSeparateProcess
  790. */
  791. public function testDeleteInexistantId()
  792. {
  793. $_GET['pasteid'] = Helper::getPasteId();
  794. $_GET['deletetoken'] = 'bar';
  795. ob_start();
  796. new Controller;
  797. $content = ob_get_contents();
  798. ob_end_clean();
  799. $this->assertRegExp(
  800. '#<div[^>]*id="errormessage"[^>]*>.*Paste does not exist, has expired or has been deleted\.#s',
  801. $content,
  802. 'outputs delete error correctly'
  803. );
  804. }
  805. /**
  806. * @runInSeparateProcess
  807. */
  808. public function testDeleteInvalidToken()
  809. {
  810. $this->_data->create(Helper::getPasteId(), Helper::getPaste());
  811. $_GET['pasteid'] = Helper::getPasteId();
  812. $_GET['deletetoken'] = 'bar';
  813. ob_start();
  814. new Controller;
  815. $content = ob_get_contents();
  816. ob_end_clean();
  817. $this->assertRegExp(
  818. '#<div[^>]*id="errormessage"[^>]*>.*Wrong deletion token\. Paste was not deleted\.#s',
  819. $content,
  820. 'outputs delete error correctly'
  821. );
  822. $this->assertTrue($this->_data->exists(Helper::getPasteId()), 'paste exists after failing to delete data');
  823. }
  824. /**
  825. * @runInSeparateProcess
  826. */
  827. public function testDeleteInvalidBurnAfterReading()
  828. {
  829. $this->_data->create(Helper::getPasteId(), Helper::getPaste());
  830. $this->assertTrue($this->_data->exists(Helper::getPasteId()), 'paste exists before deleting data');
  831. $file = tempnam(sys_get_temp_dir(), 'FOO');
  832. file_put_contents($file, json_encode(array(
  833. 'deletetoken' => 'burnafterreading',
  834. )));
  835. Request::setInputStream($file);
  836. $_SERVER['QUERY_STRING'] = Helper::getPasteId();
  837. $_GET[Helper::getPasteId()] = '';
  838. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  839. $_SERVER['REQUEST_METHOD'] = 'POST';
  840. ob_start();
  841. new Controller;
  842. $content = ob_get_contents();
  843. ob_end_clean();
  844. $response = json_decode($content, true);
  845. $this->assertEquals(1, $response['status'], 'outputs status');
  846. $this->assertTrue($this->_data->exists(Helper::getPasteId()), 'paste exists after failing to delete data');
  847. }
  848. /**
  849. * @runInSeparateProcess
  850. */
  851. public function testDeleteExpired()
  852. {
  853. $expiredPaste = Helper::getPaste(2, array('expire_date' => 1000));
  854. $this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste does not exist before being created');
  855. $this->_data->create(Helper::getPasteId(), $expiredPaste);
  856. $this->assertTrue($this->_data->exists(Helper::getPasteId()), 'paste exists before deleting data');
  857. $_GET['pasteid'] = Helper::getPasteId();
  858. $_GET['deletetoken'] = 'does not matter in this context, but has to be set';
  859. ob_start();
  860. new Controller;
  861. $content = ob_get_contents();
  862. ob_end_clean();
  863. $this->assertRegExp(
  864. '#<div[^>]*id="errormessage"[^>]*>.*Paste does not exist, has expired or has been deleted\.#s',
  865. $content,
  866. 'outputs error correctly'
  867. );
  868. $this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste successfully deleted');
  869. }
  870. /**
  871. * @runInSeparateProcess
  872. */
  873. public function testDeleteMissingPerPasteSalt()
  874. {
  875. $paste = Helper::getPaste();
  876. unset($paste['meta']['salt']);
  877. $this->_data->create(Helper::getPasteId(), $paste);
  878. $this->assertTrue($this->_data->exists(Helper::getPasteId()), 'paste exists before deleting data');
  879. $_GET['pasteid'] = Helper::getPasteId();
  880. $_GET['deletetoken'] = hash_hmac('sha256', Helper::getPasteId(), ServerSalt::get());
  881. ob_start();
  882. new Controller;
  883. $content = ob_get_contents();
  884. ob_end_clean();
  885. $this->assertRegExp(
  886. '#<div[^>]*id="status"[^>]*>.*Paste was properly deleted\.#s',
  887. $content,
  888. 'outputs deleted status correctly'
  889. );
  890. $this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste successfully deleted');
  891. }
  892. }