summaryrefslogblamecommitdiffstats
path: root/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/thrift/ServerHandler.java
blob: a4c033cc0e034c45cebdaee3d1c1f0c944745ebd (plain) (tree)
1
2
3
4
5
6
7
8
9
                                    
 
                    
                           
                             
                           

                      
 
                               
                                    
                                          
                                              
                                                     
                                                                 
                                                                
                                                       
                                                             
                                                               
                                                                   
                                                                  
                                                        
                                                   
                                                         
                                                    
                                                             
                                                              
                                                      
                                                


                                                          
                                                        
                                             
                                                             

                                                          
                                            
                                           
                                           
                                                        
                                                    


                                                      
                                                      
                                                      
                                                         
                                                       
                                                     
                                                



                                                        
                                              
                                              

                                                     
                                                    
                                                     
                                                     
                                                     

                                                         
                                               
                                                             
                                                       
                                                            
                                                          


                                                                
                                                    
                                                   

                                                 
                                                
                                              
                                            
                                              
                                   
                             
 
                                                             
 
                                                                                   
 
                                                                           

                 



                                                                    
         




                                                            
 
                 
                                                   
                                           

         



                        
                 
                                                                                                                 
                                                                                                                       
                                                                                                       
                                                                    

                                                                                                                        
                                                               



                                                                           
                                                         
                 


                                                                                               
                                             
                                                                               
                                      

                                                                                                                       



                                                                                                                     
                                                                                                         
                                                       
                                                                    
                                                                                       

                                                           
                                                                                     



                                                     













                                                                                                                      
                                                      
                                                                                       
                                     
                                                                                 
                                        
                 
 
         
 
                 
                                                                                                   
                                                                                       



                                                                                     


                                                           
         

                 
                                                                                           
                                                                                                 

                                                                    

                                                                                                                        






                                                                                                   
                                              


                                                                                                               
                                                         
                 

                                                                                                             


                                                                          


                 
                                                          
                                                                                             

                                          
         
 



                                    
                 
                                                                                                            
                                                              

         
                 
                                                                                                         

                                                                                              


                 
                                                         
                                                 

         



                                                                       
                 
                                                            
                                                 

         
                 
                                                    
                                             
         
 
                 
                                                         
                                              
         
 




                                                                                                   



                                                             
                 























                                                                                                               
                                                                                                      
                                                                              

                                                                    
                     
                                                                            
                                          
                                                         
                 
         
 
                 
                                                                                     
                                                                                                  
                                                                    
                                                    


                                                                          
                                                         
                 
         
 
                 
                                                                                                     
                                              

                                                                    
                                                                                  
                                                                                                              
                     


                                                                                               
                                          
                                                         
                 


                 
                                                                                                 
                                                                                                  
                                                                    


                                                                                                  
                                                                                                              
                                                                                                                       
                     


                                                                                                                         
                                                                                                          





                                                                                                         
                                                


                                                                                                                       
                                                                                                          




                                                                                                                     
                                                                                                             
                                                                                                                  

                                                                                
                                                         
                 


                 
                                                                                                        
                                                                                                  
                                                                    
                                                                                                              
                                                                                          



                                                                           
                                                                     
                     






                                                                                                                         
                                                                                
                                           
                                                         
                 


                 
                                                                                                               
                                                                  
                                                                    

                                                                                                                        
                                                                       
                     

                                                                                       
                                                                
                                                                                     

                                                                                                                              
                                               
                                          
                                                         
                 
                                                        

         
                 
                                                                                                         
                                                                  




                                                                                  
                                                         

                                                        

                                                                        
                                                                           






                                                                 
                                                                                  


                                                                                                            
                                                           
                 

                                                                                                                                    
                     


                                                                   
                 


                 
                                                                               
                                                                                                                      
                                              
                                                                    
                                                                      


                                                                                       
                                                         
                 

         
                 
                                                                                                      
                                                                                                  
                                                                    
                                                                                     


                                                                                          
                                                         
                 

         
                 
                                                                                          
                                                                                                              
                                                                    
                                                                  
                     




                                                                                                                         
                                                                             




                                                                                                                     
                                          
                                                         



                 


                                                                                                                     
                                                                                      


                                                                                                                  

                                                                    











                                                                                   
                                                                         



                                                                  
                                                                                                     
                                                                                                 
                                                                               






                                                                                                                    




                                                                                                  

                                                                       

                                                                                                                        




                                                                                                                   

                                                                                           




                                                                                             


                                                                                   

                                                                              

                                                                                                       



                                                                                             
                                                 




                                                                                                                 


                 
                                                                                  

                                                                                                 












                                                                                                                




                                                                                                                     








                                                                                                                   
                                                                                                           
                                                                                     
                                                                          
                                                                                                                           

                                                                                                          
                                                                                                                       

                                                                    
                                                                        
                                                            
                     


                                                                                                                                    
                                          
                                                         
                 

         
                 
                                                                                                  
                                                                                                                     

                                                                                                                           

                                                                                                                 
                                                                                                                       
                                                                    











                                                                                                      
                                                                         
                     
                                                                          
                                          
                                                         
                 


                 
                                                                                                              
                                              




                                                                                                                                  
                                                         
                 


                 
                                                                                                                
                                                                  




                                                                            
                                                         
                 


                 
                                                                                                     
                                                                  


                                                                    
                                                                                              

                                                               
                                                                                                                                    
                                          
                                                         
                 


                 
                                                                               
                                                                                                                        
                                              




                                                                                     
                                                         
                 


                 
                                                                                                        
                                                                                                  




                                                                                        
                                                         
                 

         
                 
                                                                                          
                                                                                                              
                                                                    






                                                                               
                     
                                                                        




                                                                                                                     
                                          
                                                         
                 


                 
                                                                                                     
                                              
                                                    


                                                   
                                                         


                 

                                                                





                                                                                             

         


















                                                                                                                                 
                                                                                      

                                                                                      








                                                                                                                              
         
 


                                                                                                 
                                                                                      
                                                           
                     
                                                                              



                                                                                                   
                     
                                                                           



                                                                                                     





                                                                                                  





                                                                                                    


                            
 
