Quellcode durchsuchen

fixing nasty deletion bug from #15, included unit tests to trigger it
and reworked persistence classes to through exceptions rather to fail
silently

El RIDO vor 11 Jahren
Ursprung
Commit
f775da3931
11 geänderte Dateien mit 334 neuen und 134 gelöschten Zeilen
  1. 1 1
      doc/README.md
  2. 16 7
      lib/persistence.php
  3. 29 10
      lib/serversalt.php
  4. 1 0
      lib/trafficlimiter.php
  5. 3 2
      lib/zerobin.php
  6. 2 2
      lib/zerobin/data.php
  7. 9 9
      lib/zerobin/db.php
  8. 20 20
      tst/bootstrap.php
  9. 100 0
      tst/serversalt.php
  10. 8 15
      tst/vizhash16x16.php
  11. 145 68
      tst/zerobin/db.php

+ 1 - 1
doc/README.md

@@ -10,7 +10,7 @@ Details about [installing phpDocumentor](http://phpdoc.org/docs/latest/getting-s
 can be found in its own documentation.
 can be found in its own documentation.
 
 
 Example for Debian and Ubuntu:
 Example for Debian and Ubuntu:
-    $ sudo aptitude install pear graphviz
+    $ sudo aptitude install php-pear graphviz
     $ sudo pear channel-discover pear.phpdoc.org
     $ sudo pear channel-discover pear.phpdoc.org
     $ sudo pear install phpdoc/phpDocumentor
     $ sudo pear install phpdoc/phpDocumentor
 
 

+ 16 - 7
lib/persistence.php

@@ -67,7 +67,7 @@ abstract class persistence
     protected static function _exists($filename)
     protected static function _exists($filename)
     {
     {
         self::_initialize();
         self::_initialize();
-        return is_file(self::$_path . '/' . $filename);
+        return is_file(self::$_path . DIRECTORY_SEPARATOR . $filename);
     }
     }
 
 
     /**
     /**
@@ -75,23 +75,29 @@ abstract class persistence
      *
      *
      * @access protected
      * @access protected
      * @static
      * @static
+     * @throws Exception
      * @return void
      * @return void
      */
      */
     protected static function _initialize()
     protected static function _initialize()
     {
     {
         // Create storage directory if it does not exist.
         // Create storage directory if it does not exist.
-        if (!is_dir(self::$_path)) mkdir(self::$_path, 0705);
+        if (!is_dir(self::$_path))
+            if (!@mkdir(self::$_path))
+                throw new Exception('unable to create directory ' . self::$_path, 10);
 
 
         // Create .htaccess file if it does not exist.
         // Create .htaccess file if it does not exist.
-        $file = self::$_path . '/.htaccess';
+        $file = self::$_path . DIRECTORY_SEPARATOR . '.htaccess';
         if (!is_file($file))
         if (!is_file($file))
         {
         {
-            file_put_contents(
+            $writtenBytes = @file_put_contents(
                 $file,
                 $file,
                 'Allow from none' . PHP_EOL .
                 'Allow from none' . PHP_EOL .
                 'Deny from all'. PHP_EOL,
                 'Deny from all'. PHP_EOL,
                 LOCK_EX
                 LOCK_EX
             );
             );
+            if ($writtenBytes === false || $writtenBytes < 30) {
+                throw new Exception('unable to write to file ' . $file, 11);
+            }
         }
         }
     }
     }
 
 
@@ -102,14 +108,17 @@ abstract class persistence
      * @static
      * @static
      * @param  string $filename
      * @param  string $filename
      * @param  string $data
      * @param  string $data
+     * @throws Exception
      * @return string
      * @return string
      */
      */
     protected static function _store($filename, $data)
     protected static function _store($filename, $data)
     {
     {
         self::_initialize();
         self::_initialize();
-        $file = self::$_path . '/' . $filename;
-        file_put_contents($file, $data, LOCK_EX);
-        chmod($file, 0705);
+        $file = self::$_path . DIRECTORY_SEPARATOR . $filename;
+        $writtenBytes = @file_put_contents($file, $data, LOCK_EX);
+        if ($writtenBytes === false || $writtenBytes < strlen($data)) {
+            throw new Exception('unable to write to file ' . $file, 13);
+        }
         return $file;
         return $file;
     }
     }
 }
 }

