summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Rettberg2016-08-30 18:02:43 +0200
committerSimon Rettberg2016-08-30 18:02:43 +0200
commitf45886abed5f04728561d5c8f97423a8036806fc (patch)
tree1639d97cb76658925aaf4ddc19b080b8ea273e53
parent(WiP) Global image sync (diff)
downloadmasterserver-f45886abed5f04728561d5c8f97423a8036806fc.tar.gz
masterserver-f45886abed5f04728561d5c8f97423a8036806fc.tar.xz
masterserver-f45886abed5f04728561d5c8f97423a8036806fc.zip
Implement global image exchange with satellite servers
-rw-r--r--src/main/java/org/openslx/imagemaster/Globals.java6
-rw-r--r--src/main/java/org/openslx/imagemaster/db/Paginator.java10
-rw-r--r--src/main/java/org/openslx/imagemaster/db/mappers/DbImage.java196
-rw-r--r--src/main/java/org/openslx/imagemaster/db/mappers/DbImageBlock.java67
-rw-r--r--src/main/java/org/openslx/imagemaster/db/mappers/DbUser.java15
-rw-r--r--src/main/java/org/openslx/imagemaster/db/models/LocalUser.java4
-rw-r--r--src/main/java/org/openslx/imagemaster/serverconnection/ConnectionHandler.java167
-rw-r--r--src/main/java/org/openslx/imagemaster/serverconnection/IncomingTransfer.java45
-rw-r--r--src/main/java/org/openslx/imagemaster/serverconnection/OutgoingTransfer.java22
-rw-r--r--src/main/java/org/openslx/imagemaster/session/SessionManager.java45
-rw-r--r--src/main/java/org/openslx/imagemaster/thrift/server/MasterServerHandler.java160
-rw-r--r--src/main/java/org/openslx/imagemaster/util/Sha512Crypt.java2
-rw-r--r--src/main/java/org/openslx/imagemaster/util/UserUtil.java62
13 files changed, 713 insertions, 88 deletions
diff --git a/src/main/java/org/openslx/imagemaster/Globals.java b/src/main/java/org/openslx/imagemaster/Globals.java
index aebc198..7b634f0 100644
--- a/src/main/java/org/openslx/imagemaster/Globals.java
+++ b/src/main/java/org/openslx/imagemaster/Globals.java
@@ -126,6 +126,12 @@ public class Globals
return 86400l * 500;
}
+ public static long getOldImageExpireTimeSeconds()
+ {
+ // TODO Auto-generated method stub
+ return 86400l * 30;
+ }
+
/* STRINGS */
public static String getImageDir()
diff --git a/src/main/java/org/openslx/imagemaster/db/Paginator.java b/src/main/java/org/openslx/imagemaster/db/Paginator.java
index 267a1a3..ad6bb9f 100644
--- a/src/main/java/org/openslx/imagemaster/db/Paginator.java
+++ b/src/main/java/org/openslx/imagemaster/db/Paginator.java
@@ -1,11 +1,13 @@
package org.openslx.imagemaster.db;
-public class Paginator {
+public class Paginator
+{
- public static final int PER_PAGE = 200;
+ public static final int PER_PAGE = 400;
- public static String limitStatement(int page) {
- return " LIMIT " + (page * PER_PAGE) + ", " + PER_PAGE;
+ public static String limitStatement( int page )
+ {
+ return " LIMIT " + ( page * PER_PAGE ) + ", " + PER_PAGE;
}
}
diff --git a/src/main/java/org/openslx/imagemaster/db/mappers/DbImage.java b/src/main/java/org/openslx/imagemaster/db/mappers/DbImage.java
index f4c3ddc..6fddf76 100644
--- a/src/main/java/org/openslx/imagemaster/db/mappers/DbImage.java
+++ b/src/main/java/org/openslx/imagemaster/db/mappers/DbImage.java
@@ -1,16 +1,27 @@
package org.openslx.imagemaster.db.mappers;
+import java.nio.ByteBuffer;
import java.sql.ResultSet;
import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
import org.apache.log4j.Logger;
+import org.openslx.bwlp.thrift.iface.ImageDetailsRead;
+import org.openslx.bwlp.thrift.iface.ImagePermissions;
import org.openslx.bwlp.thrift.iface.ImagePublishData;
+import org.openslx.bwlp.thrift.iface.ImageSummaryRead;
+import org.openslx.bwlp.thrift.iface.ImageVersionDetails;
import org.openslx.bwlp.thrift.iface.InvocationError;
+import org.openslx.bwlp.thrift.iface.ShareMode;
import org.openslx.bwlp.thrift.iface.TInvocationException;
+import org.openslx.bwlp.thrift.iface.TNotFoundException;
import org.openslx.imagemaster.Globals;
import org.openslx.imagemaster.db.Database;
import org.openslx.imagemaster.db.MysqlConnection;
import org.openslx.imagemaster.db.MysqlStatement;
+import org.openslx.imagemaster.db.Paginator;
+import org.openslx.thrifthelper.ImagePublishDataEx;
import org.openslx.util.Util;
/**
@@ -22,9 +33,40 @@ public class DbImage
private static final Logger LOGGER = Logger.getLogger( DbImage.class );
- public static ImagePublishData getImageVersion( String imageVersionId )
+ private static final ImagePermissions emptyPermissions = new ImagePermissions();
+
+ public static ImagePublishDataEx getImageVersion( String imageVersionId ) throws SQLException
{
- return null;
+ try ( MysqlConnection connection = Database.getConnection() ) {
+ MysqlStatement stmt = connection.prepareStatement( "SELECT v.imageversionid, v.imagebaseid, v.createtime, v.filesize, v.filepath,"
+ + " v.uploaderid, v.isvalid, v.virtualizerconfig, b.displayname, b.description, b.osid, b.virtid, b.ownerid, b.istemplate"
+ + " FROM imageversion v INNER JOIN imagebase b USING (imagebaseid)"
+ + " WHERE v.imageversionid = :imageversionid" );
+ stmt.setString( "imageversionid", imageVersionId );
+ ResultSet rs = stmt.executeQuery();
+ if ( !rs.next() )
+ return null;
+ ImagePublishDataEx img = new ImagePublishDataEx();
+ img.imageVersionId = rs.getString( "imageversionid" );
+ img.imageBaseId = rs.getString( "imagebaseid" );
+ img.createTime = rs.getLong( "createtime" );
+ img.fileSize = rs.getLong( "filesize" );
+ img.uploader = DbUser.getUserInfoOrNull( connection, rs.getString( "uploaderid" ) );
+ img.imageName = rs.getString( "displayname" );
+ img.description = rs.getString( "description" );
+ img.osId = rs.getInt( "osid" );
+ img.virtId = rs.getString( "virtid" );
+ img.owner = DbUser.getUserInfoOrNull( connection, rs.getString( "ownerid" ) );
+ img.isTemplate = rs.getBoolean( "istemplate" );
+ img.machineDescription = ByteBuffer.wrap( rs.getBytes( "virtualizerconfig" ) );
+ // Ex
+ img.exImagePath = rs.getString( "filepath" );
+ img.exIsValid = rs.getBoolean( "isvalid" );
+ return img;
+ } catch ( SQLException e ) {
+ LOGGER.error( "Query failed in DbImage.getImageVersion()", e );
+ throw e;
+ }
}
public static void createImageBase( ImagePublishData img ) throws TInvocationException
@@ -44,7 +86,7 @@ public class DbImage
+ " istemplate = :istemplate WHERE imagebaseid = :baseid" );
stmt2.setString( "baseid", img.imageBaseId );
stmt2.setString( "displayname", img.imageName );
- stmt2.setString( "updaterid", img.user.userId );
+ stmt2.setString( "updaterid", img.uploader.userId );
stmt2.setString( "description", img.description );
stmt2.setInt( "osid", img.osId );
stmt2.setBoolean( "istemplate", img.isTemplate );
@@ -62,8 +104,8 @@ public class DbImage
stmt2.setInt( "osid", img.osId );
stmt2.setString( "virtid", img.virtId );
stmt2.setLong( "createtime", img.createTime );
- stmt2.setString( "ownerid", img.user.userId );
- stmt2.setString( "updaterid", img.user.userId );
+ stmt2.setString( "ownerid", img.owner.userId );
+ stmt2.setString( "updaterid", img.uploader.userId );
stmt2.setBoolean( "istemplate", img.isTemplate );
stmt2.executeUpdate();
}
@@ -83,14 +125,15 @@ public class DbImage
+ " filepath, uploaderid, isvalid, isprocessed, mastersha1, virtualizerconfig)"
+ " VALUES "
+ " (:imageversionid, :imagebaseid, :createtime, :expiretime, :filesize,"
- + " :filepath, :uploaderid, 0, 0, NULL, NULL)" );
+ + " :filepath, :uploaderid, 0, 0, NULL, :virtualizerconfig)" );
verStmt.setString( "imageversionid", img.imageVersionId );
verStmt.setString( "imagebaseid", img.imageBaseId );
verStmt.setLong( "createtime", img.createTime );
verStmt.setLong( "expiretime", Util.unixTime() + Globals.getImageValiditySeconds() );
verStmt.setLong( "filesize", img.fileSize );
verStmt.setString( "filepath", relLocalPath );
- verStmt.setString( "uploaderid", img.user.userId );
+ verStmt.setString( "uploaderid", img.uploader.userId );
+ verStmt.setBinary( "virtualizerconfig", img.getMachineDescription() );
verStmt.execute();
connection.commit();
} catch ( SQLException e ) {
@@ -99,4 +142,143 @@ public class DbImage
}
}
+ public static void markValid( String imageVersionId, boolean isValid ) throws SQLException
+ {
+ try ( MysqlConnection connection = Database.getConnection() ) {
+ MysqlStatement stmt = connection.prepareStatement( "UPDATE imageversion"
+ + " SET isvalid = :isvalid WHERE imageversionid = :imageversionid" );
+ stmt.setBoolean( "isvalid", isValid );
+ stmt.setString( "imageversionid", imageVersionId );
+ stmt.executeUpdate();
+ updateLatestForVersion( connection, imageVersionId );
+ connection.commit();
+ } catch ( SQLException e ) {
+ LOGGER.error( "Query failed in DbImage.markValid()", e );
+ throw e;
+ }
+ }
+
+ private static void updateLatestForVersion( MysqlConnection connection, String imageVersionId ) throws SQLException
+ {
+ MysqlStatement stmt = connection.prepareStatement( "SELECT imagebaseid FROM imageversion"
+ + " WHERE imageversionid = :imageversionid" );
+ stmt.setString( "imageversionid", imageVersionId );
+ ResultSet rs = stmt.executeQuery();
+ if ( !rs.next() )
+ return;
+ updateLatestForBase( connection, rs.getString( "imagebaseid" ) );
+ }
+
+ private static void updateLatestForBase( MysqlConnection connection, String imageBaseId ) throws SQLException
+ {
+ MysqlStatement stmt = connection.prepareStatement( "SELECT imageversionid FROM imageversion"
+ + " WHERE isvalid <> 0 AND expiretime > UNIX_TIMESTAMP() AND imagebaseid = :imagebaseid"
+ + " ORDER BY createtime DESC LIMIT 1" );
+ stmt.setString( "imagebaseid", imageBaseId );
+ ResultSet rs = stmt.executeQuery();
+ String latestVersionId = null;
+ if ( rs.next() ) {
+ latestVersionId = rs.getString( "imageversionid" );
+ }
+ MysqlStatement updateLatestRef = connection.prepareStatement( "UPDATE imagebase SET latestversionid = :latestversionid"
+ + " WHERE imagebaseid = :imagebaseid" );
+ updateLatestRef.setString( "latestversionid", latestVersionId );
+ updateLatestRef.setString( "imagebaseid", imageBaseId );
+ updateLatestRef.executeUpdate();
+ MysqlStatement expireStmt = connection.prepareStatement( "UPDATE imageversion SET expiretime = :expiretime"
+ + " WHERE imagebaseid = :imagebaseid AND imageversionid <> :latestversionid" );
+ expireStmt.setString( "imagebaseid", imageBaseId );
+ expireStmt.setString( "latestversionid", latestVersionId );
+ expireStmt.setLong( "expiretime", Util.unixTime() + Globals.getOldImageExpireTimeSeconds() );
+ expireStmt.executeUpdate();
+ }
+
+ public static List<ImageSummaryRead> getPublicList( int page ) throws SQLException, TInvocationException
+ {
+ if ( page < 0 )
+ throw new TInvocationException( InvocationError.INVALID_DATA, "page must be >= 0" );
+ try ( MysqlConnection connection = Database.getConnection() ) {
+ MysqlStatement stmt = connection.prepareStatement( "SELECT v.imageversionid, v.imagebaseid, v.createtime, v.filesize,"
+ + " v.uploaderid, b.displayname, b.description, b.osid, b.virtid, b.ownerid, b.istemplate FROM imagebase b"
+ + " INNER JOIN imageversion v ON (b.latestversionid = v.imageversionid)"
+ + " WHERE v.isvalid = 1"
+ + " ORDER BY imageversionid " + Paginator.limitStatement( page ) );
+ ResultSet rs = stmt.executeQuery();
+ List<ImageSummaryRead> list = new ArrayList<>();
+ while ( rs.next() ) {
+ ImageSummaryRead img = new ImageSummaryRead();
+ img.createTime = rs.getLong( "createtime" );
+ //img.description = rs.getString( "description" );
+ img.fileSize = rs.getLong( "filesize" );
+ img.imageBaseId = rs.getString( "imagebaseid" );
+ img.imageName = rs.getString( "displayname" );
+ img.latestVersionId = rs.getString( "imageversionid" );
+ img.isTemplate = rs.getBoolean( "istemplate" );
+ img.osId = rs.getInt( "osid" );
+ img.ownerId = rs.getString( "ownerid" );
+ //img.software = rs.get( "" ); TODO
+ //img.tags = rs.get( "" ); TODO
+ img.uploaderId = rs.getString( "uploaderid" );
+ img.virtId = rs.getString( "virtid" );
+ img.userPermissions = emptyPermissions;
+ img.defaultPermissions = emptyPermissions;
+ list.add( img );
+ }
+ return list;
+ } catch ( SQLException e ) {
+ LOGGER.error( "Query failed in DbImage.getPublicList()", e );
+ throw e;
+ }
+ }
+
+ protected static List<ImageVersionDetails> getImageVersions( MysqlConnection connection, String imageBaseId )
+ throws SQLException
+ {
+ List<ImageVersionDetails> versionList = new ArrayList<>();
+ MysqlStatement stmt = connection.prepareStatement( "SELECT"
+ + " imageversionid, createtime, expiretime, filesize, uploaderid,"
+ + " isprocessed FROM imageversion"
+ + " WHERE imagebaseid = :imagebaseid AND isvalid = 1" );
+ stmt.setString( "imagebaseid", imageBaseId );
+ ResultSet rs = stmt.executeQuery();
+ while ( rs.next() ) {
+ List<String> software = null; // DbSoftwareTag.getImageVersionSoftwareList(connection, imageVersionId);
+ String imageVersionId = rs.getString( "imageversionid" );
+ versionList.add( new ImageVersionDetails( imageVersionId, rs.getLong( "createtime" ),
+ rs.getLong( "expiretime" ), rs.getLong( "filesize" ), rs.getString( "uploaderid" ),
+ true, true,
+ rs.getBoolean( "isprocessed" ), software ) );
+ }
+ stmt.close();
+ return versionList;
+ }
+
+ public static ImageDetailsRead getImageDetails( String imageBaseId ) throws TNotFoundException, SQLException
+ {
+ try ( MysqlConnection connection = Database.getConnection() ) {
+ MysqlStatement stmt = connection.prepareStatement( "SELECT i.imagebaseid, i.latestversionid, i.displayname,"
+ + " i.description, i.osid, i.virtid, i.createtime, i.updatetime, i.ownerid, i.updaterid, i.istemplate"
+ + " FROM imagebase i"
+ + " WHERE i.imagebaseid = :imagebaseid" );
+ stmt.setString( "imagebaseid", imageBaseId );
+ ResultSet rs = stmt.executeQuery();
+ if ( !rs.next() )
+ throw new TNotFoundException();
+ // Exists:
+ List<String> tags = null; // DbSoftwareTag.getImageTags(connection, imageBaseId);
+ List<ImageVersionDetails> versions = getImageVersions( connection, imageBaseId );
+ ImageDetailsRead image = new ImageDetailsRead( rs.getString( "imagebaseid" ),
+ rs.getString( "latestversionid" ), versions, rs.getString( "displayname" ),
+ rs.getString( "description" ), tags, rs.getInt( "osid" ), rs.getString( "virtid" ),
+ rs.getLong( "createtime" ), rs.getLong( "updatetime" ), rs.getString( "ownerid" ),
+ rs.getString( "updaterid" ), ShareMode.FROZEN,
+ rs.getBoolean( "istemplate" ), emptyPermissions );
+ image.userPermissions = emptyPermissions;
+ return image;
+ } catch ( SQLException e ) {
+ LOGGER.error( "Query failed in DbImage.getImageDetails()", e );
+ throw e;
+ }
+ }
+
}
diff --git a/src/main/java/org/openslx/imagemaster/db/mappers/DbImageBlock.java b/src/main/java/org/openslx/imagemaster/db/mappers/DbImageBlock.java
index 7986d87..155f522 100644
--- a/src/main/java/org/openslx/imagemaster/db/mappers/DbImageBlock.java
+++ b/src/main/java/org/openslx/imagemaster/db/mappers/DbImageBlock.java
@@ -1,6 +1,9 @@
package org.openslx.imagemaster.db.mappers;
+import java.nio.ByteBuffer;
+import java.sql.ResultSet;
import java.sql.SQLException;
+import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
@@ -109,4 +112,68 @@ public class DbImageBlock
}
}
+ public static List<Boolean> getMissingStatusList( String imageVersionId ) throws SQLException
+ {
+ try ( MysqlConnection connection = Database.getConnection() ) {
+ MysqlStatement stmt = connection.prepareStatement( "SELECT startbyte, ismissing FROM imageblock"
+ + " WHERE imageversionid = :imageversionid ORDER BY startbyte ASC" );
+ stmt.setString( "imageversionid", imageVersionId );
+ ResultSet rs = stmt.executeQuery();
+ List<Boolean> list = new ArrayList<>();
+ long expectedOffset = 0;
+ while ( rs.next() ) {
+ long currentOffset = rs.getLong( "startbyte" );
+ if ( currentOffset < expectedOffset )
+ continue;
+ while ( currentOffset > expectedOffset ) {
+ list.add( Boolean.TRUE );
+ expectedOffset += FileChunk.CHUNK_SIZE;
+ }
+ if ( currentOffset == expectedOffset ) {
+ list.add( rs.getBoolean( "ismissing" ) );
+ expectedOffset += FileChunk.CHUNK_SIZE;
+ }
+ }
+ return list;
+ } catch ( SQLException e ) {
+ LOGGER.error( "Query failed in DbImageBlock.getBlockStatuses()", e );
+ throw e;
+ }
+ }
+
+ public static List<ByteBuffer> getBlockHashes( String imageVersionId ) throws SQLException
+ {
+ try ( MysqlConnection connection = Database.getConnection() ) {
+ return getBlockHashes( connection, imageVersionId );
+ } catch ( SQLException e ) {
+ LOGGER.error( "Query failed in DbImageBlock.getBlockHashes()", e );
+ throw e;
+ }
+ }
+
+ private static List<ByteBuffer> getBlockHashes( MysqlConnection connection, String imageVersionId )
+ throws SQLException
+ {
+ MysqlStatement stmt = connection.prepareStatement( "SELECT startbyte, blocksha1 FROM imageblock"
+ + " WHERE imageversionid = :imageversionid ORDER BY startbyte ASC" );
+ stmt.setString( "imageversionid", imageVersionId );
+ ResultSet rs = stmt.executeQuery();
+ List<ByteBuffer> list = new ArrayList<>();
+ long expectedOffset = 0;
+ while ( rs.next() ) {
+ long currentOffset = rs.getLong( "startbyte" );
+ if ( currentOffset < expectedOffset )
+ continue;
+ while ( currentOffset > expectedOffset ) {
+ list.add( null );
+ expectedOffset += FileChunk.CHUNK_SIZE;
+ }
+ if ( currentOffset == expectedOffset ) {
+ list.add( ByteBuffer.wrap( rs.getBytes( "blocksha1" ) ) );
+ expectedOffset += FileChunk.CHUNK_SIZE;
+ }
+ }
+ return list;
+ }
+
}
diff --git a/src/main/java/org/openslx/imagemaster/db/mappers/DbUser.java b/src/main/java/org/openslx/imagemaster/db/mappers/DbUser.java
index b6040e7..00ec45e 100644
--- a/src/main/java/org/openslx/imagemaster/db/mappers/DbUser.java
+++ b/src/main/java/org/openslx/imagemaster/db/mappers/DbUser.java
@@ -73,7 +73,18 @@ public class DbUser
throw new TNotFoundException();
return user.toUserInfo();
}
-
+
+ static UserInfo getUserInfoOrNull( MysqlConnection connection, String userId ) throws SQLException
+ {
+ MysqlStatement stmt = connection.prepareStatement( localUserSql
+ + " WHERE user.userid = :userid" );
+ stmt.setString( "userid", userId );
+ ResultSet rs = stmt.executeQuery();
+ if ( !rs.next() )
+ return null;
+ return localFromRs( rs ).toUserInfo();
+ }
+
public static List<UserInfo> findUser( String organizationId, String searchTerm )
{
// TODO Implement
@@ -84,7 +95,7 @@ public class DbUser
{
return exists( user, false );
}
-
+
public static boolean exists( UserInfo user, boolean withIdentity )
{
if ( user == null )
diff --git a/src/main/java/org/openslx/imagemaster/db/models/LocalUser.java b/src/main/java/org/openslx/imagemaster/db/models/LocalUser.java
index bc9289a..39468c4 100644
--- a/src/main/java/org/openslx/imagemaster/db/models/LocalUser.java
+++ b/src/main/java/org/openslx/imagemaster/db/models/LocalUser.java
@@ -18,7 +18,7 @@ public class LocalUser
public final Role role;
public LocalUser( String login, String password, String organizationId, String firstName, String lastName, String eMail,
- Role tutor )
+ Role role )
{
this.login = login;
this.organizationId = organizationId;
@@ -26,7 +26,7 @@ public class LocalUser
this.firstName = firstName;
this.lastName = lastName;
this.eMail = eMail;
- this.role = tutor;
+ this.role = role;
}
@Override
diff --git a/src/main/java/org/openslx/imagemaster/serverconnection/ConnectionHandler.java b/src/main/java/org/openslx/imagemaster/serverconnection/ConnectionHandler.java
index 141e17f..f32c655 100644
--- a/src/main/java/org/openslx/imagemaster/serverconnection/ConnectionHandler.java
+++ b/src/main/java/org/openslx/imagemaster/serverconnection/ConnectionHandler.java
@@ -1,10 +1,13 @@
package org.openslx.imagemaster.serverconnection;
+import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.security.KeyStore;
+import java.sql.SQLException;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -21,13 +24,17 @@ import org.openslx.bwlp.thrift.iface.ImagePublishData;
import org.openslx.bwlp.thrift.iface.InvocationError;
import org.openslx.bwlp.thrift.iface.TInvocationException;
import org.openslx.bwlp.thrift.iface.TTransferRejectedException;
+import org.openslx.bwlp.thrift.iface.TransferInformation;
import org.openslx.filetransfer.Downloader;
import org.openslx.filetransfer.IncomingEvent;
import org.openslx.filetransfer.Listener;
import org.openslx.filetransfer.Uploader;
-import org.openslx.filetransfer.util.AbstractTransfer;
import org.openslx.imagemaster.Globals;
+import org.openslx.imagemaster.db.mappers.DbImage;
+import org.openslx.thrifthelper.ImagePublishDataEx;
import org.openslx.util.GrowingThreadPoolExecutor;
+import org.openslx.util.QuickTimer;
+import org.openslx.util.QuickTimer.Task;
/**
* Class to handle all incoming and outgoing connections.
@@ -40,8 +47,10 @@ public class ConnectionHandler implements IncomingEvent
private static final int MAX_TRANSFERS = 12;
- private static Map<String, IncomingTransfer> incomingTransfers = new ConcurrentHashMap<>();
- private static Map<String, AbstractTransfer> outgoingTransfers = new ConcurrentHashMap<>();
+ private static Map<String, IncomingTransfer> incomingTransfersByTransferId = new ConcurrentHashMap<>();
+ private static final Map<String, IncomingTransfer> incomingTransfersByVersionId = new ConcurrentHashMap<>();
+
+ private static Map<String, OutgoingTransfer> outgoingTransfers = new ConcurrentHashMap<>();
private static IncomingEvent eventHandler = new ConnectionHandler();
private final ExecutorService transferPool = new GrowingThreadPoolExecutor( 1, MAX_TRANSFERS * 2, 1, TimeUnit.MINUTES,
new SynchronousQueue<Runnable>(),
@@ -51,7 +60,7 @@ public class ConnectionHandler implements IncomingEvent
private static final Listener sslListener;
static {
- LOGGER.debug( "Starting listener on port " + Globals.getFiletransferPortSsl() );
+ LOGGER.debug( "Starting BFTP on port " + Globals.getFiletransferPortSsl() + "+ and " + Globals.getFiletransferPortPlain() );
Listener ssl = null;
Listener plain = null;
try {
@@ -68,32 +77,89 @@ public class ConnectionHandler implements IncomingEvent
ssl.start();
plain = new Listener( eventHandler, null, Globals.getFiletransferPortPlain(), Globals.getFiletransferTimeout() * 1000 );
plain.start();
+ // TODO: Bail out/retry if failed, getters for ports
} catch ( Exception e ) {
LOGGER.error( "Initialization failed.", e );
System.exit( 2 );
}
sslListener = ssl;
plainListener = plain;
+ QuickTimer.scheduleAtFixedDelay( new Task() {
+
+ @Override
+ public void fire()
+ {
+ long now = System.currentTimeMillis();
+ for ( Iterator<IncomingTransfer> it = incomingTransfersByTransferId.values().iterator(); it.hasNext(); ) {
+ IncomingTransfer t = it.next();
+ if ( t.isComplete( now ) || t.hasReachedIdleTimeout( now ) ) {
+ LOGGER.debug( "Removing transfer " + t.getId() );
+ it.remove();
+ }
+ }
+ for ( Iterator<IncomingTransfer> it = incomingTransfersByVersionId.values().iterator(); it.hasNext(); ) {
+ IncomingTransfer t = it.next();
+ if ( t.isComplete( now ) || t.hasReachedIdleTimeout( now ) ) {
+ it.remove();
+ }
+ }
+ }
+ }, 10000, 301000 );
+ }
+
+ public static int getSslPort()
+ {
+ if ( sslListener.isRunning() )
+ return sslListener.getPort();
+ return 0;
+ }
+
+ public static int getPlainPort()
+ {
+ if ( plainListener.isRunning() )
+ return plainListener.getPort();
+ return 0;
}
- public static IncomingTransfer registerUpload( ImagePublishData img, List<ByteBuffer> blockHashes )
+ /**
+ * Register new incoming transfer from a satellite server.
+ *
+ * @param img the desired meta data for the new image/version, as supplied by the satellite
+ * @param blockHashes list of block hashes for the image
+ * @param existing OPTIONAL if the image version already exists on the server, this is given so
+ * we can repair/complete the local file, if necessary
+ * @return The assigned transfer object
+ */
+ public static IncomingTransfer registerUpload( ImagePublishData img, List<ByteBuffer> blockHashes, ImagePublishDataEx existing )
throws TTransferRejectedException, TInvocationException
{
IncomingTransfer transfer;
- synchronized ( incomingTransfers ) {
- transfer = incomingTransfers.get( img.imageVersionId );
+ synchronized ( incomingTransfersByTransferId ) {
+ transfer = incomingTransfersByVersionId.get( img.imageVersionId );
if ( transfer == null ) {
if ( getUploadConnectionCount() >= MAX_TRANSFERS ) {
throw new TTransferRejectedException( "Too many active transfers" );
}
+ File absDestination;
+ if ( existing == null ) {
+ absDestination = new File( new File( Globals.getImageDir(), img.imageBaseId ), img.imageVersionId );
+ } else {
+ absDestination = new File( Globals.getImageDir(), existing.exImagePath );
+ }
+ plainListener.start();
+ sslListener.start();
try {
- transfer = new IncomingTransfer( img, blockHashes );
+ transfer = new IncomingTransfer( img, blockHashes, absDestination, getPlainPort(), getSslPort() );
} catch ( FileNotFoundException e ) {
- LOGGER.warn( "Cannot init download", e );
+ LOGGER.warn( "Cannot init download to " + absDestination.toString(), e );
throw new TInvocationException( InvocationError.INTERNAL_SERVER_ERROR, "File access error" );
}
- incomingTransfers.put( transfer.getId(), transfer );
- incomingTransfers.put( img.imageVersionId, transfer );
+ LOGGER.info( "New incoming upload: " + transfer.getId() + " for " + img.imageVersionId + " (" + img.imageName + ")" );
+ incomingTransfersByTransferId.put( transfer.getId(), transfer );
+ incomingTransfersByVersionId.put( img.imageVersionId, transfer );
+ } else {
+ LOGGER.info( "Another request for existing upload: " + transfer.getId() + " for " + img.imageVersionId + " (" + img.imageName
+ + ")" );
}
}
return transfer;
@@ -102,7 +168,7 @@ public class ConnectionHandler implements IncomingEvent
public static IncomingTransfer getExistingUpload( ImagePublishData imageData, List<ByteBuffer> crcSums )
throws TTransferRejectedException
{
- IncomingTransfer transfer = incomingTransfers.get( imageData.imageVersionId );
+ IncomingTransfer transfer = incomingTransfersByVersionId.get( imageData.imageVersionId );
if ( transfer == null )
return null;
if ( transfer.getFileSize() != imageData.fileSize )
@@ -112,15 +178,31 @@ public class ConnectionHandler implements IncomingEvent
return transfer;
}
+ public static IncomingTransfer getUploadByToken( String uploadToken )
+ {
+ return incomingTransfersByTransferId.get( uploadToken );
+ }
+
/**
* Server is uploading - client is downloading!
*/
@Override
public void incomingDownloadRequest( final Uploader uploader )
{
- // TODO
- uploader.sendErrorCode( "Too many concurrent uploads." );
- uploader.cancel();
+ OutgoingTransfer transfer = outgoingTransfers.get( uploader.getToken() );
+ if ( transfer == null ) {
+ LOGGER.debug( "Unknown download token received" );
+ uploader.sendErrorCode( "Unknown download token." );
+ uploader.cancel();
+ return;
+ }
+ if ( getDownloadConnectionCount() >= MAX_TRANSFERS ) {
+ uploader.sendErrorCode( "Too many concurrent uploads." );
+ uploader.cancel();
+ }
+ if ( !transfer.addConnection( uploader, transferPool ) ) {
+ uploader.cancel();
+ }
}
/**
@@ -129,7 +211,7 @@ public class ConnectionHandler implements IncomingEvent
@Override
public void incomingUploadRequest( final Downloader downloader ) throws IOException
{
- IncomingTransfer transfer = incomingTransfers.get( downloader.getToken() );
+ IncomingTransfer transfer = incomingTransfersByTransferId.get( downloader.getToken() );
if ( transfer == null ) {
downloader.sendErrorCode( "Unknown upload token." );
downloader.cancel();
@@ -149,7 +231,7 @@ public class ConnectionHandler implements IncomingEvent
{
final long now = System.currentTimeMillis();
int active = 0;
- for ( IncomingTransfer t : incomingTransfers.values() ) {
+ for ( IncomingTransfer t : incomingTransfersByTransferId.values() ) {
if ( t.countsTowardsConnectionLimit( now ) ) {
active += t.getActiveConnectionCount();
}
@@ -157,4 +239,55 @@ public class ConnectionHandler implements IncomingEvent
return active;
}
+ public static int getDownloadConnectionCount()
+ {
+ final long now = System.currentTimeMillis();
+ int active = 0;
+ for ( OutgoingTransfer t : outgoingTransfers.values() ) {
+ if ( t.countsTowardsConnectionLimit( now ) ) {
+ active += t.getActiveConnectionCount();
+ }
+ }
+ return active;
+ }
+
+ public static void removeUpload( IncomingTransfer transfer )
+ {
+ incomingTransfersByTransferId.remove( transfer.getId() );
+ incomingTransfersByVersionId.remove( transfer.getImageVersionId() );
+ }
+
+ public static TransferInformation registerDownload( ImagePublishDataEx img ) throws TTransferRejectedException, TInvocationException
+ {
+ OutgoingTransfer transfer;
+ File absSource;
+ absSource = new File( Globals.getImageDir(), img.exImagePath );
+ if ( !absSource.exists() ) {
+ LOGGER.error( absSource.toString() + " missing!" );
+ try {
+ DbImage.markValid( img.imageVersionId, false );
+ } catch ( SQLException e ) {
+ }
+ throw new TTransferRejectedException( "File missing on server" );
+ }
+ if ( absSource.length() != img.fileSize ) {
+ LOGGER.error( absSource.toString() + " has unexpected size (is: " + absSource.length() + ", should: " + img.fileSize + ")" );
+ try {
+ DbImage.markValid( img.imageVersionId, false );
+ } catch ( SQLException e ) {
+ }
+ throw new TTransferRejectedException( "File corrupted on server" );
+ }
+ synchronized ( outgoingTransfers ) {
+ if ( getDownloadConnectionCount() >= MAX_TRANSFERS ) {
+ throw new TTransferRejectedException( "Too many active transfers" );
+ }
+ plainListener.start();
+ sslListener.start();
+ transfer = new OutgoingTransfer( absSource, getPlainPort(), getSslPort() );
+ outgoingTransfers.put( transfer.getId(), transfer );
+ }
+ return transfer.getTransferInfo();
+ }
+
}
diff --git a/src/main/java/org/openslx/imagemaster/serverconnection/IncomingTransfer.java b/src/main/java/org/openslx/imagemaster/serverconnection/IncomingTransfer.java
index bfc65e1..53842be 100644
--- a/src/main/java/org/openslx/imagemaster/serverconnection/IncomingTransfer.java
+++ b/src/main/java/org/openslx/imagemaster/serverconnection/IncomingTransfer.java
@@ -3,9 +3,11 @@ package org.openslx.imagemaster.serverconnection;
import java.io.File;
import java.io.FileNotFoundException;
import java.nio.ByteBuffer;
+import java.sql.SQLException;
import java.util.List;
import java.util.UUID;
+import org.apache.log4j.Logger;
import org.openslx.bwlp.thrift.iface.ImagePublishData;
import org.openslx.bwlp.thrift.iface.TInvocationException;
import org.openslx.bwlp.thrift.iface.TransferInformation;
@@ -13,6 +15,7 @@ import org.openslx.filetransfer.util.ChunkStatus;
import org.openslx.filetransfer.util.FileChunk;
import org.openslx.filetransfer.util.IncomingTransferBase;
import org.openslx.imagemaster.Globals;
+import org.openslx.imagemaster.db.mappers.DbImage;
import org.openslx.imagemaster.db.mappers.DbImageBlock;
import org.openslx.imagemaster.util.Util;
import org.openslx.util.ThriftUtil;
@@ -20,16 +23,33 @@ import org.openslx.util.ThriftUtil;
public class IncomingTransfer extends IncomingTransferBase
{
+ private static final Logger LOGGER = Logger.getLogger( IncomingTransfer.class );
+
private static final long MIN_FREE_SPACE_BYTES = FileChunk.CHUNK_SIZE * 10;
private final String imageVersionId;
- public IncomingTransfer( ImagePublishData img, List<ByteBuffer> blockHashes )
+ private final TransferInformation transferInfo;
+
+ public IncomingTransfer( ImagePublishData img, List<ByteBuffer> blockHashes, File absDestination, int plainPort, int sslPort )
throws TInvocationException, FileNotFoundException
{
- super( UUID.randomUUID().toString(), new File( new File( Globals.getImageDir(), img.imageBaseId ), img.imageVersionId ),
- img.fileSize, ThriftUtil.unwrapByteBufferList( blockHashes ) );
+ super( UUID.randomUUID().toString(), absDestination, img.fileSize, ThriftUtil.unwrapByteBufferList( blockHashes ) );
this.imageVersionId = img.imageVersionId;
+ this.transferInfo = new TransferInformation( getId(), plainPort, sslPort );
+ // If the file already exists, see if any chunks are already complete
+ if ( absDestination.exists() && absDestination.length() > 0 ) {
+ try {
+ List<Boolean> statusList = DbImageBlock.getMissingStatusList( img.imageVersionId );
+ if ( !statusList.isEmpty() ) {
+ getChunks().resumeFromStatusList( statusList, absDestination.length() );
+ for ( int i = 0; i < 3; ++i ) {
+ queueUnhashedChunk( false );
+ }
+ }
+ } catch ( SQLException e ) {
+ }
+ }
}
@Override
@@ -56,18 +76,30 @@ public class IncomingTransfer extends IncomingTransferBase
protected boolean finishIncomingTransfer()
{
potentialFinishTime.set( System.currentTimeMillis() );
+ try {
+ DbImage.markValid( this.imageVersionId, true );
+ } catch ( SQLException e ) {
+ // Nothing to do
+ }
return true;
}
@Override
public TransferInformation getTransferInfo()
{
- return new TransferInformation( getId(), Globals.getFiletransferPortPlain(), Globals.getFiletransferPortSsl() );
+ return transferInfo;
}
@Override
protected void chunkStatusChanged( FileChunk chunk )
{
+ if ( chunk.getFailCount() > 6 ) {
+ cancel();
+ LOGGER.warn( "Server is cancelling upload of Version " + imageVersionId
+ + ": Hash check for block " + chunk.getChunkIndex()
+ + " failed " + chunk.getFailCount()
+ + " times." );
+ }
ChunkStatus status = chunk.getStatus();
if ( status == ChunkStatus.MISSING || status == ChunkStatus.COMPLETE ) {
try {
@@ -78,4 +110,9 @@ public class IncomingTransfer extends IncomingTransferBase
}
}
+ public Object getImageVersionId()
+ {
+ return imageVersionId;
+ }
+
}
diff --git a/src/main/java/org/openslx/imagemaster/serverconnection/OutgoingTransfer.java b/src/main/java/org/openslx/imagemaster/serverconnection/OutgoingTransfer.java
new file mode 100644
index 0000000..a6f80b2
--- /dev/null
+++ b/src/main/java/org/openslx/imagemaster/serverconnection/OutgoingTransfer.java
@@ -0,0 +1,22 @@
+package org.openslx.imagemaster.serverconnection;
+
+import java.io.File;
+import java.util.UUID;
+
+import org.openslx.filetransfer.util.OutgoingTransferBase;
+
+public class OutgoingTransfer extends OutgoingTransferBase
+{
+
+ public OutgoingTransfer( File sourceFile, int plainPort, int sslPort )
+ {
+ super( UUID.randomUUID().toString(), sourceFile, plainPort, sslPort );
+ }
+
+ @Override
+ public String getRelativePath()
+ {
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/openslx/imagemaster/session/SessionManager.java b/src/main/java/org/openslx/imagemaster/session/SessionManager.java
index c141d24..e9f133e 100644
--- a/src/main/java/org/openslx/imagemaster/session/SessionManager.java
+++ b/src/main/java/org/openslx/imagemaster/session/SessionManager.java
@@ -47,22 +47,6 @@ public class SessionManager
return new ClientSessionData( sessionId, authToken, sats, ui );
}
- public static Session getSessionFromToken( String token )
- {
- if ( token == null || token.length() != 32 ) {
- log.debug( "invalid token format: " + token );
- return null;
- }
- final Session session;
- synchronized ( sessions ) {
- session = sessions.get( token );
- }
- if ( session == null || session.timedOut() ) {
- return null;
- }
- return session;
- }
-
static {
QuickTimer.scheduleAtFixedDelay( new Task() {
@Override
@@ -81,6 +65,22 @@ public class SessionManager
}, 123, TimeUnit.MINUTES.toMillis( 13 ) );
}
+ public static Session getSessionFromToken( String token )
+ {
+ if ( token == null || token.length() != 32 ) {
+ log.debug( "invalid token format: " + token );
+ return null;
+ }
+ final Session session;
+ synchronized ( sessions ) {
+ session = sessions.get( token );
+ }
+ if ( session == null || session.timedOut() ) {
+ return null;
+ }
+ return session;
+ }
+
public static Session getSessionFromSessionId( String sessionId )
{
if ( sessionId == null || sessionId.length() != 64 ) {
@@ -94,6 +94,19 @@ public class SessionManager
if ( session == null || session.timedOut() ) {
return null;
}
+ session.refresh();
+ return session;
+ }
+
+ public static Session getSessionFromSessionIdOrToken( String sessionId )
+ {
+ final Session session;
+ synchronized ( sessions ) {
+ session = sessions.get( sessionId );
+ }
+ if ( session == null || session.timedOut() ) {
+ return null;
+ }
return session;
}
diff --git a/src/main/java/org/openslx/imagemaster/thrift/server/MasterServerHandler.java b/src/main/java/org/openslx/imagemaster/thrift/server/MasterServerHandler.java
index 60f4ccb..3bdbd3f 100644
--- a/src/main/java/org/openslx/imagemaster/thrift/server/MasterServerHandler.java
+++ b/src/main/java/org/openslx/imagemaster/thrift/server/MasterServerHandler.java
@@ -13,7 +13,9 @@ import org.apache.log4j.Logger;
import org.apache.thrift.TException;
import org.openslx.bwlp.thrift.iface.AuthorizationError;
import org.openslx.bwlp.thrift.iface.ClientSessionData;
+import org.openslx.bwlp.thrift.iface.ImageDetailsRead;
import org.openslx.bwlp.thrift.iface.ImagePublishData;
+import org.openslx.bwlp.thrift.iface.ImageSummaryRead;
import org.openslx.bwlp.thrift.iface.InvocationError;
import org.openslx.bwlp.thrift.iface.MasterServer;
import org.openslx.bwlp.thrift.iface.MasterSoftware;
@@ -29,9 +31,11 @@ import org.openslx.bwlp.thrift.iface.TInvocationException;
import org.openslx.bwlp.thrift.iface.TNotFoundException;
import org.openslx.bwlp.thrift.iface.TTransferRejectedException;
import org.openslx.bwlp.thrift.iface.TransferInformation;
+import org.openslx.bwlp.thrift.iface.TransferStatus;
import org.openslx.bwlp.thrift.iface.UserInfo;
import org.openslx.bwlp.thrift.iface.Virtualizer;
import org.openslx.encryption.AsymKeyHolder;
+import org.openslx.filetransfer.util.ChunkList;
import org.openslx.filetransfer.util.FileChunk;
import org.openslx.imagemaster.Globals;
import org.openslx.imagemaster.db.Database;
@@ -43,7 +47,6 @@ import org.openslx.imagemaster.db.mappers.DbPendingSatellite;
import org.openslx.imagemaster.db.mappers.DbSatellite;
import org.openslx.imagemaster.db.mappers.DbUser;
import org.openslx.imagemaster.db.models.LocalSatellite;
-import org.openslx.imagemaster.db.models.LocalUser;
import org.openslx.imagemaster.serverconnection.ConnectionHandler;
import org.openslx.imagemaster.serverconnection.IncomingTransfer;
import org.openslx.imagemaster.serversession.ServerAuthenticator;
@@ -52,7 +55,9 @@ import org.openslx.imagemaster.serversession.ServerSessionManager;
import org.openslx.imagemaster.session.Authenticator;
import org.openslx.imagemaster.session.Session;
import org.openslx.imagemaster.session.SessionManager;
+import org.openslx.imagemaster.util.UserUtil;
import org.openslx.imagemaster.util.Util;
+import org.openslx.thrifthelper.ImagePublishDataEx;
public class MasterServerHandler implements MasterServer.Iface
{
@@ -131,11 +136,33 @@ public class MasterServerHandler implements MasterServer.Iface
}
@Override
- public List<ImagePublishData> getPublicImages( String sessionId, int page )
+ public List<ImageSummaryRead> getPublicImages( String sessionId, int page )
throws TAuthorizationException, TInvocationException
{
- // TODO Auto-generated method stub
- return null;
+ Session session = SessionManager.getSessionFromSessionId( sessionId );
+ if ( session == null )
+ throw new TAuthorizationException( AuthorizationError.NOT_AUTHENTICATED, "Session ID not valid" );
+ UserUtil.assertTutor( session.getUserInfo() );
+ try {
+ return DbImage.getPublicList( page );
+ } catch ( SQLException e ) {
+ throw new TInvocationException( InvocationError.INTERNAL_SERVER_ERROR, "Database failure" );
+ }
+ }
+
+ @Override
+ public ImageDetailsRead getImageDetails( String sessionId, String imageBaseId )
+ throws TAuthorizationException, TNotFoundException, TInvocationException
+ {
+ Session session = SessionManager.getSessionFromSessionId( sessionId );
+ if ( session == null )
+ throw new TAuthorizationException( AuthorizationError.NOT_AUTHENTICATED, "Session ID not valid" );
+ UserUtil.assertTutor( session.getUserInfo() );
+ try {
+ return DbImage.getImageDetails( imageBaseId );
+ } catch ( SQLException e ) {
+ throw new TInvocationException( InvocationError.INTERNAL_SERVER_ERROR, "Database failure" );
+ }
}
@Override
@@ -214,8 +241,15 @@ public class MasterServerHandler implements MasterServer.Iface
public ImagePublishData getImageData( String serverSessionId, String imageVersionId )
throws TAuthorizationException, TInvocationException, TNotFoundException
{
- // TODO Auto-generated method stub
- return null;
+ Session session = SessionManager.getSessionFromSessionIdOrToken( serverSessionId );
+ if ( session == null )
+ throw new TAuthorizationException( AuthorizationError.INVALID_TOKEN, "Unknown session id/token" );
+ UserUtil.assertTutor( session.getUserInfo() );
+ try {
+ return DbImage.getImageVersion( imageVersionId );
+ } catch ( SQLException e ) {
+ throw new TInvocationException( InvocationError.INTERNAL_SERVER_ERROR, "Database error" );
+ }
}
@Override
@@ -226,6 +260,9 @@ public class MasterServerHandler implements MasterServer.Iface
Session session = SessionManager.getSessionFromToken( userToken );
if ( session == null )
throw new TAuthorizationException( AuthorizationError.INVALID_TOKEN, "Given user token not known to the server" );
+ UserUtil.assertTutor( session.getUserInfo() );
+ img.owner = UserUtil.getFirstPublishingUser( img.owner, img.uploader, session.getUserInfo() );
+ img.uploader = UserUtil.getFirstPublishingUserOrDummy( img.uploader, session.getUserInfo() );
// check image data
if ( Util.isEmpty( img.imageName ) )
throw new TInvocationException( InvocationError.INVALID_DATA, "Image name not set" );
@@ -235,8 +272,10 @@ public class MasterServerHandler implements MasterServer.Iface
throw new TInvocationException( InvocationError.MISSING_DATA, "ImagePublishData has invalid imageBaseId" );
if ( !Util.isUUID( img.imageVersionId ) )
throw new TInvocationException( InvocationError.MISSING_DATA, "ImagePublishData has invalid imageVersionId" );
- if ( img.user == null || img.user.userId == null )
- throw new TInvocationException( InvocationError.MISSING_DATA, "Missing user id" );
+ if ( img.owner == null || img.owner.userId == null )
+ throw new TInvocationException( InvocationError.MISSING_DATA, "Missing owner or owner is anonymous" );
+ if ( img.uploader == null || img.uploader.userId == null )
+ throw new TInvocationException( InvocationError.MISSING_DATA, "Missing uploader or uploader is anonymous" );
// check for complete block hash list
boolean listComplete = false;
if ( blockHashes != null && blockHashes.size() == FileChunk.fileSizeToChunkCount( img.fileSize ) ) {
@@ -253,48 +292,53 @@ public class MasterServerHandler implements MasterServer.Iface
// Check if an upload is already assigned
IncomingTransfer existingUpload = ConnectionHandler.getExistingUpload( img, blockHashes );
if ( existingUpload != null ) {
+ LOGGER.info( "Satellite tried to register already existing upload for version " + img.imageVersionId + " - is "
+ + existingUpload.getId() );
return existingUpload.getTransferInfo();
}
+ // Check if version already exists
+ ImagePublishDataEx existing;
+ try {
+ existing = DbImage.getImageVersion( img.imageVersionId );
+ if ( existing != null ) {
+ if ( existing.fileSize != img.fileSize )
+ throw new TInvocationException( InvocationError.INVALID_DATA, "Image already exists; file size mismatch" );
+ List<ByteBuffer> existingHashes = DbImageBlock.getBlockHashes( img.imageVersionId );
+ if ( !ChunkList.hashListsEqualBbBb( blockHashes, existingHashes ) )
+ throw new TInvocationException( InvocationError.INVALID_DATA, "Image already exists; block hashes mismatch" );
+ }
+ } catch ( SQLException e ) {
+ throw new TInvocationException( InvocationError.INTERNAL_SERVER_ERROR, "Internal database error" );
+ }
// No existing upload - create new one
// checks that hit the db
if ( !DbOsVirt.osExists( img.osId ) )
throw new TInvocationException( InvocationError.INVALID_DATA, "Content operating system not set" );
if ( !DbOsVirt.virtExists( img.virtId ) )
throw new TInvocationException( InvocationError.INVALID_DATA, "Content virtualizer system not set" );
- try {
- LocalUser user = DbUser.forUserId( img.user.userId );
- if ( user == null ) {
- user = DbUser.forUserId( session.getUserInfo().userId );
- if ( user != null ) {
- img.user = user.toUserInfo();
- }
- }
- if ( user == null )
- throw new TInvocationException( InvocationError.UNKNOWN_USER, "Unknown user id " + img.user.userId );
- if ( user.isAnonymous() )
- throw new TInvocationException( InvocationError.UNKNOWN_USER, "The owner of the image does not participate in image exchange" );
- } catch ( SQLException e ) {
- throw new TInvocationException( InvocationError.INTERNAL_SERVER_ERROR, "Database error" );
- }
// Make sure we have a destination to write to
if ( !new File( Globals.getImageDir() ).isDirectory() )
throw new TInvocationException( InvocationError.INTERNAL_SERVER_ERROR, "Storage offline" );
// Try to register an upload
- IncomingTransfer transfer = ConnectionHandler.registerUpload( img, blockHashes );
+ IncomingTransfer transfer = ConnectionHandler.registerUpload( img, blockHashes, existing );
try {
DbImage.createImageBase( img );
} catch ( TException t ) {
transfer.cancel();
+ ConnectionHandler.removeUpload( transfer );
throw t;
}
- try {
- DbImage.createImageVersion( img, transfer.getRelativePath() );
- } catch ( SQLException e1 ) {
- transfer.cancel();
- if ( Database.isDuplicateKeyException( e1 ) ) {
- throw new TInvocationException( InvocationError.INVALID_DATA, "The image already exists on the server" );
- } else {
- throw new TInvocationException( InvocationError.INTERNAL_SERVER_ERROR, "Database error" );
+ if ( existing == null ) {
+ try {
+ DbImage.createImageVersion( img, transfer.getRelativePath() );
+ } catch ( SQLException e1 ) {
+ transfer.cancel();
+ ConnectionHandler.removeUpload( transfer );
+ if ( Database.isDuplicateKeyException( e1 ) ) {
+ throw new TInvocationException( InvocationError.INVALID_DATA, "The image already exists on the server" );
+ } else {
+ throw new TInvocationException( InvocationError.INTERNAL_SERVER_ERROR, "Database error" );
+ }
}
}
try {
@@ -307,10 +351,27 @@ public class MasterServerHandler implements MasterServer.Iface
@Override
public TransferInformation downloadImage( String sessionId, String imageVersionId )
- throws TAuthorizationException, TInvocationException, TNotFoundException
+ throws TAuthorizationException, TInvocationException, TNotFoundException, TTransferRejectedException
{
- // TODO Auto-generated method stub
- return null;
+ // Valid submit session?
+ Session session = SessionManager.getSessionFromToken( sessionId );
+ if ( session == null )
+ throw new TAuthorizationException( AuthorizationError.INVALID_TOKEN, "Given user token not known to the server" );
+ UserUtil.assertTutor( session.getUserInfo() );
+ ImagePublishDataEx img;
+ List<ByteBuffer> blockHashes;
+ try {
+ img = DbImage.getImageVersion( imageVersionId );
+ blockHashes = DbImageBlock.getBlockHashes( img.imageVersionId );
+ } catch ( SQLException e ) {
+ throw new TInvocationException( InvocationError.INTERNAL_SERVER_ERROR, "Database error" );
+ }
+ if ( img == null || !img.exIsValid )
+ throw new TNotFoundException();
+ TransferInformation ti = ConnectionHandler.registerDownload( img );
+ ti.machineDescription = img.machineDescription;
+ ti.blockHashes = blockHashes;
+ return ti;
}
@Override
@@ -398,4 +459,33 @@ public class MasterServerHandler implements MasterServer.Iface
return false;
}
+ @Override
+ public TransferStatus queryUploadStatus( String uploadToken ) throws TInvalidTokenException
+ {
+ IncomingTransfer upload = ConnectionHandler.getUploadByToken( uploadToken );
+ if ( upload == null )
+ throw new TInvalidTokenException();
+ return upload.getStatus();
+ }
+
+ @Override
+ public UserInfo getUser( String userToken, String userId )
+ throws TAuthorizationException, TNotFoundException, TInvocationException
+ {
+ Session session = SessionManager.getSessionFromToken( userToken );
+ if ( session == null )
+ throw new TAuthorizationException( AuthorizationError.NOT_AUTHENTICATED, "No valid user token" );
+ UserInfo userInfo = session.getUserInfo();
+ UserUtil.assertTutor( userInfo );
+ UserInfo queriedUser;
+ try {
+ queriedUser = DbUser.getUserInfo( userToken );
+ } catch ( SQLException e ) {
+ throw new TInvocationException( InvocationError.INTERNAL_SERVER_ERROR, "Database broken" );
+ }
+ if ( UserUtil.getFirstPublishingUser( queriedUser ) == null )
+ throw new TNotFoundException( "Unknown userid" );
+ return queriedUser;
+ }
+
}
diff --git a/src/main/java/org/openslx/imagemaster/util/Sha512Crypt.java b/src/main/java/org/openslx/imagemaster/util/Sha512Crypt.java
index bcd4b99..c4a4b61 100644
--- a/src/main/java/org/openslx/imagemaster/util/Sha512Crypt.java
+++ b/src/main/java/org/openslx/imagemaster/util/Sha512Crypt.java
@@ -416,7 +416,7 @@ public final class Sha512Crypt
try
{
- int srounds = Integer.valueOf(num).intValue();
+ Integer.valueOf(num).intValue();
}
catch (NumberFormatException ex)
{
diff --git a/src/main/java/org/openslx/imagemaster/util/UserUtil.java b/src/main/java/org/openslx/imagemaster/util/UserUtil.java
new file mode 100644
index 0000000..2e447f3
--- /dev/null
+++ b/src/main/java/org/openslx/imagemaster/util/UserUtil.java
@@ -0,0 +1,62 @@
+package org.openslx.imagemaster.util;
+
+import java.sql.SQLException;
+
+import org.openslx.bwlp.thrift.iface.AuthorizationError;
+import org.openslx.bwlp.thrift.iface.Role;
+import org.openslx.bwlp.thrift.iface.TAuthorizationException;
+import org.openslx.bwlp.thrift.iface.TNotFoundException;
+import org.openslx.bwlp.thrift.iface.UserInfo;
+import org.openslx.imagemaster.db.mappers.DbUser;
+
+public class UserUtil
+{
+
+ /**
+ * Given a list of users, return the first one that isn't anonymous, which means they opted in
+ * for global image sharing.
+ */
+ public static UserInfo getFirstPublishingUser( UserInfo... user )
+ {
+ if ( user == null )
+ return null;
+ for ( UserInfo u : user ) {
+ if ( Util.isEmpty( u.userId ) )
+ continue;
+ try {
+ u = DbUser.getUserInfo( u.userId );
+ } catch ( SQLException | TNotFoundException e ) {
+ continue;
+ }
+ if ( !Util.isEmpty( u.eMail )
+ && ( !Util.isEmpty( u.firstName ) || !Util.isEmpty( u.lastName ) ) ) {
+ return u;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Given a list of users, return the first one that isn't anonymous, which means they opted in
+ * for global image sharing. If none matches, return the dummy user.
+ */
+ public static UserInfo getFirstPublishingUserOrDummy( UserInfo... user )
+ {
+ UserInfo ret = getFirstPublishingUser( user );
+ if ( ret != null )
+ return ret;
+ try {
+ return DbUser.getUserInfo( "dummy" );
+ } catch ( TNotFoundException | SQLException e ) {
+ return null;
+ }
+ }
+
+ public static void assertTutor( UserInfo userInfo ) throws TAuthorizationException
+ {
+ if ( userInfo == null || userInfo.role != Role.TUTOR ) {
+ throw new TAuthorizationException( AuthorizationError.NO_PERMISSION, "Permission denied!" );
+ }
+ }
+
+}