package org.openslx.bwlp.sat.thrift;

import java.io.File;
import java.nio.ByteBuffer;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.apache.thrift.TException;
import org.openslx.bwlp.sat.RuntimeConfig;
import org.openslx.bwlp.sat.SupportedFeatures;
import org.openslx.bwlp.sat.database.mappers.DbImage;
import org.openslx.bwlp.sat.database.mappers.DbImage.DeleteState;
import org.openslx.bwlp.sat.database.mappers.DbImagePermissions;
import org.openslx.bwlp.sat.database.mappers.DbLecture;
import org.openslx.bwlp.sat.database.mappers.DbLectureFilter;
import org.openslx.bwlp.sat.database.mappers.DbLectureNetshare;
import org.openslx.bwlp.sat.database.mappers.DbLectureNetworkRules;
import org.openslx.bwlp.sat.database.mappers.DbLecturePermissions;
import org.openslx.bwlp.sat.database.mappers.DbLocation;
import org.openslx.bwlp.sat.database.mappers.DbLog;
import org.openslx.bwlp.sat.database.mappers.DbRunScript;
import org.openslx.bwlp.sat.database.mappers.DbUser;
import org.openslx.bwlp.sat.database.models.ImageVersionMeta;
import org.openslx.bwlp.sat.database.models.LocalImageVersion;
import org.openslx.bwlp.sat.database.models.LocalUser;
import org.openslx.bwlp.sat.fileserv.FileServer;
import org.openslx.bwlp.sat.fileserv.IncomingDataTransfer;
import org.openslx.bwlp.sat.fileserv.OutgoingDataTransfer;
import org.openslx.bwlp.sat.fileserv.SyncTransferHandler;
import org.openslx.bwlp.sat.maintenance.DeleteOldImages;
import org.openslx.bwlp.sat.permissions.User;
import org.openslx.bwlp.sat.thrift.cache.OperatingSystemList;
import org.openslx.bwlp.sat.thrift.cache.OrganizationList;
import org.openslx.bwlp.sat.thrift.cache.VirtualizerList;
import org.openslx.bwlp.sat.util.FileSystem;
import org.openslx.bwlp.sat.util.Formatter;
import org.openslx.bwlp.sat.util.Sanitizer;
import org.openslx.bwlp.thrift.iface.AuthorizationError;
import org.openslx.bwlp.thrift.iface.DateParamError;
import org.openslx.bwlp.thrift.iface.ImageBaseWrite;
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.ImageVersionWrite;
import org.openslx.bwlp.thrift.iface.InvocationError;
import org.openslx.bwlp.thrift.iface.LdapFilter;
import org.openslx.bwlp.thrift.iface.LecturePermissions;
import org.openslx.bwlp.thrift.iface.LectureRead;
import org.openslx.bwlp.thrift.iface.LectureSummary;
import org.openslx.bwlp.thrift.iface.LectureWrite;
import org.openslx.bwlp.thrift.iface.Location;
import org.openslx.bwlp.thrift.iface.NetShare;
import org.openslx.bwlp.thrift.iface.OperatingSystem;
import org.openslx.bwlp.thrift.iface.Organization;
import org.openslx.bwlp.thrift.iface.PredefinedData;
import org.openslx.bwlp.thrift.iface.PresetRunScript;
import org.openslx.bwlp.thrift.iface.SatelliteConfig;
import org.openslx.bwlp.thrift.iface.SatelliteServer;
import org.openslx.bwlp.thrift.iface.SatelliteStatus;
import org.openslx.bwlp.thrift.iface.SatelliteUserConfig;
import org.openslx.bwlp.thrift.iface.ShareMode;
import org.openslx.bwlp.thrift.iface.TAuthorizationException;
import org.openslx.bwlp.thrift.iface.TInvalidDateParam;
import org.openslx.bwlp.thrift.iface.TInvalidTokenException;
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.UploadOptions;
import org.openslx.bwlp.thrift.iface.UserInfo;
import org.openslx.bwlp.thrift.iface.Virtualizer;
import org.openslx.bwlp.thrift.iface.WhoamiInfo;
import org.openslx.sat.thrift.version.Version;
import org.openslx.thrifthelper.Comparators;
import org.openslx.thrifthelper.ThriftManager;
import org.openslx.util.ThriftUtil;
import org.openslx.util.Util;