+ 29 - 10
lib/serversalt.php

@@ -47,12 +47,11 @@ class serversalt extends persistence
         }
         }
         else // fallback to mt_rand()
         else // fallback to mt_rand()
         {
         {
-            for($i = 0; $i < 16; ++$i) {
+            for($i = 0; $i < 256; ++$i) {
                 $randomSalt .= base_convert(mt_rand(), 10, 16);
                 $randomSalt .= base_convert(mt_rand(), 10, 16);
             }
             }
         }
         }
-        self::$_salt = $randomSalt;
-        return self::$_salt;
+        return $randomSalt;
     }
     }
 
 
     /**
     /**
@@ -60,21 +59,41 @@ class serversalt extends persistence
      *
      *
      * @access public
      * @access public
      * @static
      * @static
+     * @throws Exception
      * @return string
      * @return string
      */
      */
     public static function get()
     public static function get()
     {
     {
-    	if (strlen(self::$_salt)) return self::$_salt;
+        if (strlen(self::$_salt)) return self::$_salt;
 
 
         $file = 'salt.php';
         $file = 'salt.php';
-        if (!self::_exists($file)) {
+        if (self::_exists($file)) {
+            $items = explode('|', @file_get_contents(self::getPath($file)));
+            if (!is_array($items) || count($items) != 3) {
+                throw new Exception('unable to read file ' . self::getPath($file), 20);
+            }
+            self::$_salt = $items[1];
+        } else {
+            self::$_salt = self::generate();
             self::_store(
             self::_store(
-            	$file,
-                '<?php /* |'. self::generate() . '| */ ?>'
+                $file,
+                '<?php /* |'. self::$_salt . '| */ ?>'
             );
             );
         }
         }
-        $items = explode('|', file_get_contents(self::getPath($file)));
-        self::$_salt = $items[1];
-        return $items[1];
+        return self::$_salt;
+    }
+
+    /**
+     * set the path
+     *
+     * @access public
+     * @static
+     * @param  string $path
+     * @return void
+     */
+    public static function setPath($path)
+    {
+    	self::$_salt = '';
+        parent::setPath($path);
     }
     }
 }
 }

+ 1 - 0
lib/trafficlimiter.php

@@ -47,6 +47,7 @@ class trafficlimiter extends persistence
      * @access public
      * @access public
      * @static
      * @static
      * @param  string $ip
      * @param  string $ip
+     * @throws Exception
      * @return bool
      * @return bool
      */
      */
     public static function canPass($ip)
     public static function canPass($ip)

+ 3 - 2
lib/zerobin.php

@@ -339,7 +339,7 @@ class zerobin
             // Generate the "delete" token.
             // Generate the "delete" token.
             // The token is the hmac of the pasteid signed with the server salt.
             // The token is the hmac of the pasteid signed with the server salt.
             // The paste can be delete by calling http://myserver.com/zerobin/?pasteid=<pasteid>&deletetoken=<deletetoken>
             // The paste can be delete by calling http://myserver.com/zerobin/?pasteid=<pasteid>&deletetoken=<deletetoken>
-            $deletetoken = hash_hmac('sha1', $dataid , serversalt::get());
+            $deletetoken = hash_hmac('sha1', $dataid, serversalt::get());
 
 
             // 0 = no error
             // 0 = no error
             $this->_return_message(0, $dataid, array('deletetoken' => $deletetoken));
             $this->_return_message(0, $dataid, array('deletetoken' => $deletetoken));
@@ -373,7 +373,8 @@ class zerobin
         }
         }
 
 
         // Make sure token is valid.
         // Make sure token is valid.
