PrivateBinTest.php 38 KB

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