public class ServerHandler implements SatelliteServer.Iface {

	private static final Logger LOGGER = Logger.getLogger(ServerHandler.class);

	private static final FileServer fileServer = FileServer.instance();

	@Override
	public long getVersion(long clientVersion) {
		if (clientVersion >= Version.VERSION)
			return Version.VERSION;
		return Math.max(clientVersion, Version.MIN_VERSION);
	}
	
	@Override
	public String getSupportedFeatures() {
		return SupportedFeatures.getFeatureString();
	}

	@Override
	public SatelliteConfig getConfiguration() {
		return RuntimeConfig.get();
	}

	/*
	 * File Transfer
	 */

	@Override
	public TransferInformation requestImageVersionUpload(String userToken, String imageBaseId, long fileSize,
			List<ByteBuffer> blockHashes, ByteBuffer machineDescription) throws TTransferRejectedException,
			TAuthorizationException, TInvocationException, TNotFoundException, TException {
		UserInfo user = SessionManager.getOrFail(userToken);
		if (!FileSystem.waitForStorage())
			throw new TInvocationException(InvocationError.INTERNAL_SERVER_ERROR, "VM storage not mounted");
		User.canEditBaseImageOrFail(user, imageBaseId);
		ImageDetailsRead image;
		try {
			image = DbImage.getImageDetails(user, imageBaseId);
		} catch (SQLException e) {
			throw new TInvocationException();
		}
		if (image.shareMode != ShareMode.LOCAL && image.shareMode != ShareMode.PUBLISH)
			throw new TAuthorizationException(AuthorizationError.NO_PERMISSION,
					"Cannot upload new versions of a replicated image");
		// Unwrap machine description
		byte[] mDesc = ThriftUtil.unwrapByteBuffer(machineDescription);
		// Unwrap sha1sum list
		List<byte[]> hashList = ThriftUtil.unwrapByteBufferList(blockHashes);
		IncomingDataTransfer transfer = fileServer.createNewUserUpload(user, image, fileSize, hashList, mDesc);
		return new TransferInformation(transfer.getId(), fileServer.getPlainPort(), fileServer.getSslPort());
	}

	@Override
	public void updateBlockHashes(String uploadToken, List<ByteBuffer> blockHashes, String userToken)
			throws TInvalidTokenException {
		// TODO: Validate user token some time in the future
		IncomingDataTransfer upload = fileServer.getUploadByToken(uploadToken);
		if (upload == null)
			throw new TInvalidTokenException();
		List<byte[]> hashList = ThriftUtil.unwrapByteBufferList(blockHashes);
		upload.updateBlockHashList(hashList);
	}

	@Override
	public UploadOptions setUploadOptions(String userToken, String uploadToken, UploadOptions options)
			throws TAuthorizationException, TInvalidTokenException, TException {
		IncomingDataTransfer upload = fileServer.getUploadByToken(uploadToken);
		if (upload == null)
			throw new TInvalidTokenException();
		if (options == null) // Query only -- don't validate user
			return upload.setOptions(null);
		UserInfo user = SessionManager.getOrFail(userToken);
		if (Comparators.user.compare(user, upload.getOwner()) != 0)
			throw new TAuthorizationException(AuthorizationError.NO_PERMISSION, "This isn't your upload");
		return upload.setOptions(options);
	}

	@Override
	public void cancelUpload(String uploadToken) {
		IncomingDataTransfer upload = fileServer.getUploadByToken(uploadToken);
		if (upload != null) {
			LOGGER.debug("User is cancelling upload " + uploadToken);
			upload.cancel();
		}

	}

	@Override
	public TransferStatus queryUploadStatus(String uploadToken) throws TInvalidTokenException {
		IncomingDataTransfer upload = fileServer.getUploadByToken(uploadToken);
		if (upload == null) {
			// It might be a master -> sat transfer...
			upload = SyncTransferHandler.getDownloadByToken(uploadToken);
		}
		if (upload == null)
			throw new TInvalidTokenException();
		return upload.getStatus();
	}

	@Override
	public TransferInformation requestDownload(String userToken, String imageVersionId)
			throws TAuthorizationException, TInvocationException, TNotFoundException,
			TTransferRejectedException {
		UserInfo user = SessionManager.getOrFail(userToken);
		if (!FileSystem.waitForStorage())
			throw new TInvocationException(InvocationError.INTERNAL_SERVER_ERROR, "VM storage not mounted");
		ImageVersionMeta imageVersion;
		try {
			imageVersion = DbImage.getVersionDetails(imageVersionId);
		} catch (SQLException e) {
			throw new TInvocationException();
		}
		User.canDownloadImageVersionOrFail(user, imageVersion.imageBaseId, imageVersionId);
		OutgoingDataTransfer transfer;
		try {
			transfer = fileServer.createNewUserDownload(DbImage.getLocalImageData(imageVersionId));
		} catch (SQLException e) {
			throw new TInvocationException();
		}
		TransferInformation ti = new TransferInformation(transfer.getId(), fileServer.getPlainPort(),
				fileServer.getSslPort());
		ti.setBlockHashes(imageVersion.sha1sums);
		ti.setMachineDescription(imageVersion.machineDescription);
		return ti;
	}

