1
0

ControllerTest.php 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952
  1. <?php declare(strict_types=1);
  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->assertFalse(array_key_exists('created', $paste['meta']), 'does not output created');
  162. $this->assertEquals(
  163. hash_hmac('sha256', $response['id'], $paste['meta']['salt']),
  164. $response['deletetoken'],
  165. 'outputs valid delete token'
  166. );
  167. }
  168. /**
  169. * @runInSeparateProcess
  170. */
  171. public function testCreateInvalidTimelimit()
  172. {
  173. $options = parse_ini_file(CONF, true);
  174. $options['traffic']['limit'] = 0;
  175. Helper::createIniFile(CONF, $options);
  176. $paste = Helper::getPasteJson(2, array('expire' => 25));
  177. $file = tempnam(sys_get_temp_dir(), 'FOO');
  178. file_put_contents($file, $paste);
  179. Request::setInputStream($file);
  180. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  181. $_SERVER['REQUEST_METHOD'] = 'POST';
  182. $_SERVER['REMOTE_ADDR'] = '::1';
  183. TrafficLimiter::canPass();
  184. ob_start();
  185. new Controller;
  186. $content = ob_get_contents();
  187. ob_end_clean();
  188. $response = json_decode($content, true);
  189. $this->assertEquals(0, $response['status'], 'outputs status');
  190. $this->assertTrue($this->_data->exists($response['id']), 'paste exists after posting data');
  191. $paste = $this->_data->read($response['id']);
  192. $this->assertEquals(
  193. hash_hmac('sha256', $response['id'], $paste['meta']['salt']),
  194. $response['deletetoken'],
  195. 'outputs valid delete token'
  196. );
  197. }
  198. /**
  199. * @runInSeparateProcess
  200. */
  201. public function testCreateInvalidSize()
  202. {
  203. $options = parse_ini_file(CONF, true);
  204. $options['main']['sizelimit'] = 10;
  205. $options['traffic']['limit'] = 0;
  206. Helper::createIniFile(CONF, $options);
  207. $paste = Helper::getPasteJson();
  208. $file = tempnam(sys_get_temp_dir(), 'FOO');
  209. file_put_contents($file, $paste);
  210. Request::setInputStream($file);
  211. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  212. $_SERVER['REQUEST_METHOD'] = 'POST';
  213. $_SERVER['REMOTE_ADDR'] = '::1';
  214. ob_start();
  215. new Controller;
  216. $content = ob_get_contents();
  217. ob_end_clean();
  218. $response = json_decode($content, true);
  219. $this->assertEquals(1, $response['status'], 'outputs error status');
  220. $this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste exists after posting data');
  221. }
  222. /**
  223. * @runInSeparateProcess
  224. */
  225. public function testCreateProxyHeader()
  226. {
  227. $options = parse_ini_file(CONF, true);
  228. $options['traffic']['header'] = 'X_FORWARDED_FOR';
  229. Helper::createIniFile(CONF, $options);
  230. $paste = Helper::getPasteJson();
  231. $file = tempnam(sys_get_temp_dir(), 'FOO');
  232. file_put_contents($file, $paste);
  233. Request::setInputStream($file);
  234. $_SERVER['HTTP_X_FORWARDED_FOR'] = '::2';
  235. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  236. $_SERVER['REQUEST_METHOD'] = 'POST';
  237. $_SERVER['REMOTE_ADDR'] = '::1';
  238. ob_start();
  239. new Controller;
  240. $content = ob_get_contents();
  241. ob_end_clean();
  242. $response = json_decode($content, true);
  243. $this->assertEquals(0, $response['status'], 'outputs status');
  244. $this->assertTrue($this->_data->exists($response['id']), 'paste exists after posting data');
  245. $paste = $this->_data->read($response['id']);
  246. $this->assertEquals(
  247. hash_hmac('sha256', $response['id'], $paste['meta']['salt']),
  248. $response['deletetoken'],
  249. 'outputs valid delete token'
  250. );
  251. }
  252. /**
  253. * @runInSeparateProcess
  254. */
  255. public function testCreateDuplicateId()
  256. {
  257. $options = parse_ini_file(CONF, true);
  258. $options['traffic']['limit'] = 0;
  259. Helper::createIniFile(CONF, $options);
  260. $this->_data->create(Helper::getPasteId(), Helper::getPaste());
  261. $paste = Helper::getPasteJson();
  262. $file = tempnam(sys_get_temp_dir(), 'FOO');
  263. file_put_contents($file, $paste);
  264. Request::setInputStream($file);
  265. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  266. $_SERVER['REQUEST_METHOD'] = 'POST';
  267. $_SERVER['REMOTE_ADDR'] = '::1';
  268. ob_start();
  269. new Controller;
  270. $content = ob_get_contents();
  271. ob_end_clean();
  272. $response = json_decode($content, true);
  273. $this->assertEquals(1, $response['status'], 'outputs error status');
  274. $this->assertTrue($this->_data->exists(Helper::getPasteId()), 'paste exists after posting data');
  275. }
  276. /**
  277. * @runInSeparateProcess
  278. */
  279. public function testCreateValidExpire()
  280. {
  281. $options = parse_ini_file(CONF, true);
  282. $options['traffic']['limit'] = 0;
  283. Helper::createIniFile(CONF, $options);
  284. $paste = Helper::getPasteJson();
  285. $file = tempnam(sys_get_temp_dir(), 'FOO');
  286. file_put_contents($file, $paste);
  287. Request::setInputStream($file);
  288. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  289. $_SERVER['REQUEST_METHOD'] = 'POST';
  290. $_SERVER['REMOTE_ADDR'] = '::1';
  291. $time = time();
  292. ob_start();
  293. new Controller;
  294. $content = ob_get_contents();
  295. ob_end_clean();
  296. $response = json_decode($content, true);
  297. $this->assertEquals(0, $response['status'], 'outputs status');
  298. $this->assertTrue($this->_data->exists($response['id']), 'paste exists after posting data');
  299. $paste = $this->_data->read($response['id']);
  300. $this->assertEquals(
  301. hash_hmac('sha256', $response['id'], $paste['meta']['salt']),
  302. $response['deletetoken'],
  303. 'outputs valid delete token'
  304. );
  305. $this->assertGreaterThanOrEqual($time + 300, $paste['meta']['expire_date'], 'time is set correctly');
  306. }
  307. /**
  308. * @runInSeparateProcess
  309. */
  310. public function testCreateValidExpireWithDiscussion()
  311. {
  312. $options = parse_ini_file(CONF, true);
  313. $options['traffic']['limit'] = 0;
  314. Helper::createIniFile(CONF, $options);
  315. $paste = Helper::getPasteJson();
  316. $file = tempnam(sys_get_temp_dir(), 'FOO');
  317. file_put_contents($file, $paste);
  318. Request::setInputStream($file);
  319. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  320. $_SERVER['REQUEST_METHOD'] = 'POST';
  321. $_SERVER['REMOTE_ADDR'] = '::1';
  322. $time = time();
  323. ob_start();
  324. new Controller;
  325. $content = ob_get_contents();
  326. ob_end_clean();
  327. $response = json_decode($content, true);
  328. $this->assertEquals(0, $response['status'], 'outputs status');
  329. $this->assertTrue($this->_data->exists($response['id']), 'paste exists after posting data');
  330. $paste = $this->_data->read($response['id']);
  331. $this->assertEquals(
  332. hash_hmac('sha256', $response['id'], $paste['meta']['salt']),
  333. $response['deletetoken'],
  334. 'outputs valid delete token'
  335. );
  336. $this->assertGreaterThanOrEqual($time + 300, $paste['meta']['expire_date'], 'time is set correctly');
  337. $this->assertEquals(1, $paste['adata'][2], 'discussion is enabled');
  338. }
  339. /**
  340. * @runInSeparateProcess
  341. */
  342. public function testCreateInvalidExpire()
  343. {
  344. $options = parse_ini_file(CONF, true);
  345. $options['traffic']['limit'] = 0;
  346. Helper::createIniFile(CONF, $options);
  347. $paste = Helper::getPasteJson(2, array('expire' => 'foo'));
  348. $file = tempnam(sys_get_temp_dir(), 'FOO');
  349. file_put_contents($file, $paste);
  350. Request::setInputStream($file);
  351. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  352. $_SERVER['REQUEST_METHOD'] = 'POST';
  353. $_SERVER['REMOTE_ADDR'] = '::1';
  354. ob_start();
  355. new Controller;
  356. $content = ob_get_contents();
  357. ob_end_clean();
  358. $response = json_decode($content, true);
  359. $this->assertEquals(0, $response['status'], 'outputs status');
  360. $this->assertTrue($this->_data->exists($response['id']), 'paste exists after posting data');
  361. $paste = $this->_data->read($response['id']);
  362. $this->assertEquals(
  363. hash_hmac('sha256', $response['id'], $paste['meta']['salt']),
  364. $response['deletetoken'],
  365. 'outputs valid delete token'
  366. );
  367. }
  368. /**
  369. * @runInSeparateProcess
  370. */
  371. public function testCreateInvalidBurn()
  372. {
  373. $options = parse_ini_file(CONF, true);
  374. $options['traffic']['limit'] = 0;
  375. Helper::createIniFile(CONF, $options);
  376. $paste = Helper::getPastePost();
  377. $paste['adata'][3] = 'neither 1 nor 0';
  378. $file = tempnam(sys_get_temp_dir(), 'FOO');
  379. file_put_contents($file, json_encode($paste));
  380. Request::setInputStream($file);
  381. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  382. $_SERVER['REQUEST_METHOD'] = 'POST';
  383. $_SERVER['REMOTE_ADDR'] = '::1';
  384. ob_start();
  385. new Controller;
  386. $content = ob_get_contents();
  387. ob_end_clean();
  388. $response = json_decode($content, true);
  389. $this->assertEquals(1, $response['status'], 'outputs error status');
  390. $this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste exists after posting data');
  391. }
  392. /**
  393. * @runInSeparateProcess
  394. */
  395. public function testCreateInvalidOpenDiscussion()
  396. {
  397. $options = parse_ini_file(CONF, true);
  398. $options['traffic']['limit'] = 0;
  399. Helper::createIniFile(CONF, $options);
  400. $paste = Helper::getPastePost();
  401. $paste['adata'][2] = 'neither 1 nor 0';
  402. $file = tempnam(sys_get_temp_dir(), 'FOO');
  403. file_put_contents($file, json_encode($paste));
  404. Request::setInputStream($file);
  405. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  406. $_SERVER['REQUEST_METHOD'] = 'POST';
  407. $_SERVER['REMOTE_ADDR'] = '::1';
  408. ob_start();
  409. new Controller;
  410. $content = ob_get_contents();
  411. ob_end_clean();
  412. $response = json_decode($content, true);
  413. $this->assertEquals(1, $response['status'], 'outputs error status');
  414. $this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste exists after posting data');
  415. }
  416. /**
  417. * In some webserver setups (found with Suhosin) overly long POST params are
  418. * silently removed, check that this case is handled
  419. *
  420. * @runInSeparateProcess
  421. */
  422. public function testCreateBrokenUpload()
  423. {
  424. $paste = substr(Helper::getPasteJson(), 0, -10);
  425. $file = tempnam(sys_get_temp_dir(), 'FOO');
  426. file_put_contents($file, $paste);
  427. Request::setInputStream($file);
  428. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  429. $_SERVER['REQUEST_METHOD'] = 'POST';
  430. $_SERVER['REMOTE_ADDR'] = '::1';
  431. $this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste does not exists before posting data');
  432. ob_start();
  433. new Controller;
  434. $content = ob_get_contents();
  435. ob_end_clean();
  436. $response = json_decode($content, true);
  437. $this->assertEquals(1, $response['status'], 'outputs error status');
  438. $this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste exists after posting data');
  439. }
  440. /**
  441. * @runInSeparateProcess
  442. */
  443. public function testCreateTooSoon()
  444. {
  445. $paste = Helper::getPasteJson();
  446. $file = tempnam(sys_get_temp_dir(), 'FOO');
  447. file_put_contents($file, $paste);
  448. Request::setInputStream($file);
  449. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  450. $_SERVER['REQUEST_METHOD'] = 'POST';
  451. $_SERVER['REMOTE_ADDR'] = '::1';
  452. ob_start();
  453. new Controller;
  454. ob_end_clean();
  455. $this->_data->delete(Helper::getPasteId());
  456. ob_start();
  457. new Controller;
  458. $content = ob_get_contents();
  459. ob_end_clean();
  460. $response = json_decode($content, true);
  461. $this->assertEquals(1, $response['status'], 'outputs error status');
  462. $this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste exists after posting data');
  463. }
  464. /**
  465. * @runInSeparateProcess
  466. */
  467. public function testCreateInvalidFormat()
  468. {
  469. $options = parse_ini_file(CONF, true);
  470. $options['traffic']['limit'] = 0;
  471. Helper::createIniFile(CONF, $options);
  472. $file = tempnam(sys_get_temp_dir(), 'FOO');
  473. file_put_contents($file, Helper::getPasteJson(1));
  474. Request::setInputStream($file);
  475. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  476. $_SERVER['REQUEST_METHOD'] = 'POST';
  477. $_SERVER['REMOTE_ADDR'] = '::1';
  478. ob_start();
  479. new Controller;
  480. $content = ob_get_contents();
  481. ob_end_clean();
  482. $response = json_decode($content, true);
  483. $this->assertEquals(1, $response['status'], 'outputs error status');
  484. $this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste exists after posting data');
  485. }
  486. /**
  487. * @runInSeparateProcess
  488. */
  489. public function testCreateComment()
  490. {
  491. $options = parse_ini_file(CONF, true);
  492. $options['traffic']['limit'] = 0;
  493. Helper::createIniFile(CONF, $options);
  494. $comment = Helper::getCommentJson();
  495. $file = tempnam(sys_get_temp_dir(), 'FOO');
  496. file_put_contents($file, $comment);
  497. Request::setInputStream($file);
  498. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  499. $_SERVER['REQUEST_METHOD'] = 'POST';
  500. $_SERVER['REMOTE_ADDR'] = '::1';
  501. $this->_data->create(Helper::getPasteId(), Helper::getPaste());
  502. ob_start();
  503. new Controller;
  504. $content = ob_get_contents();
  505. ob_end_clean();
  506. $response = json_decode($content, true);
  507. $this->assertEquals(0, $response['status'], 'outputs status');
  508. $this->assertTrue($this->_data->existsComment(Helper::getPasteId(), Helper::getPasteId(), $response['id']), 'paste exists after posting data');
  509. }
  510. /**
  511. * @runInSeparateProcess
  512. */
  513. public function testCreateInvalidComment()
  514. {
  515. $options = parse_ini_file(CONF, true);
  516. $options['traffic']['limit'] = 0;
  517. Helper::createIniFile(CONF, $options);
  518. $comment = Helper::getCommentPost();
  519. $comment['parentid'] = 'foo';
  520. $file = tempnam(sys_get_temp_dir(), 'FOO');
  521. file_put_contents($file, json_encode($comment));
  522. Request::setInputStream($file);
  523. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  524. $_SERVER['REQUEST_METHOD'] = 'POST';
  525. $_SERVER['REMOTE_ADDR'] = '::1';
  526. $this->_data->create(Helper::getPasteId(), Helper::getPaste());
  527. ob_start();
  528. new Controller;
  529. $content = ob_get_contents();
  530. ob_end_clean();
  531. $response = json_decode($content, true);
  532. $this->assertEquals(1, $response['status'], 'outputs error status');
  533. $this->assertFalse($this->_data->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment exists after posting data');
  534. }
  535. /**
  536. * @runInSeparateProcess
  537. */
  538. public function testCreateCommentDiscussionDisabled()
  539. {
  540. $options = parse_ini_file(CONF, true);
  541. $options['traffic']['limit'] = 0;
  542. Helper::createIniFile(CONF, $options);
  543. $comment = Helper::getCommentJson();
  544. $file = tempnam(sys_get_temp_dir(), 'FOO');
  545. file_put_contents($file, $comment);
  546. Request::setInputStream($file);
  547. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  548. $_SERVER['REQUEST_METHOD'] = 'POST';
  549. $_SERVER['REMOTE_ADDR'] = '::1';
  550. $paste = Helper::getPaste();
  551. $paste['adata'][2] = 0;
  552. $this->_data->create(Helper::getPasteId(), $paste);
  553. ob_start();
  554. new Controller;
  555. $content = ob_get_contents();
  556. ob_end_clean();
  557. $response = json_decode($content, true);
  558. $this->assertEquals(1, $response['status'], 'outputs error status');
  559. $this->assertFalse($this->_data->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'paste exists after posting data');
  560. }
  561. /**
  562. * @runInSeparateProcess
  563. */
  564. public function testCreateCommentInvalidPaste()
  565. {
  566. $options = parse_ini_file(CONF, true);
  567. $options['traffic']['limit'] = 0;
  568. Helper::createIniFile(CONF, $options);
  569. $comment = Helper::getCommentJson();
  570. $file = tempnam(sys_get_temp_dir(), 'FOO');
  571. file_put_contents($file, $comment);
  572. Request::setInputStream($file);
  573. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  574. $_SERVER['REQUEST_METHOD'] = 'POST';
  575. $_SERVER['REMOTE_ADDR'] = '::1';
  576. ob_start();
  577. new Controller;
  578. $content = ob_get_contents();
  579. ob_end_clean();
  580. $response = json_decode($content, true);
  581. $this->assertEquals(1, $response['status'], 'outputs error status');
  582. $this->assertFalse($this->_data->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'paste exists after posting data');
  583. }
  584. /**
  585. * @runInSeparateProcess
  586. */
  587. public function testCreateDuplicateComment()
  588. {
  589. $options = parse_ini_file(CONF, true);
  590. $options['traffic']['limit'] = 0;
  591. Helper::createIniFile(CONF, $options);
  592. $this->_data->create(Helper::getPasteId(), Helper::getPaste());
  593. $this->_data->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getPasteId(), Helper::getComment());
  594. $this->assertTrue($this->_data->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getPasteId()), 'comment exists before posting data');
  595. $comment = Helper::getCommentJson();
  596. $file = tempnam(sys_get_temp_dir(), 'FOO');
  597. file_put_contents($file, $comment);
  598. Request::setInputStream($file);
  599. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  600. $_SERVER['REQUEST_METHOD'] = 'POST';
  601. $_SERVER['REMOTE_ADDR'] = '::1';
  602. ob_start();
  603. new Controller;
  604. $content = ob_get_contents();
  605. ob_end_clean();
  606. $response = json_decode($content, true);
  607. $this->assertEquals(1, $response['status'], 'outputs error status');
  608. $this->assertTrue($this->_data->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getPasteId()), 'paste exists after posting data');
  609. }
  610. /**
  611. * @runInSeparateProcess
  612. */
  613. public function testReadInvalidId()
  614. {
  615. $_SERVER['QUERY_STRING'] = 'foo';
  616. $_GET['foo'] = '';
  617. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  618. ob_start();
  619. new Controller;
  620. $content = ob_get_contents();
  621. ob_end_clean();
  622. $response = json_decode($content, true);
  623. $this->assertEquals(1, $response['status'], 'outputs error status');
  624. $this->assertEquals('Invalid paste ID.', $response['message'], 'outputs error message');
  625. }
  626. /**
  627. * @runInSeparateProcess
  628. */
  629. public function testReadNonexisting()
  630. {
  631. $_SERVER['QUERY_STRING'] = Helper::getPasteId();
  632. $_GET[Helper::getPasteId()] = '';
  633. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  634. ob_start();
  635. new Controller;
  636. $content = ob_get_contents();
  637. ob_end_clean();
  638. $response = json_decode($content, true);
  639. $this->assertEquals(1, $response['status'], 'outputs error status');
  640. $this->assertEquals('Paste does not exist, has expired or has been deleted.', $response['message'], 'outputs error message');
  641. }
  642. /**
  643. * @runInSeparateProcess
  644. */
  645. public function testReadExpired()
  646. {
  647. $expiredPaste = Helper::getPaste(2, array('expire_date' => 1344803344));
  648. $this->_data->create(Helper::getPasteId(), $expiredPaste);
  649. $_SERVER['QUERY_STRING'] = Helper::getPasteId();
  650. $_GET[Helper::getPasteId()] = '';
  651. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  652. ob_start();
  653. new Controller;
  654. $content = ob_get_contents();
  655. ob_end_clean();
  656. $response = json_decode($content, true);
  657. $this->assertEquals(1, $response['status'], 'outputs error status');
  658. $this->assertEquals('Paste does not exist, has expired or has been deleted.', $response['message'], 'outputs error message');
  659. }
  660. /**
  661. * @runInSeparateProcess
  662. */
  663. public function testReadBurn()
  664. {
  665. $paste = Helper::getPaste();
  666. $paste['adata'][3] = 1;
  667. $this->_data->create(Helper::getPasteId(), $paste);
  668. $_SERVER['QUERY_STRING'] = Helper::getPasteId();
  669. $_GET[Helper::getPasteId()] = '';
  670. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  671. ob_start();
  672. new Controller;
  673. $content = ob_get_contents();
  674. ob_end_clean();
  675. $response = json_decode($content, true);
  676. $this->assertEquals(0, $response['status'], 'outputs success status');
  677. $this->assertEquals(Helper::getPasteId(), $response['id'], 'outputs data correctly');
  678. $this->assertStringEndsWith('?' . $response['id'], $response['url'], 'returned URL points to new paste');
  679. $this->assertEquals($paste['ct'], $response['ct'], 'outputs ct correctly');
  680. $this->assertEquals($paste['adata'][1], $response['adata'][1], 'outputs formatter correctly');
  681. $this->assertEquals($paste['adata'][2], $response['adata'][2], 'outputs opendiscussion correctly');
  682. $this->assertEquals($paste['adata'][3], $response['adata'][3], 'outputs burnafterreading correctly');
  683. $this->assertFalse(array_key_exists('created', $response['meta']), 'does not output created');
  684. $this->assertEquals(0, $response['comment_count'], 'outputs comment_count correctly');
  685. $this->assertEquals(0, $response['comment_offset'], 'outputs comment_offset correctly');
  686. // by default it will be deleted instantly after it is read
  687. $this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste exists after reading');
  688. }
  689. /**
  690. * @runInSeparateProcess
  691. */
  692. public function testReadJson()
  693. {
  694. $paste = Helper::getPaste();
  695. $this->_data->create(Helper::getPasteId(), $paste);
  696. $_SERVER['QUERY_STRING'] = Helper::getPasteId();
  697. $_GET[Helper::getPasteId()] = '';
  698. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  699. ob_start();
  700. new Controller;
  701. $content = ob_get_contents();
  702. ob_end_clean();
  703. $response = json_decode($content, true);
  704. $this->assertEquals(0, $response['status'], 'outputs success status');
  705. $this->assertEquals(Helper::getPasteId(), $response['id'], 'outputs data correctly');
  706. $this->assertStringEndsWith('?' . $response['id'], $response['url'], 'returned URL points to new paste');
  707. $this->assertEquals($paste['ct'], $response['ct'], 'outputs ct correctly');
  708. $this->assertEquals($paste['adata'][1], $response['adata'][1], 'outputs formatter correctly');
  709. $this->assertEquals($paste['adata'][2], $response['adata'][2], 'outputs opendiscussion correctly');
  710. $this->assertEquals($paste['adata'][3], $response['adata'][3], 'outputs burnafterreading correctly');
  711. $this->assertFalse(array_key_exists('created', $response['meta']), 'does not output created');
  712. $this->assertEquals(0, $response['comment_count'], 'outputs comment_count correctly');
  713. $this->assertEquals(0, $response['comment_offset'], 'outputs comment_offset correctly');
  714. }
  715. /**
  716. * @runInSeparateProcess
  717. */
  718. public function testReadOldSyntax()
  719. {
  720. $paste = Helper::getPaste(1);
  721. $paste['meta'] = array(
  722. 'syntaxcoloring' => true,
  723. 'postdate' => $paste['meta']['postdate'],
  724. 'opendiscussion' => $paste['meta']['opendiscussion'],
  725. );
  726. $this->_data->create(Helper::getPasteId(), $paste);
  727. $_SERVER['QUERY_STRING'] = Helper::getPasteId();
  728. $_GET[Helper::getPasteId()] = '';
  729. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  730. ob_start();
  731. new Controller;
  732. $content = ob_get_contents();
  733. ob_end_clean();
  734. $response = json_decode($content, true);
  735. $this->assertEquals(0, $response['status'], 'outputs success status');
  736. $this->assertEquals(Helper::getPasteId(), $response['id'], 'outputs data correctly');
  737. $this->assertStringEndsWith('?' . $response['id'], $response['url'], 'returned URL points to new paste');
  738. $this->assertEquals($paste['data'], $response['data'], 'outputs data correctly');
  739. $this->assertEquals('syntaxhighlighting', $response['meta']['formatter'], 'outputs format correctly');
  740. $this->assertFalse(array_key_exists('postdate', $response['meta']), 'does not output postdate');
  741. $this->assertEquals($paste['meta']['opendiscussion'], $response['meta']['opendiscussion'], 'outputs opendiscussion correctly');
  742. $this->assertEquals(0, $response['comment_count'], 'outputs comment_count correctly');
  743. $this->assertEquals(0, $response['comment_offset'], 'outputs comment_offset correctly');
  744. }
  745. /**
  746. * @runInSeparateProcess
  747. */
  748. public function testReadBurnAfterReading()
  749. {
  750. $burnPaste = Helper::getPaste();
  751. $burnPaste['adata'][3] = 1;
  752. $this->_data->create(Helper::getPasteId(), $burnPaste);
  753. $this->assertTrue($this->_data->exists(Helper::getPasteId()), 'paste exists before deleting data');
  754. $_SERVER['QUERY_STRING'] = Helper::getPasteId();
  755. $_GET[Helper::getPasteId()] = '';
  756. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  757. ob_start();
  758. new Controller;
  759. $content = ob_get_contents();
  760. ob_end_clean();
  761. $response = json_decode($content, true);
  762. $this->assertEquals(0, $response['status'], 'outputs status');
  763. $this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste successfully deleted');
  764. }
  765. /**
  766. * @runInSeparateProcess
  767. */
  768. public function testDelete()
  769. {
  770. $this->_data->create(Helper::getPasteId(), Helper::getPaste());
  771. $this->assertTrue($this->_data->exists(Helper::getPasteId()), 'paste exists before deleting data');
  772. $paste = $this->_data->read(Helper::getPasteId());
  773. $_GET['pasteid'] = Helper::getPasteId();
  774. $_GET['deletetoken'] = hash_hmac('sha256', Helper::getPasteId(), $paste['meta']['salt']);
  775. ob_start();
  776. new Controller;
  777. $content = ob_get_contents();
  778. ob_end_clean();
  779. $this->assertMatchesRegularExpression(
  780. '#<div[^>]*id="status"[^>]*>.*Paste was properly deleted\.#s',
  781. $content,
  782. 'outputs deleted status correctly'
  783. );
  784. $this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste successfully deleted');
  785. }
  786. /**
  787. * @runInSeparateProcess
  788. */
  789. public function testDeleteInvalidId()
  790. {
  791. $this->_data->create(Helper::getPasteId(), Helper::getPaste());
  792. $_GET['pasteid'] = 'foo';
  793. $_GET['deletetoken'] = 'bar';
  794. ob_start();
  795. new Controller;
  796. $content = ob_get_contents();
  797. ob_end_clean();
  798. $this->assertMatchesRegularExpression(
  799. '#<div[^>]*id="errormessage"[^>]*>.*Invalid paste ID\.#s',
  800. $content,
  801. 'outputs delete error correctly'
  802. );
  803. $this->assertTrue($this->_data->exists(Helper::getPasteId()), 'paste exists after failing to delete data');
  804. }
  805. /**
  806. * @runInSeparateProcess
  807. */
  808. public function testDeleteInexistantId()
  809. {
  810. $_GET['pasteid'] = Helper::getPasteId();
  811. $_GET['deletetoken'] = 'bar';
  812. ob_start();
  813. new Controller;
  814. $content = ob_get_contents();
  815. ob_end_clean();
  816. $this->assertMatchesRegularExpression(
  817. '#<div[^>]*id="errormessage"[^>]*>.*Paste does not exist, has expired or has been deleted\.#s',
  818. $content,
  819. 'outputs delete error correctly'
  820. );
  821. }
  822. /**
  823. * @runInSeparateProcess
  824. */
  825. public function testDeleteInvalidToken()
  826. {
  827. $this->_data->create(Helper::getPasteId(), Helper::getPaste());
  828. $_GET['pasteid'] = Helper::getPasteId();
  829. $_GET['deletetoken'] = 'bar';
  830. ob_start();
  831. new Controller;
  832. $content = ob_get_contents();
  833. ob_end_clean();
  834. $this->assertMatchesRegularExpression(
  835. '#<div[^>]*id="errormessage"[^>]*>.*Wrong deletion token\. Paste was not deleted\.#s',
  836. $content,
  837. 'outputs delete error correctly'
  838. );
  839. $this->assertTrue($this->_data->exists(Helper::getPasteId()), 'paste exists after failing to delete data');
  840. }
  841. /**
  842. * @runInSeparateProcess
  843. */
  844. public function testDeleteInvalidBurnAfterReading()
  845. {
  846. $this->_data->create(Helper::getPasteId(), Helper::getPaste());
  847. $this->assertTrue($this->_data->exists(Helper::getPasteId()), 'paste exists before deleting data');
  848. $file = tempnam(sys_get_temp_dir(), 'FOO');
  849. file_put_contents($file, json_encode(array(
  850. 'deletetoken' => 'burnafterreading',
  851. )));
  852. Request::setInputStream($file);
  853. $_SERVER['QUERY_STRING'] = Helper::getPasteId();
  854. $_GET[Helper::getPasteId()] = '';
  855. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  856. $_SERVER['REQUEST_METHOD'] = 'POST';
  857. ob_start();
  858. new Controller;
  859. $content = ob_get_contents();
  860. ob_end_clean();
  861. $response = json_decode($content, true);
  862. $this->assertEquals(1, $response['status'], 'outputs status');
  863. $this->assertTrue($this->_data->exists(Helper::getPasteId()), 'paste exists after failing to delete data');
  864. }
  865. /**
  866. * @runInSeparateProcess
  867. */
  868. public function testDeleteExpired()
  869. {
  870. $expiredPaste = Helper::getPaste(2, array('expire_date' => 1000));
  871. $this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste does not exist before being created');
  872. $this->_data->create(Helper::getPasteId(), $expiredPaste);
  873. $this->assertTrue($this->_data->exists(Helper::getPasteId()), 'paste exists before deleting data');
  874. $_GET['pasteid'] = Helper::getPasteId();
  875. $_GET['deletetoken'] = 'does not matter in this context, but has to be set';
  876. ob_start();
  877. new Controller;
  878. $content = ob_get_contents();
  879. ob_end_clean();
  880. $this->assertMatchesRegularExpression(
  881. '#<div[^>]*id="errormessage"[^>]*>.*Paste does not exist, has expired or has been deleted\.#s',
  882. $content,
  883. 'outputs error correctly'
  884. );
  885. $this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste successfully deleted');
  886. }
  887. /**
  888. * @runInSeparateProcess
  889. */
  890. public function testDeleteMissingPerPasteSalt()
  891. {
  892. $paste = Helper::getPaste();
  893. unset($paste['meta']['salt']);
  894. $this->_data->create(Helper::getPasteId(), $paste);
  895. $this->assertTrue($this->_data->exists(Helper::getPasteId()), 'paste exists before deleting data');
  896. $_GET['pasteid'] = Helper::getPasteId();
  897. $_GET['deletetoken'] = hash_hmac('sha256', Helper::getPasteId(), ServerSalt::get());
  898. ob_start();
  899. new Controller;
  900. $content = ob_get_contents();
  901. ob_end_clean();
  902. $this->assertMatchesRegularExpression(
  903. '#<div[^>]*id="status"[^>]*>.*Paste was properly deleted\.#s',
  904. $content,
  905. 'outputs deleted status correctly'
  906. );
  907. $this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste successfully deleted');
  908. }
  909. }