ControllerTest.php 38 KB

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