	@Override
	public void cancelDownload(String downloadToken) {
		OutgoingDataTransfer download = fileServer.getDownloadByToken(downloadToken);
		if (download != null)
			download.cancel();
	}

	/*
	 * Authentication/Validation
	 */

	@Override
	public void isAuthenticated(String userToken) throws TAuthorizationException, TInvocationException {
		SessionManager.ensureAuthenticated(userToken);
	}

	@Override
	public WhoamiInfo whoami(String userToken) throws TAuthorizationException, TInvocationException {
		UserInfo user = SessionManager.getOrFail(userToken);
		return new WhoamiInfo(user, User.isSuperUser(user), User.canListImages(user));
	}

	@Override
	public void invalidateSession(String userToken) {
		SessionManager.remove(userToken);
	}

	/*
	 * Query basic information which doesn't require authentication
	 */

	@Override
	public List<OperatingSystem> getOperatingSystems() {
		return OperatingSystemList.get();
	}

	@Override
	public List<Virtualizer> getVirtualizers() {
		return VirtualizerList.get();
	}

	@Override
	public List<Organization> getAllOrganizations() {
		return OrganizationList.get();
	}

	@Override
	public SatelliteStatus getStatus() {
		return new SatelliteStatus(FileSystem.getAvailableStorageBytes(), Util.unixTime());
	}

	/*
	 * Everything below required at least a valid session
	 */

	@Override
	public SatelliteUserConfig getUserConfig(String userToken) throws TAuthorizationException,
			TInvocationException {
		UserInfo user = SessionManager.getOrFail(userToken);
		LocalUser localData;
		try {
			localData = DbUser.getLocalData(user);
		} catch (SQLException e) {
			throw new TInvocationException();
		}
		return new SatelliteUserConfig(localData != null && localData.emailNotifications);
	}

	@Override
	public void setUserConfig(String userToken, SatelliteUserConfig config) throws TAuthorizationException,
			TInvocationException {
		UserInfo user = SessionManager.getOrFail(userToken);
		try {
			DbUser.writeUserConfig(user, config);
		} catch (SQLException e) {
			throw new TInvocationException();
		}
	}

	@Override
	public List<ImageSummaryRead> getImageList(String userToken, List<String> tagSearch, int page)
			throws TAuthorizationException, TInvocationException {
		UserInfo user = SessionManager.getOrFail(userToken);
		User.canListImagesOrFail(user);
		try {
			return DbImage.getAllVisible(user, tagSearch, page);
		} catch (SQLException e) {
			throw new TInvocationException();
		}
	}

	@Override
	public ImageDetailsRead getImageDetails(String userToken, String imageBaseId)
			throws TAuthorizationException, TNotFoundException, TInvocationException {
		UserInfo user = SessionManager.getOrFail(userToken);
		User.canSeeImageDetailsOrFail(user);
		try {
			return DbImage.getImageDetails(user, imageBaseId);
		} catch (SQLException e) {
			throw new TInvocationException();
		}
	}

	@Override
	public String createImage(String userToken, String imageName) throws TAuthorizationException,
			TInvocationException {
		UserInfo user = SessionManager.getOrFail(userToken);
		User.canCreateImageOrFail(user);
		if (!Util.isPrintable(imageName) || Util.isEmptyString(imageName))
			throw new TInvocationException(InvocationError.INVALID_DATA, "Invalid or empty name");
		try {
			String imageBaseId = DbImage.createImage(user, imageName);
			DbLog.log(user, imageBaseId, "New Image created: '" + imageName + "'");
			return imageBaseId;
		} catch (SQLException e) {
			throw new TInvocationException();
		}
	}

	@Override
	public void updateImageBase(String userToken, String imageBaseId, ImageBaseWrite newData)
			throws TAuthorizationException, TInvocationException, TNotFoundException {
		UserInfo user = SessionManager.getOrFail(userToken);
		User.canEditBaseImageOrFail(user, imageBaseId);
		// Check image name for invalid characters
		if (!Util.isPrintable(newData.imageName) || Util.isEmptyString(newData.imageName))
			throw new TInvocationException(InvocationError.INVALID_DATA, "Invalid or empty name");
		// Check if image is marked for replication. If so, only allow changing the syncmode to FROZEN/DOWNLOAD
		try {
			ImageSummaryRead imageSummary = DbImage.getImageSummary(user, imageBaseId);
			if (imageSummary.shareMode == ShareMode.DOWNLOAD || imageSummary.shareMode == ShareMode.FROZEN) {
				if (newData.shareMode != ShareMode.DOWNLOAD && newData.shareMode != ShareMode.FROZEN) {
					throw new TInvocationException(InvocationError.INVALID_SHARE_MODE,
							"Cannot change share mode from remote to local");
				} else {
					// Share mode is valid and changed, but ignore all other fields
					DbImage.setShareMode(imageBaseId, newData);
					return;
				}
				// (Unreachable)
			} else {
				// Likewise, if share mode is local or publish, don't allow changing to FROZEN/DOWNLOAD
				if (newData.shareMode != ShareMode.LOCAL && newData.shareMode != ShareMode.PUBLISH) {
					throw new TInvocationException(InvocationError.INVALID_SHARE_MODE,
							"Cannot change share mode from local to remote");
				}
			}
			// TODO: Should other fields be validated? Most fields should be protected by fk constraints,
			// but the user would only get a generic error, with no hint at the actual problem.
			// The update routine will make sure only the super user can change the template flag
			newData.defaultPermissions = Sanitizer.handleImagePermissions(newData.defaultPermissions);
			DbImage.updateImageMetadata(user, imageBaseId, newData);
		} catch (SQLException e1) {
			throw new TInvocationException();
		}
	}

