PrivateBinTest.php 40 KB

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