PrivateBinTest.php 40 KB

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