-        if (filter::slow_equals($deletetoken, hash_hmac('sha1', $dataid , serversalt::get())))
+        serversalt::setPath($this->_conf['traffic']['dir']);
+        if (!filter::slow_equals($deletetoken, hash_hmac('sha1', $dataid, serversalt::get())))
         {
         {
             $this->_error = 'Wrong deletion token. Paste was not deleted.';
             $this->_error = 'Wrong deletion token. Paste was not deleted.';
             return;
             return;

+ 2 - 2
lib/zerobin/data.php

@@ -38,8 +38,8 @@ class zerobin_data extends zerobin_abstract
     {
     {
         // if given update the data directory
         // if given update the data directory
         if (
         if (
-        	is_array($options) &&
-        	array_key_exists('dir', $options)
+            is_array($options) &&
+            array_key_exists('dir', $options)
         ) self::$_dir = $options['dir'] . DIRECTORY_SEPARATOR;
         ) self::$_dir = $options['dir'] . DIRECTORY_SEPARATOR;
         // if needed initialize the singleton
         // if needed initialize the singleton
         if(!(self::$_instance instanceof zerobin_data)) {
         if(!(self::$_instance instanceof zerobin_data)) {

+ 9 - 9
lib/zerobin/db.php

@@ -80,13 +80,6 @@ class zerobin_db extends zerobin_abstract
                 array_key_exists('opt', $options)
                 array_key_exists('opt', $options)
             )
             )
             {
             {
-                self::$_db = new PDO(
-                    $options['dsn'],
-                    $options['usr'],
-                    $options['pwd'],
-                    $options['opt']
-                );
-
                 // check if the database contains the required tables
                 // check if the database contains the required tables
                 self::$_type = strtolower(
                 self::$_type = strtolower(
                     substr($options['dsn'], 0, strpos($options['dsn'], ':'))
                     substr($options['dsn'], 0, strpos($options['dsn'], ':'))
@@ -132,9 +125,16 @@ class zerobin_db extends zerobin_abstract
                         throw new Exception(
                         throw new Exception(
                             'PDO type ' .
                             'PDO type ' .
                             self::$_type .
                             self::$_type .
-                            ' is currently not supported.'
+                            ' is currently not supported.',
+                            5
                         );
                         );
                 }
                 }
+                self::$_db = new PDO(
+                    $options['dsn'],
+                    $options['usr'],
+                    $options['pwd'],
+                    $options['opt']
+                );
                 $statement = self::$_db->query($sql);
                 $statement = self::$_db->query($sql);
                 $tables = $statement->fetchAll(PDO::FETCH_COLUMN, 0);
                 $tables = $statement->fetchAll(PDO::FETCH_COLUMN, 0);
 
 
@@ -266,7 +266,7 @@ class zerobin_db extends zerobin_abstract
             array($pasteid)
             array($pasteid)
         );
         );
         if (
         if (
-        		array_key_exists($pasteid, self::$_cache)
+            array_key_exists($pasteid, self::$_cache)
         ) unset(self::$_cache[$pasteid]);
         ) unset(self::$_cache[$pasteid]);
     }
     }
 
 

+ 20 - 20
tst/bootstrap.php

@@ -8,24 +8,24 @@ require PATH . 'lib/auto.php';
 
 
 class helper
 class helper
 {
 {
-    public static function rmdir($path)
-    {
-    	$path .= DIRECTORY_SEPARATOR;
-    	$dir = dir($path);
-    	while(false !== ($file = $dir->read())) {
-    		if($file != '.' && $file != '..') {
-    			if(is_dir($path . $file)) {
-    				self::rmdir($path . $file);
-    			} elseif(is_file($path . $file)) {
-    				if(!@unlink($path . $file)) {
-    					throw new Exception('Error deleting file "' . $path . $file . '".');
-    				}
-    			}
-    		}
-    	}
-    	$dir->close();
-    	if(!@rmdir($path)) {
-    		throw new Exception('Error deleting directory "' . $path . '".');
-    	}
-    }
+    public static function rmdir($path)
+    {
+        $path .= DIRECTORY_SEPARATOR;
+        $dir = dir($path);
+        while(false !== ($file = $dir->read())) {
+            if($file != '.' && $file != '..') {
+                if(is_dir($path . $file)) {
+                    self::rmdir($path . $file);
+                } elseif(is_file($path . $file)) {
+                    if(!@unlink($path . $file)) {
+                        throw new Exception('Error deleting file "' . $path . $file . '".');
+                    }
+                }
+            }
+        }
+        $dir->close();
+        if(!@rmdir($path)) {
+            throw new Exception('Error deleting directory "' . $path . '".');
+        }
+    }
 }
 }

+ 100 - 0
tst/serversalt.php

@@ -0,0 +1,100 @@
+<?php
+class serversaltTest extends PHPUnit_Framework_TestCase
+{
+    private $_path;
+
+    private $_invalidPath;
+
+    private $_otherPath;
+
+    private $_invalidFile;
+
+    public function setUp()
+    {
+        /* Setup Routine */
+        $this->_path = PATH . 'data';
+        if(!is_dir($this->_path)) mkdir($this->_path);
+        serversalt::setPath($this->_path);
+
+        $this->_otherPath = $this->_path . DIRECTORY_SEPARATOR . 'foo';
+
+        $this->_invalidPath = $this->_path . DIRECTORY_SEPARATOR . 'bar';
+        if(!is_dir($this->_invalidPath)) mkdir($this->_invalidPath);
+        $this->_invalidFile = $this->_invalidPath . DIRECTORY_SEPARATOR . 'salt.php';
+    }
+
+    public function tearDown()
+    {
+        /* Tear Down Routine */
+        chmod($this->_invalidPath, 0700);
+        helper::rmdir($this->_path);
+    }
+
+    public function testGeneration()
+    {
+        // generating new salt
+        serversalt::setPath($this->_path);
+        $salt = serversalt::get();
+        require 'mcrypt_mock.php';
+        $this->assertNotEquals($salt, serversalt::generate());
+
+        // try setting a different path and resetting it
+        serversalt::setPath($this->_otherPath);
+        $this->assertNotEquals($salt, serversalt::get());
+        serversalt::setPath($this->_path);
+        $this->assertEquals($salt, serversalt::get());
+    }
+
+    /**
+     * @expectedException Exception
+     * @expectedExceptionCode 11
+     */
+    public function testPathShenanigans()
+    {
+        // try setting an invalid path
+        chmod($this->_invalidPath, 0000);
+        serversalt::setPath($this->_invalidPath);
+        serversalt::get();
+    }
+
+    /**
+     * @expectedException Exception
+     * @expectedExceptionCode 20
+     */
+    public function testFileRead()
+    {
+        // try setting an invalid file
+        chmod($this->_invalidPath, 0700);
+        file_put_contents($this->_invalidFile, '');
+        chmod($this->_invalidFile, 0000);
+        serversalt::setPath($this->_invalidPath);
+        serversalt::get();
+    }
+
+    /**
+     * @expectedException Exception
+     * @expectedExceptionCode 13
+     */
+    public function testFileWrite()
+    {
+        // try setting an invalid file
+        chmod($this->_invalidPath, 0700);
+        @unlink($this->_invalidFile);
+        file_put_contents($this->_invalidPath . DIRECTORY_SEPARATOR . '.htaccess', '');
+        chmod($this->_invalidPath, 0500);
+        serversalt::setPath($this->_invalidPath);
+        serversalt::get();
+    }
+
+    /**
+     * @expectedException Exception
+     * @expectedExceptionCode 10
+     */
+    public function testPermissionShenanigans()
+    {
+        // try creating an invalid path
+        chmod($this->_invalidPath, 0000);
+        serversalt::setPath($this->_invalidPath . DIRECTORY_SEPARATOR . 'baz');
+        serversalt::get();
+    }
+}

+ 8 - 15
tst/vizhash16x16.php

@@ -10,22 +10,20 @@ class vizhash16x16Test extends PHPUnit_Framework_TestCase
     public function setUp()
     public function setUp()
     {
     {
         /* Setup Routine */
         /* Setup Routine */
-        $this->_path = PATH . 'data' . DIRECTORY_SEPARATOR;
-        $this->_dataDirCreated = !is_dir($this->_path);
-        if($this->_dataDirCreated) mkdir($this->_path);
-        $this->_file = $this->_path . 'vizhash.png';
+        $this->_path = PATH . 'data';
+        if(!is_dir($this->_path)) mkdir($this->_path);
+        $this->_file = $this->_path . DIRECTORY_SEPARATOR . 'vizhash.png';
+        serversalt::setPath($this->_path);
     }
     }
 
 
     public function tearDown()
     public function tearDown()
     {
     {
         /* Tear Down Routine */
         /* Tear Down Routine */
-    		if($this->_dataDirCreated) {
-		        helper::rmdir($this->_path);
-        } else {
-            if(!@unlink($this->_file)) {
-            	throw new Exception('Error deleting file "' . $this->_file . '".');
-            }
+        chmod($this->_path, 0700);
+        if(!@unlink($this->_file)) {
+            throw new Exception('Error deleting file "' . $this->_file . '".');
         }
         }
+        helper::rmdir($this->_path);
     }
     }
 
 
     public function testVizhashGeneratesUniquePngsPerIp()
     public function testVizhashGeneratesUniquePngsPerIp()
@@ -37,10 +35,5 @@ class vizhash16x16Test extends PHPUnit_Framework_TestCase
         $this->assertEquals('image/png', $finfo->file($this->_file));
         $this->assertEquals('image/png', $finfo->file($this->_file));
         $this->assertNotEquals($pngdata, $vz->generate('2001:1620:2057:dead:beef::cafe:babe'));
         $this->assertNotEquals($pngdata, $vz->generate('2001:1620:2057:dead:beef::cafe:babe'));
         $this->assertEquals($pngdata, $vz->generate('127.0.0.1'));
         $this->assertEquals($pngdata, $vz->generate('127.0.0.1'));
-
-        // generating new salt
-        $salt = serversalt::get();
-        require 'mcrypt_mock.php';
-        $this->assertNotEquals($salt, serversalt::generate());
     }
     }
 }
 }

