ControllerTest.php 37 KB

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