	@Override
	public void updateImageVersion(String userToken, String imageVersionId, ImageVersionWrite image)
			throws TAuthorizationException, TInvocationException, TNotFoundException {
		UserInfo user = SessionManager.getOrFail(userToken);
		// Special case: Version is still being uploaded, so there's no entry yet - remember for later
		IncomingDataTransfer upload = fileServer.getUploadByToken(imageVersionId);
		if (upload != null && upload.setVersionData(user, image)) {
			return;
		}
		// Normal case - version already exists
		User.canEditImageVersionOrFail(user, imageVersionId);
		try {
			// Do not allow editing remote images
			ImageSummaryRead imageSummary = DbImage.getImageSummary(user,
					DbImage.getBaseIdForVersionId(imageVersionId));
			if (imageSummary.shareMode == ShareMode.DOWNLOAD || imageSummary.shareMode == ShareMode.FROZEN) {
				throw new TAuthorizationException(AuthorizationError.NO_PERMISSION,
						"Cannot edit image coming from master server");
			}
			DbImage.updateImageVersion(user, imageVersionId, image);
		} catch (SQLException e1) {
			throw new TInvocationException();
		}
	}

	@Override
	public void deleteImageVersion(String userToken, String imageVersionId) throws TAuthorizationException,
			TNotFoundException, TInvocationException {
		UserInfo user = SessionManager.getOrFail(userToken);
		if (!FileSystem.waitForStorage())
			throw new TInvocationException(InvocationError.INTERNAL_SERVER_ERROR, "VM storage not mounted");
		User.canDeleteImageVersionOrFail(user, imageVersionId);
		try {
			ImageSummaryRead imageSummary = DbImage.getImageSummary(user,
					DbImage.getBaseIdForVersionId(imageVersionId));
			DbImage.markForDeletion(imageVersionId);
			DbImage.setDeletion(DeleteState.WANT_DELETE, imageVersionId);
			DbLog.log(user, imageSummary.imageBaseId, Formatter.userFullName(user) + " deleted Version "
					+ imageVersionId + " of '" + imageSummary.imageName + "' (" + imageSummary.imageBaseId
					+ ")");
		} catch (SQLException e) {
			throw new TInvocationException();
		}
		DeleteOldImages.hardDeleteImagesAsync();
	}

	@Override
	public void deleteImageBase(String userToken, String imageBaseId) throws TAuthorizationException,
			TNotFoundException, TInvocationException {
		UserInfo user = SessionManager.getOrFail(userToken);
		ImageDetailsRead imageDetails;
		try {
			imageDetails = DbImage.getImageDetails(user, imageBaseId);
		} catch (SQLException e) {
			throw new TInvocationException();
		}
		User.canDeleteImageOrFail(imageDetails);
		String[] ids = new String[imageDetails.versions.size()];
		int index = 0;
		for (ImageVersionDetails version : imageDetails.versions) {
			if (version.versionId != null) {
				ids[index++] = version.versionId;
			}
		}
		if (index != 0) {
			try {
				DbImage.markForDeletion(ids);
				DbImage.setDeletion(DeleteState.WANT_DELETE, ids);
			} catch (Exception e) {
				LOGGER.warn("Could not delete version when trying to delete base image", e);
			}
			DeleteOldImages.hardDeleteImages();
		}
		DbLog.log(user, imageDetails.imageBaseId, Formatter.userFullName(user) + " deleted Image '" + imageDetails.imageName
				+ "' with all its versions (" + index + ")");
		try {
			DbImage.deleteBasePermanently(imageBaseId);
		} catch (SQLException e) {
			throw new TInvocationException();
		}
	}

	@Override
	public void writeImagePermissions(String userToken, String imageBaseId,
			Map<String, ImagePermissions> permissions) throws TAuthorizationException, TNotFoundException,
			TInvocationException {
		UserInfo user = SessionManager.getOrFail(userToken);
		User.canEditImagePermissionsOrFail(user, imageBaseId);
		try {
			DbImagePermissions.writeForImageBase(imageBaseId, permissions);
		} catch (SQLException e) {
			throw new TInvocationException();
		}
	}

	@Override
	public Map<String, ImagePermissions> getImagePermissions(String userToken, String imageBaseId)
			throws TAuthorizationException, TNotFoundException, TInvocationException {
		UserInfo user = SessionManager.getOrFail(userToken);
		boolean adminOnly = !User.canEditImagePermissions(user, imageBaseId);
		try {
			return DbImagePermissions.getForImageBase(imageBaseId, adminOnly);
		} catch (SQLException e) {
			throw new TInvocationException();
		}
	}

