diff --git a/apps/admin_export/settings.php b/apps/admin_export/settings.php index 1c98bb552f..af8dd0dbf5 100644 --- a/apps/admin_export/settings.php +++ b/apps/admin_export/settings.php @@ -27,11 +27,10 @@ OC_Util::checkAppEnabled('admin_export'); define('DS', '/'); - // Export? if (isset($_POST['admin_export'])) { // Create the export zip - if( !$path = OC_Migrate::createSysExportFile( $_POST['export_type'] ) ){ + if( !$path = OC_Migrate::export( $_POST['export_type'] ) ){ // Error die('error'); } else { @@ -46,136 +45,11 @@ if (isset($_POST['admin_export'])) { // Import? } else if( isset($_POST['admin_import']) ){ - $root = OC::$SERVERROOT . "/"; - $importname = "owncloud_import_" . date("y-m-d_H-i-s"); - - // Save data dir for later - $datadir = OC_Config::getValue( 'datadirectory' ); - - // Copy the uploaded file - $from = $_FILES['owncloud_import']['tmp_name']; - $to = get_temp_dir().'/'.$importname.'.zip'; - if( !move_uploaded_file( $from, $to ) ){ - OC_Log::write('admin_export',"Failed to copy the uploaded file",OC_Log::INFO); - exit(); - } - - // Extract zip - $zip = new ZipArchive(); - if ($zip->open(get_temp_dir().'/'.$importname.'.zip') != TRUE) { - OC_Log::write('admin_export',"Failed to open zip file",OC_Log::INFO); - exit(); - } - $zip->extractTo(get_temp_dir().'/'.$importname.'/'); - $zip->close(); - - // Delete uploaded file - unlink( get_temp_dir() . '/' . $importname . '.zip' ); - - // Now we need to check if everything is present. Data and dbexport.xml - - - // Delete current data folder. - OC_Log::write('admin_export',"Deleting current data dir",OC_Log::INFO); - unlinkRecursive( $datadir, false ); - - // Copy over data - if( !copy_r( get_temp_dir() . '/' . $importname . '/data', $datadir ) ){ - OC_Log::write('admin_export',"Failed to copy over data directory",OC_Log::INFO); - exit(); - } - - OC_DB::replaceDB( get_temp_dir() . '/' . $importname . '/dbexport.xml' ); + // TODO + // OC_Migrate::import( $pathtozipfile ); } else { // fill template $tmpl = new OC_Template('admin_export', 'settings'); return $tmpl->fetchPage(); -} - -function zipAddDir($dir, $zip, $recursive=true, $internalDir='') { - $dirname = basename($dir); - $zip->addEmptyDir($internalDir . $dirname); - $internalDir.=$dirname.='/'; - - if ($dirhandle = opendir($dir)) { - while (false !== ( $file = readdir($dirhandle))) { - - if (( $file != '.' ) && ( $file != '..' )) { - - if (is_dir($dir . '/' . $file) && $recursive) { - zipAddDir($dir . '/' . $file, $zip, $recursive, $internalDir); - } elseif (is_file($dir . '/' . $file)) { - $zip->addFile($dir . '/' . $file, $internalDir . $file); - } - } - } - closedir($dirhandle); - } else { - OC_Log::write('admin_export',"Was not able to open directory: " . $dir,OC_Log::ERROR); - } -} - -function unlinkRecursive($dir, $deleteRootToo) -{ - if(!$dh = @opendir($dir)) - { - return; - } - while (false !== ($obj = readdir($dh))) - { - if($obj == '.' || $obj == '..') - { - continue; - } - - if (!@unlink($dir . '/' . $obj)) - { - unlinkRecursive($dir.'/'.$obj, true); - } - } - - closedir($dh); - - if ($deleteRootToo) - { - @rmdir($dir); - } - - return; -} - - function copy_r( $path, $dest ) - { - if( is_dir($path) ) - { - @mkdir( $dest ); - $objects = scandir($path); - if( sizeof($objects) > 0 ) - { - foreach( $objects as $file ) - { - if( $file == "." || $file == ".." ) - continue; - // go on - if( is_dir( $path.DS.$file ) ) - { - copy_r( $path.DS.$file, $dest.DS.$file ); - } - else - { - copy( $path.DS.$file, $dest.DS.$file ); - } - } - } - return true; - } - elseif( is_file($path) ) - { - return copy($path, $dest); - } - else - { - return false; - } - } +} \ No newline at end of file diff --git a/apps/bookmarks/lib/migrate.php b/apps/bookmarks/lib/migrate.php index ffc5e9f838..36a08c0cf4 100644 --- a/apps/bookmarks/lib/migrate.php +++ b/apps/bookmarks/lib/migrate.php @@ -1,16 +1,16 @@ 'bookmarks', 'matchcol'=>'user_id', - 'matchval'=>$uid, + 'matchval'=>$this->uid, 'idcol'=>'id' ); - $ids = OC_Migrate::copyRows( $options ); + $ids = $this->content->copyRows( $options ); $options = array( 'table'=>'bookmarks_tags', @@ -19,7 +19,7 @@ class OC_Migrate_Provider_Bookmarks extends OC_Migrate_Provider{ ); // Export tags - $ids2 = OC_Migrate::copyRows( $options ); + $ids2 = $this->content->copyRows( $options ); // If both returned some ids then they worked if( is_array( $ids ) && is_array( $ids2 ) ) @@ -32,17 +32,17 @@ class OC_Migrate_Provider_Bookmarks extends OC_Migrate_Provider{ } // Import function for bookmarks - function import( $app, $info ){ - switch( $app->version ){ + function import( ){ + switch( $this->appinfo->version ){ default: // All versions of the app have had the same db structure, so all can use the same import function $query = OC_Migrate::prepare( "SELECT * FROM bookmarks WHERE user_id LIKE ?" ); - $results = $query->execute( array( $info['olduid'] ) ); + $results = $query->execute( array( $this->info['olduid'] ) ); $idmap = array(); while( $row = $data->fetchRow() ){ // Import each bookmark, saving its id into the map $query = OC_DB::prepare( "INSERT INTO *PREFIX*bookmarks(url, title, user_id, public, added, lastmodified) VALUES (?, ?, ?, ?, ?, ?)" ); - $query->execute( array( $row['url'], $row['title'], $info['newuid'], $row['public'], $row['added'], $row['lastmodified'] ) ); + $query->execute( array( $row['url'], $row['title'], $this->info['newuid'], $row['public'], $row['added'], $row['lastmodified'] ) ); // Map the id $idmap[$row['id']] = OC_DB::insertid(); } @@ -66,4 +66,4 @@ class OC_Migrate_Provider_Bookmarks extends OC_Migrate_Provider{ } // Load the provider -new OC_Migrate_Provider_Bookmarks( 'bookmarks' ); \ No newline at end of file +new OC_Migration_Provider_Bookmarks( 'bookmarks' ); \ No newline at end of file diff --git a/apps/user_migrate/settings.php b/apps/user_migrate/settings.php index 62f5e3f20d..38eee990b4 100644 --- a/apps/user_migrate/settings.php +++ b/apps/user_migrate/settings.php @@ -26,7 +26,7 @@ OC_Util::checkAppEnabled('user_migrate'); if (isset($_POST['user_export'])) { // Create the export zip - if( !$path = OC_Migrate::createUserExportFile() ){ + if( !$path = OC_Migrate::export() ){ // Error die('error'); } else { diff --git a/lib/migrate.php b/lib/migrate.php index 84eafcd4cd..338d091af8 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -22,28 +22,31 @@ /** - * provides an interface to all search providers + * provides an interface to migrate users and whole ownclouds */ class OC_Migrate{ - // Holds the db object - static private $MDB2=false; + // Array of OC_Migration_Provider objects static private $providers=array(); - // Schema db object - static private $schema=false; // User id of the user to import/export static private $uid=false; - // Path to the sqlite db - static private $dbpath=false; // Holds the ZipArchive object static private $zip=false; - // String path to export - static private $zippath=false; // Stores the type of export static private $exporttype=false; // Array of temp files to be deleted after zip creation static private $tmpfiles=array(); + // Holds the db object + static private $MDB2=false; + // Schema db object + static private $schema=false; + // Path to the sqlite db + static private $dbpath=false; + // Holds the path to the zip file + static private $zippath=false; + // Holds the OC_Migration_Content object + static private $content=false; /** * register a new migration provider @@ -69,99 +72,78 @@ class OC_Migrate{ } /** - * @breif creates a migration.db in the users data dir with their app data in - * @return bool whether operation was successfull + * @breif exports a user, or owncloud instance + * @param ootional $type string type of export, defualts to user + * @param otional $path string path to zip output folder + * @param optional $uid string user id of user to export if export type is user, defaults to current */ - private static function exportAppData( ){ - - self::connectDB(); - $ok = true; - $return = array(); - - // Find the providers - self::findProviders(); - - // Foreach provider - foreach( self::$providers as $provider ){ - $failed = false; - - // Does this app use the database? - if(file_exists(OC::$SERVERROOT.'/apps/'.$provider->id.'/appinfo/database.xml')){ - // Create some app tables - $tables = self::createAppTables( $provider->id ); - if( is_array( $tables ) ){ - // Save the table names - foreach($tables as $table){ - $return['apps'][$provider->id]['tables'][] = $table; - } - } else { - // It failed to create the tables - $failed = true; - } - } - - // Run the import function? - if( !$failed ){ - $return['apps'][$provider->id]['success'] = $provider->export( self::$uid ); - } else { - $return['apps'][$provider->id]['success'] = false; - $return['apps'][$provider->id]['message'] = 'failed to create the app tables'; - } - - // Now add some app info the the return array - $appinfo = OC_App::getAppInfo( $provider->id ); - $return['apps'][$provider->id]['version'] = $appinfo['version']; - - } - - return $return; - - } - - /** - * @breif creates an export file for the whole system - * @param optional $exporttype string export type ('instance','system' or 'userfiles') - * @param optional $path string path to zip destination (with trailing slash) - * @return path to the zip or false if there was a problem - */ - static public function createSysExportFile( $exporttype='instance', $path=null ){ - // Calculate zip name - $zipname = "oc_export_" . date("y-m-d_H-i-s") . ".zip"; - // Get the data dir + public static function export( $type='user', $path=null, $uid=null ){ $datadir = OC_Config::getValue( 'datadirectory' ); - // Calculate destination - if( !is_null( $path ) ){ - // Path given - // Is a directory? - if( !is_dir( $path ) ){ - OC_Log::write('migration', 'Path supplied to createSysExportFile() is not a directory', OC_Log::ERROR); - return false; - } - // Is writeable - if( !is_writeable( $path ) ){ - OC_Log::write('migration', 'Path supplied to createSysExportFile() is not writeable', OC_Log::ERROR); - return false; - } - self::$zippath = $path . $zipname; - } else { - // Save in tmp dir - self::$zippath = sys_get_temp_dir() . '/' . $zipname; - } - // Create the zip object - self::$zip = new ZipArchive; - // Try to create the zip - if( !self::createZip() ){ - return false; - } - // Handle export types - $exporttypes = array( 'userfiles', 'instance', 'system' ); - self::$exporttype = in_array( $exporttype, $exporttypes ) ? $exporttype : false; - if( !self::$exporttype ){ - OC_Log::write( 'migration', 'Export type: '.$exporttype.' is not supported.', OC_Log::ERROR); - return false; - } - switch( self::$exporttype ){ - case 'instance': + // Validate export type + $types = array( 'user', 'instance', 'system', 'userfiles' ); + if( !in_array( $type, $types ) ){ + OC_Log::write( 'migration', 'Invalid export type', OC_Log::ERROR ); + return false; + } + self::$exporttype = $type; + // Userid? + if( self::$exporttype == 'user' ){ + // Check user exists + if( !is_null($uid) ){ + if( !OC_User_Database::userExists( $uid ) ){ + OC_Log::write('migration', 'User: '.$uid.' is not in the database and so cannot be exported.', OC_Log::ERROR); + return false; + } + self::$uid = $uid; + } else { + self::$uid = OC_User::getUser(); + } + } + // Calculate zipname + if( self::$exporttype == 'user' ){ + $zipname = 'oc_export_' . self::$uid . '_' . date("y-m-d_H-i-s") . '.zip'; + } else { + $zipname = 'oc_export_' . self::$exporttype . '_' . date("y-m-d_H-i-s") . '.zip'; + } + // Calculate path + if( self::$exporttype == 'user' ){ + self::$zippath = $datadir . '/' . self::$uid . '/' . $zipname; + } else { + if( !is_null( $path ) ){ + // Validate custom path + if( !file_exists( $path ) || !is_writeable( $path ) ){ + OC_Log::write( 'migration', 'Path supplied is invalid.', OC_Log::ERROR ); + return false; + } + self::$zippath = $path . $zipname; + } else { + // Default path + self::$zippath = get_temp_dir() . '/' . $zipname; + } + } + // Create the zip object + self::$zip = new ZipArchive; + if( !self::createZip() ){ + return false; + } + // Do the export + self::findProviders(); + $exportdata = array(); + switch( self::$exporttype ){ + case 'user': + // Connect to the db + self::$dbpath = $datadir . '/' . self::$uid . '/migration.db'; + if( !self::connectDB() ){ + return false; + } + self::$content = new OC_Migration_Content( self::$zip, self::$MDB2 ); + // Export the app info + $exportdata = self::exportAppData(); + // Add the data dir to the zip + self::$content->addDir( $datadir . '/' . self::$uid, true, '/' ); + break; + case 'instance': + self::$content = new OC_Migration_Content( self::$zip ); // Creates a zip that is compatable with the import function $dbfile = tempnam( "/tmp", "owncloud_export_data_" ); OC_DB::getDbStructure( $dbfile, 'MDB2_SCHEMA_DUMP_ALL'); @@ -172,43 +154,112 @@ class OC_Migrate{ $dbtableprefixstring = "\n\n " . OC_Config::getValue( "dbtableprefix", "oc_" ); $dbexport = str_replace( $dbnamestring, "\n\n *dbname*", $dbexport ); $dbexport = str_replace( $dbtableprefixstring, "
\n\n *dbprefix*", $dbexport ); - // Write the new db export file - file_put_contents( $dbfile, $dbexport ); - self::$zip->addFile( $dbfile, "dbexport.xml" ); + // Add the export to the zip + self::$content->addFromString( $dbexport, "dbexport.xml" ); // Add user data foreach(OC_User::getUsers() as $user){ - self::addDirToZip( $datadir . '/' . $user . '/', true, "/userdata/" ); + self::$content->addDir( $datadir . '/' . $user . '/', true, "/userdata/" ); } break; case 'userfiles': + self::$content = new OC_Migration_Content( self::$zip ); // Creates a zip with all of the users files foreach(OC_User::getUsers() as $user){ - self::addDirToZip( $datadir . '/' . $user . '/', true, "/" ); + self::$content->addDir( $datadir . '/' . $user . '/', true, "/" ); } break; case 'system': + self::$content = new OC_Migration_Content( self::$zip ); // Creates a zip with the owncloud system files - self::addDirToZip( OC::$SERVERROOT . '/', false, '/'); + self::$content->addDir( OC::$SERVERROOT . '/', false, '/'); foreach (array(".git", "3rdparty", "apps", "core", "files", "l10n", "lib", "ocs", "search", "settings", "tests") as $dir) { - self::addDirToZip( OC::$SERVERROOT . '/' . $dir, true, "/"); + self::$content->addDir( OC::$SERVERROOT . '/' . $dir, true, "/"); } - break; + break; + } + if( !$info = self::getExportInfo( $exportdata ) ){ + return false; + } + // Add the export info json to the export zip + self::$content->addFromString( $info, 'export_info.json' ); + if( !self::$content->finish() ){ + return false; + } + return self::$zippath; + } + + /** + * @brief connects to a MDB2 database scheme + * @returns bool + */ + static private function connectScheme(){ + // We need a mdb2 database connection + self::$MDB2->loadModule( 'Manager' ); + self::$MDB2->loadModule( 'Reverse' ); + + // Connect if this did not happen before + if( !self::$schema ){ + require_once('MDB2/Schema.php'); + self::$schema=MDB2_Schema::factory( self::$MDB2 ); } - // Add export info - self::addExportInfo(); - // Close the zip - if( !self::closeZip() ){ - return false; - } - return self::$zippath; - + + return true; } /** - * @breif adds a json file with infomation on the export to the zips root (used on import) - * @return bool - */ - static private function addExportInfo( $array=array() ){ + * @breif creates a migration.db in the users data dir with their app data in + * @return bool whether operation was successfull + */ + private static function exportAppData( ){ + + $success = true; + $return = array(); + + // Foreach provider + foreach( self::$providers as $provider ){ + $success = true; + // Does this app use the database? + if( file_exists( OC::$SERVERROOT.'/apps/'.$provider->getID().'/appinfo/database.xml' ) ){ + // Create some app tables + $tables = self::createAppTables( $provider->getID() ); + if( is_array( $tables ) ){ + // Save the table names + foreach($tables as $table){ + $return['apps'][$provider->getID()]['tables'][] = $table; + } + } else { + // It failed to create the tables + $success = false; + } + } + + // Run the export function? + if( $success ){ + // Set the provider properties + $provider->setData( self::$uid, self::$content ); + $return['apps'][$provider->getID()]['success'] = $provider->export(); + } else { + $return['apps'][$provider->getID()]['success'] = false; + $return['apps'][$provider->getID()]['message'] = 'failed to create the app tables'; + } + + // Now add some app info the the return array + $appinfo = OC_App::getAppInfo( $provider->getID() ); + $return['apps'][$provider->getID()]['version'] = $appinfo['version']; + + } + + return $return; + + } + + + /** + * @breif generates json containing export info, and merges any data supplied + * @param optional $array array of data to include in the returned json + * @return bool + */ + static private function getExportInfo( $array=array() ){ $info = array( 'ocversion' => OC_Util::getVersion(), 'exporttime' => time(), @@ -216,11 +267,12 @@ class OC_Migrate{ 'exporttype' => self::$exporttype ); // Add hash if user export - if( self::$exporttype = 'user' ){ + if( self::$exporttype == 'user' ){ $query = OC_DB::prepare( "SELECT password FROM *PREFIX*users WHERE uid LIKE ?" ); $result = $query->execute( array( self::$uid ) ); $row = $result->fetchRow(); $hash = $row ? $row['password'] : false; + die(var_dump($hash)); if( !$hash ){ OC_Log::write( 'migration', 'Failed to get the users password hash', OC_log::ERROR); return false; @@ -228,110 +280,122 @@ class OC_Migrate{ $info['hash'] = $hash; $info['exporteduser'] = self::$uid; } + if( !is_array( $array ) ){ + OC_Log::write( 'migration', 'Supplied $array was not an array in getExportInfo()', OC_Log::ERROR ); + } // Merge in other data - $info = array_merge( $info, $array ); + $info = array_merge( $info, (array)$array ); // Create json $json = json_encode( $info ); - $tmpfile = tempnam("/tmp", "oc_export_info_"); - self::$tmpfiles[] = $tmpfile; - if( !file_put_contents( $tmpfile, $json ) ){ - return false; - } else { - self::$zip->addFile( $tmpfile, "/" . self::$uid . "/export_info.json" ); - return true; - } - } - - - /** - * @breif tried to finalise the zip - * @return bool - */ - static private function closeZip(){ - if( !self::$zip->close() ){ - OC_Log::write('migration', 'Failed to save the zip with error: '.self::$zip->getStatusString(), OC_Log::ERROR); - self::cleanup(); - return false; - } else { - OC_Log::write('migration', 'Export zip created ok', OC_Log::INFO); - self::cleanup(); - return true; - } + return true; } /** - * @breif cleans up after the zip - */ - static private function cleanup(){ - // Delete tmp files - foreach(self::$tmpfiles as $i){ - unlink( $i ); - } - } - - /** - * @breif creates a zip user export - * @param optional $uid string user id of the user to export (defaults to current) - * @param optional $path string path to folder to create file in (with trailing slash) (defaults to current user's data dir) - * @return false on failure | string path on success - */ - static public function createUserExportFile( $uid=null, $path=null ){ - // User passed? - $uid = is_null( $uid ) ? OC_User::getUser() : $uid ; - // Is a database user? - if( !OC_User_Database::userExists( $uid ) ){ - OC_Log::write('migration', 'User: '.$uid.' is not in the database and so cannot be exported.', OC_Log::ERROR); + * @breif connects to migration.db, or creates if not found + * @param $db optional path to migration.db, defaults to user data dir + * @return bool whether the operation was successful + */ + static private function connectDB( $path=null ){ + // Has the dbpath been set? + self::$dbpath = !is_null( $path ) ? $path : self::$dbpath; + if( !self::$dbpath ){ + OC_Log::write( 'migration', 'connectDB() was called without dbpath being set', OC_Log::ERROR ); return false; } - // Set the uid - self::$uid = $uid; - // Create the zip object - self::$zip = new ZipArchive; - // Set export type - self::$exporttype = 'user'; - // Calculate users data dir - $user = OC_User::getUser(); - $userdatadir = OC_Config::getValue( 'datadirectory' ) . '/' . $user . '/'; - // Calculate zip name - $zipname = "oc_userexport_" . $user . '_' . date("y-m-d_H-i-s") . ".zip"; - // Calculate destination - if( !is_null( $path ) ){ - // Path given - // Is a directory? - if( !is_dir( $path ) ){ - OC_Log::write('migration', 'Path supplied to createUserExportFile() is not a directory', OC_Log::ERROR); - return false; - } - // Is writeable - if( !is_writeable( $path ) ){ - OC_Log::write('migration', 'Path supplied to createUserExportFile() is not writeable', OC_Log::ERROR); + // Already connected + if(!self::$MDB2){ + require_once('MDB2.php'); + + $datadir = OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" ); + + // Prepare options array + $options = array( + 'portability' => MDB2_PORTABILITY_ALL & (!MDB2_PORTABILITY_FIX_CASE), + 'log_line_break' => '
', + 'idxname_format' => '%s', + 'debug' => true, + 'quote_identifier' => true + ); + $dsn = array( + 'phptype' => 'sqlite3', + 'database' => self::$dbpath, + 'mode' => '0644' + ); + + // Try to establish connection + self::$MDB2 = MDB2::factory( $dsn, $options ); + // Die if we could not connect + if( PEAR::isError( self::$MDB2 ) ){ + die( self::$MDB2->getMessage() ); + OC_Log::write( 'migration', 'Failed to create/connect to migration.db', OC_Log::FATAL ); + OC_Log::write( 'migration', self::$MDB2->getUserInfo(), OC_Log::FATAL ); + OC_Log::write( 'migration', self::$MDB2->getMessage(), OC_Log::FATAL ); return false; } - self::$zippath = $path . $zipname; - } else { - // Save in users data dir - self::$zippath = $userdatadir . $zipname; + // We always, really always want associative arrays + self::$MDB2->setFetchMode(MDB2_FETCHMODE_ASSOC); } - // Try to create the zip - if( !self::createZip() ){ - return false; - } - // Export the app info - $appinfo = self::exportAppData(); - // Save the migration results - self::addExportInfo( $appinfo ); - // Add the data dir to the zip - self::addDirToZip( $userdatadir ); - // Close the zip - if( !self::closeZip() ){ - return false; - } - // All good - return self::$zippath; - } + return true; + + } + /** + * @breif creates the tables in migration.db from an apps database.xml + * @param $appid string id of the app + * @return bool whether the operation was successful + */ + static private function createAppTables( $appid ){ + + if( !self::connectScheme() ){ + return false; + } + + // There is a database.xml file + $content = file_get_contents( OC::$SERVERROOT . '/apps/' . $appid . '/appinfo/database.xml' ); + + $file2 = 'static://db_scheme'; + // TODO get the relative path to migration.db from the data dir + // For now just cheat + $path = pathinfo( self::$dbpath ); + $content = str_replace( '*dbname*', self::$uid.'/migration', $content ); + $content = str_replace( '*dbprefix*', '', $content ); + + $xml = new SimpleXMLElement($content); + foreach($xml->table as $table){ + $tables[] = (string)$table->name; + } + + file_put_contents( $file2, $content ); + + // Try to create tables + $definition = self::$schema->parseDatabaseDefinitionFile( $file2 ); + + unlink( $file2 ); + + // Die in case something went wrong + if( $definition instanceof MDB2_Schema_Error ){ + OC_Log::write( 'migration', 'Failed to parse database.xml for: '.$appid, OC_Log::FATAL ); + OC_Log::write( 'migration', $definition->getMessage().': '.$definition->getUserInfo(), OC_Log::FATAL ); + return false; + } + + $definition['overwrite'] = true; + + $ret = self::$schema->createDatabase( $definition ); + + // Die in case something went wrong + if( $ret instanceof MDB2_Error ){ + OC_Log::write( 'migration', 'Failed to create tables for: '.$appid, OC_Log::FATAL ); + OC_Log::write( 'migration', $ret->getMessage().': '.$ret->getUserInfo(), OC_Log::FATAL ); + return false; + } + return $tables; + + } + /** * @breif tries to create the zip + * @param $path string path to zip destination * @return bool */ static private function createZip(){ @@ -340,45 +404,13 @@ class OC_Migrate{ OC_Log::write('migration', 'createZip() called but $zip and/or $zippath have not been set', OC_Log::ERROR); return false; } - if ( self::$zip->open( self::$zippath, ZIPARCHIVE::CREATE ) !== TRUE ) { + if ( self::$zip->open( self::$zippath, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE ) !== TRUE ) { OC_Log::write('migration', 'Failed to create the zip with error: '.self::$zip->getStatusString(), OC_Log::ERROR); return false; } else { return true; } } - - /** - * @breif adds a directory to the zip object - * @param $dir string path of the directory to add - * @param $recursive bool - * @param $internaldir string path of folder to add dir to in zip - * @return bool - */ - static private function addDirToZip($dir, $recursive=true, $internaldir='') { - $dirname = basename($dir); - self::$zip->addEmptyDir($internaldir . $dirname); - $internaldir.=$dirname.='/'; - - if ($dirhandle = opendir($dir)) { - while (false !== ( $file = readdir($dirhandle))) { - - if (( $file != '.' ) && ( $file != '..' )) { - - if (is_dir($dir . '/' . $file) && $recursive) { - self::addDirToZip($dir . '/' . $file, $recursive, $internaldir); - } elseif (is_file($dir . '/' . $file)) { - self::$zip->addFile($dir . '/' . $file, $internaldir . $file); - } - } - } - closedir($dirhandle); - } else { - OC_Log::write('admin_export',"Was not able to open directory: " . $dir,OC_Log::ERROR); - return false; - } - return true; - } /** * @breif returns an array of apps that support migration @@ -429,10 +461,17 @@ class OC_Migrate{ foreach( self::$providers as $provider){ // Is the app in the export? - $id = $provider->id; + $id = $provider->getID(); if( isset( $info->apps->$id ) ){ // Did it succeed? if( $info->apps->$id->success ){ + // Give the provider the content object + // TODO PASS THE PATH TO MIGRATION.DB + if( !self::connectDB() ){ + return false; + } + $content = new OC_Migration_Content( self::$zip, self::$db ); + $provider->setObject( $content ); // Then do the import $provider->import( $info->apps->$id, $importinfo ); } @@ -443,252 +482,12 @@ class OC_Migrate{ } - // @breif connects to migration.db, or creates if not found - // @param $db optional path to migration.db, defaults to user data dir - // @return bool whether the operation was successful - private static function connectDB( $dbpath=null ){ - OC_Log::write('migration','connecting to migration.db for user: '.self::$uid,OC_Log::INFO); - // Fail if no user is set - if(!self::$uid){ - OC_Log::write('migration','connectDB() called without self::$uid being set',OC_Log::INFO); - return false; - } - // Already connected - if(!self::$MDB2){ - require_once('MDB2.php'); - - $datadir = OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" ); - - self::$dbpath = $datadir.'/'.self::$uid.'/migration.db';//!is_null( $dbpath ) ? $dbpath : $datadir.'/'.self::$uid.'/migration.db'; - - // Prepare options array - $options = array( - 'portability' => MDB2_PORTABILITY_ALL & (!MDB2_PORTABILITY_FIX_CASE), - 'log_line_break' => '
', - 'idxname_format' => '%s', - 'debug' => true, - 'quote_identifier' => true - ); - $dsn = array( - 'phptype' => 'sqlite3', - 'database' => self::$dbpath, - 'mode' => '0644' - ); - - // Try to establish connection - self::$MDB2 = MDB2::factory( $dsn, $options ); - // Die if we could not connect - if( PEAR::isError( self::$MDB2 )){ - die(self::$MDB2->getMessage()); - OC_Log::write('migration', 'Failed to create/connect to migration.db',OC_Log::FATAL); - OC_Log::write('migration',self::$MDB2->getUserInfo(),OC_Log::FATAL); - OC_Log::write('migration',self::$MDB2->getMessage(),OC_Log::FATAL); - return false; - } else { - } - // We always, really always want associative arrays - self::$MDB2->setFetchMode(MDB2_FETCHMODE_ASSOC); - } - return true; - - } - - // @breif prepares the db - // @param $query the sql query to prepare - public static function prepare( $query ){ - - // Optimize the query - $query = self::processQuery( $query ); - - // Optimize the query - $query = self::$MDB2->prepare( $query ); - - // Die if we have an error (error means: bad query, not 0 results!) - if( PEAR::isError( $query )) { - $entry = 'DB Error: "'.$result->getMessage().'"
'; - $entry .= 'Offending command was: '.$query.'
'; - OC_Log::write('migration',$entry,OC_Log::FATAL); - return false; - } else { - return $query; - } - - } - - // @breif processes the db query - // @param $query the query to process - // @return string of processed query - private static function processQuery( $query ){ - - self::connectDB(); - $prefix = ''; - - $query = str_replace( '`', '\'', $query ); - $query = str_replace( 'NOW()', 'datetime(\'now\')', $query ); - $query = str_replace( 'now()', 'datetime(\'now\')', $query ); - - // replace table name prefix - $query = str_replace( '*PREFIX*', $prefix, $query ); - - return $query; - - } - - // @brief copys rows to migration.db from the main database - // @param $options array of options. - // @return bool - public static function copyRows( $options ){ - if( !array_key_exists( 'table', $options ) ){ - return false; - } - - $return = array(); - - // Need to include 'where' in the query? - if( array_key_exists( 'matchval', $options ) && array_key_exists( 'matchcol', $options ) ){ - - // If only one matchval, create an array - if(!is_array($options['matchval'])){ - $options['matchval'] = array( $options['matchval'] ); - } - - foreach( $options['matchval'] as $matchval ){ - // Run the query for this match value (where x = y value) - $query = OC_DB::prepare( "SELECT * FROM *PREFIX*" . $options['table'] . " WHERE " . $options['matchcol'] . " LIKE ?" ); - $results = $query->execute( array( $matchval ) ); - $newreturns = self::insertData( $results, $options ); - $return = array_merge( $return, $newreturns ); - } - - } else { - // Just get everything - $query = OC_DB::prepare( "SELECT * FROM *PREFIX*" . $options['table'] ); - $results = $query->execute(); - $return = self::insertData( $results, $options ); - - } - - return $return; - - } - - // @breif saves a sql data set into migration.db - // @param $data a sql data set returned from self::prepare()->query() - // @param $options array of copyRows options - // @return void - private static function insertData( $data, $options ){ - $return = array(); - while( $row = $data->fetchRow() ){ - // Now save all this to the migration.db - $fields = array(); - $values = array(); - foreach($row as $field=>$value){ - $fields[] = $field; - $values[] = $value; - } - - // Generate some sql - $sql = "INSERT INTO `" . $options['table'] . '` ( `'; - $fieldssql = implode( '`, `', $fields ); - $sql .= $fieldssql . "` ) VALUES( "; - $valuessql = substr( str_repeat( '?, ', count( $fields ) ),0,-2 ); - $sql .= $valuessql . " )"; - // Make the query - $query = self::prepare( $sql ); - if(!$query){ - OC_Log::write('migration','Invalid sql produced: '.$sql,OC_Log::FATAL); - return false; - exit(); - } else { - $query->execute( $values ); - // Do we need to return some values? - if( array_key_exists( 'idcol', $options ) ){ - // Yes we do - $return[] = $row[$options['idcol']]; - } else { - // Take a guess and return the first field :) - $return[] = reset($row); - } - } - } - return $return; - } - - // @breif creates the tables in migration.db from an apps database.xml - // @param $appid string id of the app - // @return bool whether the operation was successful - private static function createAppTables( $appid ){ - - if(!self::connectScheme()){ - return false; - } - - // There is a database.xml file - $content = file_get_contents( OC::$SERVERROOT . '/apps/' . $appid . '/appinfo/database.xml' ); - - $file2 = 'static://db_scheme'; - $content = str_replace( '*dbname*', self::$uid.'/migration', $content ); - $content = str_replace( '*dbprefix*', '', $content ); - - $xml = new SimpleXMLElement($content); - foreach($xml->table as $table){ - $tables[] = (string)$table->name; - } - - file_put_contents( $file2, $content ); - - // Try to create tables - $definition = self::$schema->parseDatabaseDefinitionFile( $file2 ); - - unlink( $file2 ); - - // Die in case something went wrong - if( $definition instanceof MDB2_Schema_Error ){ - OC_Log::write('migration','Failed to parse database.xml for: '.$appid,OC_Log::FATAL); - OC_Log::write('migration',$definition->getMessage().': '.$definition->getUserInfo(),OC_Log::FATAL); - return false; - } - - $definition['overwrite'] = true; - - $ret = self::$schema->createDatabase( $definition ); - // Die in case something went wrong - - if( $ret instanceof MDB2_Error ){ - OC_Log::write('migration','Failed to create tables for: '.$appid,OC_Log::FATAL); - OC_Log::write('migration',$ret->getMessage().': '.$ret->getUserInfo(),OC_Log::FATAL); - return false; - } - return $tables; - - } - - - /** - * @brief connects to a MDB2 database scheme - * @returns true/false - * - * Connects to a MDB2 database scheme - */ - private static function connectScheme(){ - // We need a mdb2 database connection - self::connectDB(); - self::$MDB2->loadModule( 'Manager' ); - self::$MDB2->loadModule( 'Reverse' ); - - // Connect if this did not happen before - if( !self::$schema ){ - require_once('MDB2/Schema.php'); - self::$schema=MDB2_Schema::factory( self::$MDB2 ); - } - - return true; - } - - // @breif creates a new user in the database - // @param $uid string user_id of the user to be created - // @param $hash string hash of the user to be created - // @return bool result of user creation + /* + * @breif creates a new user in the database + * @param $uid string user_id of the user to be created + * @param $hash string hash of the user to be created + * @return bool result of user creation + */ public static function createUser( $uid, $hash ){ // Check if userid exists diff --git a/lib/migrate/provider.php b/lib/migrate/provider.php deleted file mode 100644 index 7ac3cf97ca..0000000000 --- a/lib/migrate/provider.php +++ /dev/null @@ -1,28 +0,0 @@ -id = $appid; - OC_Migrate::registerProvider( $this ); - } - - /** - * @breif exports data for apps - * @param string $uid - * @return array appdata to be exported - */ - abstract function export($uid); - - /** - * @breif imports data for the app - * @param $appinfo object with the data that the app exported - * @param $info array of info including exportinfo.json - * @return void - */ - abstract function import( $appinfo, $info ); -} diff --git a/lib/migration/content.php b/lib/migration/content.php new file mode 100644 index 0000000000..fe8a21a45b --- /dev/null +++ b/lib/migration/content.php @@ -0,0 +1,239 @@ +. + * + */ + + +/** + * provides methods to add and access data from the migration + */ +class OC_Migration_Content{ + + private $zip=false; + // Holds the MDB2 object + private $db=false; + // Holds an array of tmpfiles to delete after zip creation + private $tmpfiles=false; + + /** + * @breif sets up the + * @param $zip ZipArchive object + * @param optional $db a MDB2 database object (required for exporttype user) + * @return bool + */ + public function __construct( $zip, $db=false ){ + + $this->zip = $zip; + $this->db = $db; + + } + + // @breif prepares the db + // @param $query the sql query to prepare + public function prepare( $query ){ + + // Optimize the query + $query = $this->processQuery( $query ); + + // Optimize the query + $query = $this->MDB2->prepare( $query ); + + // Die if we have an error (error means: bad query, not 0 results!) + if( PEAR::isError( $query ) ) { + $entry = 'DB Error: "'.$result->getMessage().'"
'; + $entry .= 'Offending command was: '.$query.'
'; + OC_Log::write( 'migration', $entry, OC_Log::FATAL ); + return false; + } else { + return $query; + } + + } + + /** + * @breif processes the db query + * @param $query the query to process + * @return string of processed query + */ + private function processQuery( $query ){ + $query = str_replace( '`', '\'', $query ); + $query = str_replace( 'NOW()', 'datetime(\'now\')', $query ); + $query = str_replace( 'now()', 'datetime(\'now\')', $query ); + // remove table prefixes + $query = str_replace( '*PREFIX*', '', $query ); + return $query; + } + + /** + * @brief copys rows to migration.db from the main database + * @param $options array of options. + * @return bool + */ + public function copyRows( $options ){ + if( !array_key_exists( 'table', $options ) ){ + return false; + } + + $return = array(); + + // Need to include 'where' in the query? + if( array_key_exists( 'matchval', $options ) && array_key_exists( 'matchcol', $options ) ){ + + // If only one matchval, create an array + if(!is_array($options['matchval'])){ + $options['matchval'] = array( $options['matchval'] ); + } + + foreach( $options['matchval'] as $matchval ){ + // Run the query for this match value (where x = y value) + $query = OC_DB::prepare( "SELECT * FROM *PREFIX*" . $options['table'] . " WHERE " . $options['matchcol'] . " LIKE ?" ); + $results = $query->execute( array( $matchval ) ); + $newreturns = $this->insertData( $results, $options ); + $return = array_merge( $return, $newreturns ); + } + + } else { + // Just get everything + $query = OC_DB::prepare( "SELECT * FROM *PREFIX*" . $options['table'] ); + $results = $query->execute(); + $return = $this->insertData( $results, $options ); + + } + + return $return; + + } + + /** + * @breif saves a sql data set into migration.db + * @param $data a sql data set returned from self::prepare()->query() + * @param $options array of copyRows options + * @return void + */ + private function insertData( $data, $options ){ + $return = array(); + while( $row = $data->fetchRow() ){ + // Now save all this to the migration.db + foreach($row as $field=>$value){ + $fields[] = $field; + $values[] = $value; + } + + // Generate some sql + $sql = "INSERT INTO `" . $options['table'] . '` ( `'; + $fieldssql = implode( '`, `', $fields ); + $sql .= $fieldssql . "` ) VALUES( "; + $valuessql = substr( str_repeat( '?, ', count( $fields ) ),0,-2 ); + $sql .= $valuessql . " )"; + // Make the query + $query = $this->prepare( $sql ); + if( !$query ){ + OC_Log::write( 'migration', 'Invalid sql produced: '.$sql, OC_Log::FATAL ); + return false; + exit(); + } else { + $query->execute( $values ); + // Do we need to return some values? + if( array_key_exists( 'idcol', $options ) ){ + // Yes we do + $return[] = $row[$options['idcol']]; + } else { + // Take a guess and return the first field :) + $return[] = reset($row); + } + } + } + return $return; + } + + /** + * @breif adds a directory to the zip object + * @param $dir string path of the directory to add + * @param $recursive bool + * @param $internaldir string path of folder to add dir to in zip + * @return bool + */ + public function addDir( $dir, $recursive=true, $internaldir='' ) { + $dirname = basename($dir); + $this->zip->addEmptyDir($internaldir . $dirname); + $internaldir.=$dirname.='/'; + + if ($dirhandle = opendir($dir)) { + while (false !== ( $file = readdir($dirhandle))) { + + if (( $file != '.' ) && ( $file != '..' )) { + + if (is_dir($dir . '/' . $file) && $recursive) { + $this->addDir($dir . '/' . $file, $recursive, $internaldir); + } elseif (is_file($dir . '/' . $file)) { + $this->zip->addFile($dir . '/' . $file, $internaldir . $file); + } + } + } + closedir($dirhandle); + } else { + OC_Log::write('admin_export',"Was not able to open directory: " . $dir,OC_Log::ERROR); + return false; + } + return true; + } + + /** + * @breif adds a file to the zip from a given string + * @param $data string of data to add + * @param $path the relative path inside of the zip to save the file to + * @return bool + */ + public function addFromString( $data, $path ){ + // Create a temp file + $file = tempnam( get_temp_dir(). '/', 'oc_export_tmp_' ); + $this->tmpfiles[] = $file; + if( !file_put_contents( $file, $data ) ){ + OC_Log::write( 'migation', 'Failed to save data to a temporary file', OC_Log::ERROR ); + return false; + } + // Add file to the zip + $this->zip->addFile( $file, $path ); + return true; + } + + /** + * @breif closes the zip, removes temp files + * @return bool + */ + public function finish(){ + if( !$this->zip->close() ){ + OC_Log::write( 'migration', 'Failed to write the zip file with error: '.$this->zip->getStatusString(), OC_Log::ERROR ); + return false; + } + $this->cleanup(); + return true; + } + + /** + * @breif cleans up after the zip + */ + private function cleanup(){ + // Delete tmp files + foreach($this->tmpfiles as $i){ + unlink( $i ); + } + } +} \ No newline at end of file diff --git a/lib/migration/provider.php b/lib/migration/provider.php new file mode 100644 index 0000000000..b9e2c47620 --- /dev/null +++ b/lib/migration/provider.php @@ -0,0 +1,49 @@ +id = $appid; + OC_Migrate::registerProvider( $this ); + } + + /** + * @breif exports data for apps + * @return array appdata to be exported + */ + abstract function export( ); + + /** + * @breif imports data for the app + * @return void + */ + abstract function import( ); + + /** + * @breif sets the OC_Migration_Content object to $this->content + * @param $content a OC_Migration_Content object + */ + public function setData( $uid, $content, $info=false, $appinfo=false ){ + $this->content = $content; + $this->uid = $uid; + $this->info = $info; + $this->appinfo = $appinfo; + } + + /** + * @breif returns the appid of the provider + * @return string + */ + public function getID(){ + return $this->id; + } +}