+ 145 - 68
tst/zerobin/db.php

@@ -1,68 +1,145 @@
-<?php
-class zerobin_dbTest extends PHPUnit_Framework_TestCase
-{
-    private static $pasteid = '501f02e9eeb8bcec';
-
-    private static $paste = array(
-        'data' => '{"iv":"EN39/wd5Nk8HAiSG2K5AsQ","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"QKN1DBXe5PI","ct":"8hA83xDdXjD7K2qfmw5NdA"}',
-        'meta' => array(
-            'postdate' => 1344803344,
-            'expire_date' => 1344803644,
-            'opendiscussion' => true,
-        ),
-    );
-
-    private static $commentid = 'c47efb4741195f42';
-
-    private static $comment = array(
-        'data' => '{"iv":"Pd4pOKWkmDTT9uPwVwd5Ag","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"ZIUhFTliVz4","ct":"6nOCU3peNDclDDpFtJEBKA"}',
-        'meta' => array(
-            'nickname' => '{"iv":"76MkAtOGC4oFogX/aSMxRA","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"ZIUhFTliVz4","ct":"b6Ae/U1xJdsX/+lATud4sQ"}',
-            'vizhash' => 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAABGUlEQVQokWOsl5/94983CNKQMjnxaOePf98MeKwPfNjkLZ3AgARab6b9+PeNEVnDj3/ff/z7ZiHnzsDA8Pv7H2TVPJw8EAYLAwb48OaVgIgYKycLsrYv378wMDB8//qdCVMDRA9EKSsnCwRBxNsepaLboMFlyMDAICAi9uHNK24GITQ/MDAwoNhgIGMLtwGrzegaLjw5jMz9+vUdnN17uwDCQDhJgk0O07yvX9+teDX1x79v6DYIsIjgcgMaYGFgYOBg4kJx2JejkAiBxAw+PzAwMNz4dp6wDXDw4MdNNOl0rWYsNkD89OLXI/xmo9sgzatJjAYmBgYGDiauD3/ePP18nVgb4MF89+M5ZX6js293wUMpnr8KTQMAxsCJnJ30apMAAAAASUVORK5CYII=',
-            'postdate' => 1344803528,
-        ),
-    );
-
-    private $_model;
-
-    public function setUp()
-    {
-        /* Setup Routine */
-        $this->_model = zerobin_db::getInstance(
-            array(
-                'dsn' => 'sqlite::memory:',
-                'usr' => null,
-                'pwd' => null,
-                'opt' => array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION),
-            )
-        );
-    }
-
-    public function testDatabaseBasedDataStoreWorks()
-    {
-        // storing pastes
-        $this->assertFalse($this->_model->exists(self::$pasteid), 'paste does not yet exist');
-        $this->assertTrue($this->_model->create(self::$pasteid, self::$paste), 'store new paste');
-        $this->assertTrue($this->_model->exists(self::$pasteid), 'paste exists after storing it');
-        $this->assertFalse($this->_model->create(self::$pasteid, self::$paste), 'unable to store the same paste twice');
-        $this->assertEquals(json_decode(json_encode(self::$paste)), $this->_model->read(self::$pasteid));
-
-        // storing comments
-        $this->assertFalse($this->_model->existsComment(self::$pasteid, self::$pasteid, self::$commentid), 'comment does not yet exist');
-        $this->assertTrue($this->_model->createComment(self::$pasteid, self::$pasteid, self::$commentid, self::$comment) !== false, 'store comment');
-        $this->assertTrue($this->_model->existsComment(self::$pasteid, self::$pasteid, self::$commentid), 'comment exists after storing it');
-        $comment = json_decode(json_encode(self::$comment));
-        $comment->meta->commentid = self::$commentid;
-        $comment->meta->parentid = self::$pasteid;
-        $this->assertEquals(
-            array($comment->meta->postdate => $comment),
-            $this->_model->readComments(self::$pasteid)
-        );
-
-        // deleting pastes
-        $this->_model->delete(self::$pasteid);
-        $this->assertFalse($this->_model->exists(self::$pasteid), 'paste successfully deleted');
-        $this->assertFalse($this->_model->existsComment(self::$pasteid, self::$pasteid, self::$commentid), 'comment was deleted with paste');
-        $this->assertFalse($this->_model->read(self::$pasteid), 'paste can no longer be found');
-    }
-}
+<?php
+class zerobin_dbTest extends PHPUnit_Framework_TestCase
+{
+    private static $pasteid = '501f02e9eeb8bcec';
+
+    private static $paste = array(
+        'data' => '{"iv":"EN39/wd5Nk8HAiSG2K5AsQ","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"QKN1DBXe5PI","ct":"8hA83xDdXjD7K2qfmw5NdA"}',
+        'meta' => array(
+            'postdate' => 1344803344,
+            'expire_date' => 1344803644,
+            'opendiscussion' => true,
+        ),
+    );
+
+    private static $commentid = 'c47efb4741195f42';
+
+    private static $comment = array(
+        'data' => '{"iv":"Pd4pOKWkmDTT9uPwVwd5Ag","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"ZIUhFTliVz4","ct":"6nOCU3peNDclDDpFtJEBKA"}',
+        'meta' => array(
+            'nickname' => '{"iv":"76MkAtOGC4oFogX/aSMxRA","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"ZIUhFTliVz4","ct":"b6Ae/U1xJdsX/+lATud4sQ"}',
+            'vizhash' => 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAABGUlEQVQokWOsl5/94983CNKQMjnxaOePf98MeKwPfNjkLZ3AgARab6b9+PeNEVnDj3/ff/z7ZiHnzsDA8Pv7H2TVPJw8EAYLAwb48OaVgIgYKycLsrYv378wMDB8//qdCVMDRA9EKSsnCwRBxNsepaLboMFlyMDAICAi9uHNK24GITQ/MDAwoNhgIGMLtwGrzegaLjw5jMz9+vUdnN17uwDCQDhJgk0O07yvX9+teDX1x79v6DYIsIjgcgMaYGFgYOBg4kJx2JejkAiBxAw+PzAwMNz4dp6wDXDw4MdNNOl0rWYsNkD89OLXI/xmo9sgzatJjAYmBgYGDiauD3/ePP18nVgb4MF89+M5ZX6js293wUMpnr8KTQMAxsCJnJ30apMAAAAASUVORK5CYII=',
+            'postdate' => 1344803528,
+        ),
+    );
+
+    private $_model;
+
+    public function setUp()
+    {
+        /* Setup Routine */
+        $this->_model = zerobin_db::getInstance(
+            array(
+                'dsn' => 'sqlite::memory:',
+                'usr' => null,
+                'pwd' => null,
+                'opt' => array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION),
+            )
+        );
+    }
+
+    public function testDatabaseBasedDataStoreWorks()
+    {
+        // storing pastes
+        $this->assertFalse($this->_model->exists(self::$pasteid), 'paste does not yet exist');
+        $this->assertTrue($this->_model->create(self::$pasteid, self::$paste), 'store new paste');
+        $this->assertTrue($this->_model->exists(self::$pasteid), 'paste exists after storing it');
+        $this->assertFalse($this->_model->create(self::$pasteid, self::$paste), 'unable to store the same paste twice');
+        $this->assertEquals(json_decode(json_encode(self::$paste)), $this->_model->read(self::$pasteid));
+
+        // storing comments
+        $this->assertFalse($this->_model->existsComment(self::$pasteid, self::$pasteid, self::$commentid), 'comment does not yet exist');
+        $this->assertTrue($this->_model->createComment(self::$pasteid, self::$pasteid, self::$commentid, self::$comment) !== false, 'store comment');
+        $this->assertTrue($this->_model->existsComment(self::$pasteid, self::$pasteid, self::$commentid), 'comment exists after storing it');
+        $comment = json_decode(json_encode(self::$comment));
+        $comment->meta->commentid = self::$commentid;
+        $comment->meta->parentid = self::$pasteid;
+        $this->assertEquals(
+            array($comment->meta->postdate => $comment),
+            $this->_model->readComments(self::$pasteid)
+        );
+
+        // deleting pastes
+        $this->_model->delete(self::$pasteid);
+        $this->assertFalse($this->_model->exists(self::$pasteid), 'paste successfully deleted');
+        $this->assertFalse($this->_model->existsComment(self::$pasteid, self::$pasteid, self::$commentid), 'comment was deleted with paste');
+        $this->assertFalse($this->_model->read(self::$pasteid), 'paste can no longer be found');
+    }
+
+    /**
+     * @expectedException PDOException
+     */
+    public function testGetIbmInstance()
+    {
+        zerobin_db::getInstance(array(
+            'dsn' => 'ibm:', 'usr' => null, 'pwd' => null,
+            'opt' => array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION)
+        ));
+    }
+
+    /**
+     * @expectedException PDOException
+     */
+    public function testGetInformixInstance()
+    {
+        zerobin_db::getInstance(array(
+            'dsn' => 'informix:', 'usr' => null, 'pwd' => null,
+            'opt' => array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION)
+        ));
+    }
+
+    /**
+     * @expectedException PDOException
+     */
+    public function testGetMssqlInstance()
+    {
+        zerobin_db::getInstance(array(
+            'dsn' => 'mssql:', 'usr' => null, 'pwd' => null,
+            'opt' => array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION)
+        ));
+    }
+
+    /**
+     * @expectedException PDOException
+     */
+    public function testGetMysqlInstance()
+    {
+        zerobin_db::getInstance(array(
+            'dsn' => 'mysql:', 'usr' => null, 'pwd' => null,
+            'opt' => array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION)
+        ));
+    }
+
+    /**
+     * @expectedException PDOException
+     */
+    public function testGetOciInstance()
+    {
+        zerobin_db::getInstance(array(
+            'dsn' => 'oci:', 'usr' => null, 'pwd' => null,
+            'opt' => array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION)
+        ));
+    }
+
+    /**
+     * @expectedException PDOException
+     */
+    public function testGetPgsqlInstance()
+    {
+        zerobin_db::getInstance(array(
+            'dsn' => 'pgsql:', 'usr' => null, 'pwd' => null,
+            'opt' => array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION)
+        ));
+    }
+
+    /**
+     * @expectedException Exception
+     * @expectedExceptionCode 5
+     */
+    public function testGetFooInstance()
+    {
+        zerobin_db::getInstance(array(
+            'dsn' => 'foo:', 'usr' => null, 'pwd' => null, 'opt' => null
+        ));
+    }
+}