	@Override
	public void setImageOwner(String userToken, String imageBaseId, String newOwnerId)
			throws TAuthorizationException, TNotFoundException, TInvocationException, TException {
		UserInfo user = SessionManager.getOrFail(userToken);
		User.canChangeImageOwnerOrFail(user, imageBaseId);
		try {
			ImageSummaryRead imageSummary = DbImage.getImageSummary(user, imageBaseId);
			if (imageSummary.shareMode == ShareMode.DOWNLOAD || imageSummary.shareMode == ShareMode.FROZEN) {
				throw new TAuthorizationException(AuthorizationError.NO_PERMISSION,
						"Cannot change owner of image that gets downloaded from master server");
			}
			DbImage.setImageOwner(imageBaseId, newOwnerId, user);
			UserInfo newOwner = DbUser.getOrNull(newOwnerId);
			DbLog.log(user, imageBaseId, Formatter.userFullName(user) + " changed owner of '"
					+ imageSummary.imageName + "' to " + Formatter.userFullName(newOwner));
			DbLog.log(user, newOwnerId, Formatter.userFullName(newOwner) + " was declared new owner of '"
					+ imageSummary.imageName + "' by " + Formatter.userFullName(user));
		} catch (SQLException e) {
			throw new TInvocationException();
		}
	}

	@Override
	public void setImageVersionExpiry(String userToken, String imageVersionId, long expireTime)
			throws TAuthorizationException, TNotFoundException, TInvocationException, TInvalidDateParam {
		long now = Util.unixTime();
		if (expireTime > now + 3 * RuntimeConfig.getMaxImageValiditySeconds())
			throw new TInvalidDateParam(DateParamError.TOO_HIGH, "Expire date too far in the future");
		if (expireTime < now - 365 * 86400)
			throw new TInvalidDateParam(DateParamError.TOO_LOW, "Expire date too far in the past");
		UserInfo user = SessionManager.getOrFail(userToken);
		User.canChangeImageExpireDateOrFail(user);
		LocalImageVersion localImageData;
		try {
			localImageData = DbImage.getLocalImageData(imageVersionId);
		} catch (SQLException e) {
			throw new TInvocationException();
		}
		File srcFile = FileSystem.composeAbsoluteImagePath(localImageData);
		String errorMessage = null;
		if (srcFile == null) {
			errorMessage = "File has invalid path on server";
		} else if (!srcFile.canRead()) {
			errorMessage = "File missing on server";
		} else if (srcFile.length() != localImageData.fileSize) {
			errorMessage = "File corrupted on server";
		}
		try {
			if (errorMessage == null) {
				DbImage.setDeletion(DeleteState.KEEP, localImageData.imageVersionId);
				DbImage.setExpireDate(localImageData.imageVersionId, expireTime);
				DbImage.markValid(true, false, localImageData);
			} else {
				DbImage.markValid(false, false, localImageData);
				throw new TInvocationException(InvocationError.INTERNAL_SERVER_ERROR, errorMessage);
			}
		} catch (SQLException e) {
			throw new TInvocationException();
		}
	}

	@Override
	public String requestImageReplication(String userToken, String imageVersionId)
			throws TAuthorizationException, TNotFoundException, TInvocationException {
		UserInfo user = SessionManager.getOrFail(userToken);
		User.canTriggerReplicationOrFail(user, imageVersionId);
		if (!FileSystem.waitForStorage())
			throw new TInvocationException(InvocationError.INTERNAL_SERVER_ERROR, "VM storage not mounted");
		// Query master server
		ImagePublishData imagePublishData;
		try {
			imagePublishData = ThriftManager.getMasterClient().getImageData(userToken, imageVersionId);
		} catch (TException e) {
			LOGGER.error("Could not query image data from master server for"
					+ " an image that a client wants to replicate", e);
			throw new TInvocationException(InvocationError.INTERNAL_SERVER_ERROR,
					"Cannot query master server for image information");
		}
		// Known by master server; now update/write to DB
		try {
			if (imagePublishData.owner == null) {
				imagePublishData.owner = imagePublishData.uploader;
			}
			DbUser.writeUserOnReplication(imagePublishData.owner);
			DbImage.writeBaseImage(imagePublishData);
			DbImagePermissions.writeForImageBase(imagePublishData.imageBaseId, user.userId,
					new ImagePermissions(true, true, true, true));
		} catch (SQLException e) {
			throw new TInvocationException(InvocationError.INTERNAL_SERVER_ERROR,
					"Could not write to local DB");
		}
		imagePublishData.uploader = user;
		String transferId = SyncTransferHandler.requestImageDownload(userToken, imagePublishData);
		DbLog.log(user, imagePublishData.imageBaseId, Formatter.userFullName(user)
				+ " triggered download from master server of version " + imageVersionId + " of '"
				+ imagePublishData.imageName + "'");
		return transferId;
	}

