PrivateBinTest.php 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128
  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. // by default it will be deleted after encryption by the JS
  787. $this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists after reading');
  788. }
  789. /**
  790. * @runInSeparateProcess
  791. */
  792. public function testReadInstantBurn()
  793. {
  794. $this->reset();
  795. $options = parse_ini_file(CONF, true);
  796. $options['main']['instantburnafterreading'] = 1;
  797. Helper::confBackup();
  798. Helper::createIniFile(CONF, $options);
  799. $burnPaste = Helper::getPaste(array('burnafterreading' => true));
  800. $this->_model->create(Helper::getPasteId(), $burnPaste);
  801. $_SERVER['QUERY_STRING'] = Helper::getPasteId();
  802. ob_start();
  803. new PrivateBin;
  804. $content = ob_get_contents();
  805. ob_end_clean();
  806. unset($burnPaste['meta']['salt']);
  807. $this->assertRegExp(
  808. '#<div id="cipherdata"[^>]*>' .
  809. preg_quote(htmlspecialchars(Helper::getPasteAsJson($burnPaste['meta']), ENT_NOQUOTES)) .
  810. '</div>#',
  811. $content,
  812. 'outputs data correctly'
  813. );
  814. // in this case the changed configuration deletes it instantly
  815. $this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste exists after reading');
  816. }
  817. /**
  818. * @runInSeparateProcess
  819. */
  820. public function testReadJson()
  821. {
  822. $this->reset();
  823. $paste = Helper::getPaste();
  824. $this->_model->create(Helper::getPasteId(), $paste);
  825. $_SERVER['QUERY_STRING'] = Helper::getPasteId();
  826. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  827. ob_start();
  828. new PrivateBin;
  829. $content = ob_get_contents();
  830. ob_end_clean();
  831. $response = json_decode($content, true);
  832. $this->assertEquals(0, $response['status'], 'outputs success status');
  833. $this->assertEquals(Helper::getPasteId(), $response['id'], 'outputs data correctly');
  834. $this->assertStringEndsWith('?' . $response['id'], $response['url'], 'returned URL points to new paste');
  835. $this->assertEquals($paste['data'], $response['data'], 'outputs data correctly');
  836. $this->assertEquals($paste['meta']['formatter'], $response['meta']['formatter'], 'outputs format correctly');
  837. $this->assertEquals($paste['meta']['postdate'], $response['meta']['postdate'], 'outputs postdate correctly');
  838. $this->assertEquals($paste['meta']['opendiscussion'], $response['meta']['opendiscussion'], 'outputs opendiscussion correctly');
  839. $this->assertEquals(0, $response['comment_count'], 'outputs comment_count correctly');
  840. $this->assertEquals(0, $response['comment_offset'], 'outputs comment_offset correctly');
  841. }
  842. /**
  843. * @runInSeparateProcess
  844. */
  845. public function testReadInvalidJson()
  846. {
  847. $this->reset();
  848. $_SERVER['QUERY_STRING'] = Helper::getPasteId();
  849. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  850. ob_start();
  851. new PrivateBin;
  852. $content = ob_get_contents();
  853. ob_end_clean();
  854. $response = json_decode($content, true);
  855. $this->assertEquals(1, $response['status'], 'outputs error status');
  856. }
  857. /**
  858. * @runInSeparateProcess
  859. */
  860. public function testReadOldSyntax()
  861. {
  862. $this->reset();
  863. $oldPaste = Helper::getPaste();
  864. $meta = array(
  865. 'syntaxcoloring' => true,
  866. 'postdate' => $oldPaste['meta']['postdate'],
  867. 'opendiscussion' => $oldPaste['meta']['opendiscussion'],
  868. );
  869. $oldPaste['meta'] = $meta;
  870. $this->_model->create(Helper::getPasteId(), $oldPaste);
  871. $_SERVER['QUERY_STRING'] = Helper::getPasteId();
  872. ob_start();
  873. new PrivateBin;
  874. $content = ob_get_contents();
  875. ob_end_clean();
  876. $meta['formatter'] = 'syntaxhighlighting';
  877. $this->assertRegExp(
  878. '#<div id="cipherdata"[^>]*>' .
  879. preg_quote(htmlspecialchars(Helper::getPasteAsJson($meta), ENT_NOQUOTES)) .
  880. '</div>#',
  881. $content,
  882. 'outputs data correctly'
  883. );
  884. }
  885. /**
  886. * @runInSeparateProcess
  887. */
  888. public function testReadOldFormat()
  889. {
  890. $this->reset();
  891. $oldPaste = Helper::getPaste();
  892. unset($oldPaste['meta']['formatter']);
  893. $this->_model->create(Helper::getPasteId(), $oldPaste);
  894. $_SERVER['QUERY_STRING'] = Helper::getPasteId();
  895. ob_start();
  896. new PrivateBin;
  897. $content = ob_get_contents();
  898. ob_end_clean();
  899. $oldPaste['meta']['formatter'] = 'plaintext';
  900. unset($oldPaste['meta']['salt']);
  901. $this->assertRegExp(
  902. '#<div id="cipherdata"[^>]*>' .
  903. preg_quote(htmlspecialchars(Helper::getPasteAsJson($oldPaste['meta']), ENT_NOQUOTES)) .
  904. '</div>#',
  905. $content,
  906. 'outputs data correctly'
  907. );
  908. }
  909. /**
  910. * @runInSeparateProcess
  911. */
  912. public function testDelete()
  913. {
  914. $this->reset();
  915. $this->_model->create(Helper::getPasteId(), Helper::getPaste());
  916. $this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists before deleting data');
  917. $paste = $this->_model->read(Helper::getPasteId());
  918. $_GET['pasteid'] = Helper::getPasteId();
  919. $_GET['deletetoken'] = hash_hmac('sha256', Helper::getPasteId(), $paste->meta->salt);
  920. ob_start();
  921. new PrivateBin;
  922. $content = ob_get_contents();
  923. ob_end_clean();
  924. $this->assertRegExp(
  925. '#<div[^>]*id="status"[^>]*>.*Paste was properly deleted\.#s',
  926. $content,
  927. 'outputs deleted status correctly'
  928. );
  929. $this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste successfully deleted');
  930. }
  931. /**
  932. * @runInSeparateProcess
  933. */
  934. public function testDeleteInvalidId()
  935. {
  936. $this->reset();
  937. $this->_model->create(Helper::getPasteId(), Helper::getPaste());
  938. $_GET['pasteid'] = 'foo';
  939. $_GET['deletetoken'] = 'bar';
  940. ob_start();
  941. new PrivateBin;
  942. $content = ob_get_contents();
  943. ob_end_clean();
  944. $this->assertRegExp(
  945. '#<div[^>]*id="errormessage"[^>]*>.*Invalid paste ID\.#s',
  946. $content,
  947. 'outputs delete error correctly'
  948. );
  949. $this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists after failing to delete data');
  950. }
  951. /**
  952. * @runInSeparateProcess
  953. */
  954. public function testDeleteInexistantId()
  955. {
  956. $this->reset();
  957. $_GET['pasteid'] = Helper::getPasteId();
  958. $_GET['deletetoken'] = 'bar';
  959. ob_start();
  960. new PrivateBin;
  961. $content = ob_get_contents();
  962. ob_end_clean();
  963. $this->assertRegExp(
  964. '#<div[^>]*id="errormessage"[^>]*>.*Paste does not exist, has expired or has been deleted\.#s',
  965. $content,
  966. 'outputs delete error correctly'
  967. );
  968. }
  969. /**
  970. * @runInSeparateProcess
  971. */
  972. public function testDeleteInvalidToken()
  973. {
  974. $this->reset();
  975. $this->_model->create(Helper::getPasteId(), Helper::getPaste());
  976. $_GET['pasteid'] = Helper::getPasteId();
  977. $_GET['deletetoken'] = 'bar';
  978. ob_start();
  979. new PrivateBin;
  980. $content = ob_get_contents();
  981. ob_end_clean();
  982. $this->assertRegExp(
  983. '#<div[^>]*id="errormessage"[^>]*>.*Wrong deletion token\. Paste was not deleted\.#s',
  984. $content,
  985. 'outputs delete error correctly'
  986. );
  987. $this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists after failing to delete data');
  988. }
  989. /**
  990. * @runInSeparateProcess
  991. */
  992. public function testDeleteBurnAfterReading()
  993. {
  994. $this->reset();
  995. $burnPaste = Helper::getPaste(array('burnafterreading' => true));
  996. $this->_model->create(Helper::getPasteId(), $burnPaste);
  997. $this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists before deleting data');
  998. $_POST['deletetoken'] = 'burnafterreading';
  999. $_SERVER['QUERY_STRING'] = Helper::getPasteId();
  1000. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  1001. $_SERVER['REQUEST_METHOD'] = 'POST';
  1002. ob_start();
  1003. new PrivateBin;
  1004. $content = ob_get_contents();
  1005. ob_end_clean();
  1006. $response = json_decode($content, true);
  1007. $this->assertEquals(0, $response['status'], 'outputs status');
  1008. $this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste successfully deleted');
  1009. }
  1010. /**
  1011. * @runInSeparateProcess
  1012. */
  1013. public function testDeleteInvalidBurnAfterReading()
  1014. {
  1015. $this->reset();
  1016. $this->_model->create(Helper::getPasteId(), Helper::getPaste());
  1017. $this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists before deleting data');
  1018. $_POST['deletetoken'] = 'burnafterreading';
  1019. $_SERVER['QUERY_STRING'] = Helper::getPasteId();
  1020. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  1021. $_SERVER['REQUEST_METHOD'] = 'POST';
  1022. ob_start();
  1023. new PrivateBin;
  1024. $content = ob_get_contents();
  1025. ob_end_clean();
  1026. $response = json_decode($content, true);
  1027. $this->assertEquals(1, $response['status'], 'outputs status');
  1028. $this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists after failing to delete data');
  1029. }
  1030. /**
  1031. * @runInSeparateProcess
  1032. */
  1033. public function testDeleteExpired()
  1034. {
  1035. $this->reset();
  1036. $expiredPaste = Helper::getPaste(array('expire_date' => 1000));
  1037. $this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste does not exist before being created');
  1038. $this->_model->create(Helper::getPasteId(), $expiredPaste);
  1039. $this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists before deleting data');
  1040. $_GET['pasteid'] = Helper::getPasteId();
  1041. $_GET['deletetoken'] = 'does not matter in this context, but has to be set';
  1042. ob_start();
  1043. new PrivateBin;
  1044. $content = ob_get_contents();
  1045. ob_end_clean();
  1046. $this->assertRegExp(
  1047. '#<div[^>]*id="errormessage"[^>]*>.*Paste does not exist, has expired or has been deleted\.#s',
  1048. $content,
  1049. 'outputs error correctly'
  1050. );
  1051. $this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste successfully deleted');
  1052. }
  1053. /**
  1054. * @runInSeparateProcess
  1055. */
  1056. public function testDeleteMissingPerPasteSalt()
  1057. {
  1058. $this->reset();
  1059. $paste = Helper::getPaste();
  1060. unset($paste['meta']['salt']);
  1061. $this->_model->create(Helper::getPasteId(), $paste);
  1062. $this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists before deleting data');
  1063. $_GET['pasteid'] = Helper::getPasteId();
  1064. $_GET['deletetoken'] = hash_hmac('sha256', Helper::getPasteId(), ServerSalt::get());
  1065. ob_start();
  1066. new PrivateBin;
  1067. $content = ob_get_contents();
  1068. ob_end_clean();
  1069. $this->assertRegExp(
  1070. '#<div[^>]*id="status"[^>]*>.*Paste was properly deleted\.#s',
  1071. $content,
  1072. 'outputs deleted status correctly'
  1073. );
  1074. $this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste successfully deleted');
  1075. }
  1076. }