	@Override
	public String publishImageVersion(String userToken, String imageVersionId)
			throws TAuthorizationException, TNotFoundException, TInvocationException,
			TTransferRejectedException {
		UserInfo user = SessionManager.getOrFail(userToken);
		User.canCreateImageOrFail(user);
		LocalImageVersion imgVersion = null;
		ImageSummaryRead imgBase = null;
		try {
			imgVersion = DbImage.getLocalImageData(imageVersionId);
			imgBase = DbImage.getImageSummary(user, imgVersion.imageBaseId);
			//img.uploaderId
		} catch (SQLException e1) {
			throw new TInvocationException(InvocationError.INTERNAL_SERVER_ERROR, "Database error");
		}
		User.canUploadToMasterOrFail(user, imgBase);
		try {
			String transferId = SyncTransferHandler.requestImageUpload(userToken, imgBase, imgVersion);
			DbLog.log(user, imgBase.imageBaseId, Formatter.userFullName(user)
					+ " triggered upload to master server of version " + imageVersionId + " of '"
					+ imgBase.imageName + "'");
			return transferId;
		} catch (TTransferRejectedException e) {
			LOGGER.warn("Master server rejected upload of image version " + imgVersion.imageVersionId);
			throw e;
		} catch (SQLException e) {
			throw new TInvocationException(InvocationError.INTERNAL_SERVER_ERROR, "Database error");
		}
	}

	@Override
	public String createLecture(String userToken, LectureWrite lecture) throws TAuthorizationException,
			TInvocationException, TInvalidDateParam, TNotFoundException {
		if (lecture == null || lecture.defaultPermissions == null)
			throw new TInvocationException(InvocationError.MISSING_DATA, "Lecture data missing or incomplete");
		if (lecture.locationIds != null
				&& lecture.locationIds.size() > RuntimeConfig.getMaxLocationsPerLecture())
			throw new TInvocationException(InvocationError.INVALID_DATA, "Too many locations for lecture");
		UserInfo user = SessionManager.getOrFail(userToken);
		User.canCreateLectureOrFail(user);
		User.canLinkToImageOrFail(user, lecture.imageVersionId);
		Sanitizer.handleLectureDates(lecture, null);
		try {
			String lectureId = DbLecture.create(user, lecture);
			DbLog.log(user, lectureId, Formatter.userFullName(user) + " created lecture '" + lecture.lectureName + "'");
			return lectureId;
		} catch (SQLException e) {
			throw new TInvocationException();
		}
	}

	@Override
	public void updateLecture(String userToken, String lectureId, LectureWrite newLectureData)
			throws TAuthorizationException, TNotFoundException, TInvocationException, TInvalidDateParam {
		if (newLectureData == null)
			throw new TInvocationException(InvocationError.MISSING_DATA, "Lecture data missing or incomplete");
		if (newLectureData.locationIds != null
				&& newLectureData.locationIds.size() > RuntimeConfig.getMaxLocationsPerLecture())
			throw new TInvocationException(InvocationError.INVALID_DATA, "Too many locations for lecture");
		UserInfo user = SessionManager.getOrFail(userToken);
		LectureSummary oldLecture;
		try {
			oldLecture = DbLecture.getLectureSummary(user, lectureId);
		} catch (SQLException e1) {
			throw new TInvocationException();
		}
		User.canEditLectureOrFail(user, oldLecture);
		// TODO Copy empty fields in new from old
		if (oldLecture.imageVersionId == null
				|| !oldLecture.imageVersionId.equals(newLectureData.imageVersionId)) {
			User.canLinkToImageOrFail(user, newLectureData.imageVersionId);
		}
		Sanitizer.handleLectureDates(newLectureData, oldLecture);
		try {
			DbLecture.update(user, lectureId, newLectureData);
		} catch (SQLException e) {
			throw new TInvocationException();
		}
	}

	@Override
	public List<LectureSummary> getLectureList(String userToken, int page) throws TAuthorizationException,
			TInvocationException {
		UserInfo user = SessionManager.getOrFail(userToken);
		try {
			// If user is student, getAll() will only return lectures where the current linked image is not restricted
			return DbLecture.getAll(user, page);
		} catch (SQLException e) {
			throw new TInvocationException();
		}
	}

	@Override
	public LectureRead getLectureDetails(String userToken, String lectureId) throws TAuthorizationException,
			TNotFoundException, TInvocationException {
		UserInfo user = SessionManager.getOrFail(userToken);
		User.canSeeLectureDetailsOrFail(user);
		try {
			return DbLecture.getLectureDetails(user, lectureId);
		} catch (SQLException e) {
			throw new TInvocationException();
		}
	}

	@Override
	public void deleteLecture(String userToken, String lectureId) throws TAuthorizationException,
			TNotFoundException, TInvocationException {
		UserInfo user = SessionManager.getOrFail(userToken);
		User.canDeleteLectureOrFail(user, lectureId);
		try {
			LectureSummary lecture = DbLecture.getLectureSummary(user, lectureId);
			if (!DbLecture.delete(lectureId))
				throw new TNotFoundException();
			DbLog.log(user, lectureId, Formatter.userFullName(user) + " deleted lecture '" + lecture.lectureName + "'");
		} catch (SQLException e) {
			throw new TInvocationException();
		}
	}

	@Override
	public void writeLecturePermissions(String userToken, String lectureId,
			Map<String, LecturePermissions> permissions) throws TAuthorizationException, TNotFoundException,
			TInvocationException {
		UserInfo user = SessionManager.getOrFail(userToken);
		User.canEditLecturePermissionsOrFail(user, lectureId);
		try {
			DbLecturePermissions.writeForLecture(lectureId, permissions);
		} catch (SQLException e) {
			throw new TInvocationException();
		}
	}

	@Override
	public Map<String, LecturePermissions> getLecturePermissions(String userToken, String lectureId)
			throws TAuthorizationException, TNotFoundException, TInvocationException {
		UserInfo user = SessionManager.getOrFail(userToken);
		boolean adminOnly = !User.canEditLecturePermissions(user, lectureId);
		try {
			return DbLecturePermissions.getForLecture(lectureId, adminOnly);
		} catch (SQLException e) {
			throw new TInvocationException();
		}
	}

	@Override
	public void setLectureOwner(String userToken, String lectureId, String newOwnerId)
			throws TAuthorizationException, TNotFoundException, TInvocationException, TException {
		UserInfo user = SessionManager.getOrFail(userToken);
		LectureSummary lecture;
		try {
			lecture = DbLecture.getLectureSummary(user, lectureId);
		} catch (SQLException e1) {
			throw new TInvocationException();
		}
		User.canChangeLectureOwnerOrFail(user, lecture);
		try {
			DbLecture.setOwner(user, lectureId, newOwnerId);
			UserInfo newOwner = DbUser.getOrNull(newOwnerId);
			DbLog.log(user, lectureId, Formatter.userFullName(user) + " changed owner of '"
					+ lecture.lectureName + "' to " + Formatter.userFullName(newOwner));
			DbLog.log(user, newOwnerId, Formatter.userFullName(newOwner) + " was declared new owner of '"
					+ lecture.lectureName + "' by " + Formatter.userFullName(user));
		} catch (SQLException e) {
			throw new TInvocationException();
		}
	}

	@Override
	public List<UserInfo> getUserList(String userToken, int page) throws TAuthorizationException,
			TInvocationException {
		SessionManager.getOrFail(userToken);
		try {
			return DbUser.getAll(page);
		} catch (SQLException e) {
			throw new TInvocationException();
		}
	}

	@Override
	public List<Location> getLocations() throws TException {
		try {
			return DbLocation.getLocations();
		} catch (SQLException e) {
			throw new TInvocationException(InvocationError.INTERNAL_SERVER_ERROR,
					"Database failure when retrieving list");
		}
	}

	@Override
	public ByteBuffer getImageVersionVirtConfig(String userToken,
			String imageVersionId) throws TAuthorizationException,
			TNotFoundException, TInvocationException, TException {
		UserInfo user = SessionManager.getOrFail(userToken);
		User.canSeeImageDetailsOrFail(user);
		byte[] machineDescription = null;
		try {
			machineDescription = DbImage.getVirtualizerConfig(imageVersionId);
		} catch (SQLException e) {
			throw new TInvocationException(InvocationError.INTERNAL_SERVER_ERROR,
					"Database failure when retrieving the virtualizer config for '" + imageVersionId + "'.");
		}
		if (machineDescription == null)
			return null;
		return ByteBuffer.wrap(machineDescription);
	}

	@Override
	public void setImageVersionVirtConfig(String userToken, String imageVersionId,
			ByteBuffer machineDescription) throws TAuthorizationException,
			TNotFoundException, TInvocationException, TException {
		UserInfo user = SessionManager.getOrFail(userToken);
		User.canEditImageVersionOrFail(user, imageVersionId);
		byte[] mdBytes = ThriftUtil.unwrapByteBuffer(machineDescription);
		try {
			DbImage.setVirtualizerConfig(imageVersionId, mdBytes);
		} catch (SQLException e) {
			throw new TInvocationException(InvocationError.INTERNAL_SERVER_ERROR,
					"Database failure when setting the virtualizer config for '" + imageVersionId + "'.");
		}
	}

	@Override
	public PredefinedData getPredefinedData(String userToken) throws TAuthorizationException,
			TInvocationException, TException {
		SessionManager.ensureAuthenticated(userToken); // Only logged in users
		PredefinedData data = new PredefinedData();
		try {
			data.ldapFilter = DbLectureFilter.getPredefinedLdap();
		} catch (SQLException e) {
			throw new TInvocationException(InvocationError.INTERNAL_SERVER_ERROR,
					"Database failure when querying predefined LDAP filters.");
		}
		try {
			data.netShares = DbLectureNetshare.getPredefined();
		} catch (SQLException e) {
			throw new TInvocationException(InvocationError.INTERNAL_SERVER_ERROR,
					"Database failure when querying predefined network shares.");
		}
		try {
			data.runScripts = DbRunScript.getPredefinedRunScripts();
		} catch (SQLException e) {
			throw new TInvocationException(InvocationError.INTERNAL_SERVER_ERROR,
					"Database failure when querying predefined run scripts.");
		}
		try {
			data.networkExceptions = DbLectureNetworkRules.getPredefined();
		} catch (SQLException e) {
			throw new TInvocationException(InvocationError.INTERNAL_SERVER_ERROR,
					"Database failure when querying predefined network rules.");
		}
		return data;
	}

}