summaryrefslogblamecommitdiffstats
path: root/dozentenmodul/src/main/java/org/openslx/dozmod/thrift/ThriftActions.java
blob: 2900ee2044db508825db85e09347cb25cdb81505 (plain) (tree)
1
2
3
4
5
6
7
8
9





                                     
                           
                                              
                           
                      
                     




                                    
                                                    

                                                      
                                                         
                                                       
                                                        
                                                 
                                                    
                                                  
                                                            
                                                             
                                                          
                                                         
                                                   
                                              

                                                
                                 

                                                                          
                                                          
                                                    

                                                             
                                                  
                                                   
                                  


                                                  
                                            

                                                                  
                                              


                                              
                             

                                                                 



                                                                                   

                                                                             
 


                                                                                          
           

                        


                                                                                     
           

                                                                                 
                                                               
                                                                           




















                                                                                       
                                                            
                                                  



                                                       

                                                                                                                  


                                     








                                                                               


                                                                                                                                          

                                                                                                                                    
                         
                                     
                 



                                                         

                                                         


                                                                                                                                   
                         

                                                  





                                                                                                                                                                      

                                       
                                           

                                                      


                                                                                                                                       

                                     
 

                                                                                   

                                                                                                          




                                                                                                                   
 


                                                                                          
           

                                                   


                                                                                     
           

                                                                               
           
                                           





                                                                                                           

                                                                                                         
                                       

                                                                                                                        


                            
 
           

                                                                            
           


                                                                           
           

                                                                                          
                     
                                                                                                                     
                                        
                                                                                                                   
                                     
                 
                            
         
 
           

                                                                                    
           


                                                                      
           

                                                                                             
                     

                                                                                                                    
                                        
                                                                                                             

                 
 


                                                                                          
           


                                                                                  
           


                                                                                     
           


                                                                                    
           


                                                                      


                                    

                                                                                  

                                                                                                           
                                                                                                                       
                     

                                                                                                          



                                                                                                                                                                                  


                                            
                                                                                                                   

                                    

                                              

                                                                                                                








                                                                                                               
 
           

                                                                     
           



                                                                                

                                                                                
           

                                                                                             



                                                                          

                                                                                                                      
                                                   

                                                                                                                         
                                                                      

                                    
                                                  


                                                                                              

                                                                                         



                                                                                                                       



                                                                                        

                                                                                        
                                      

                                                                                             

                                                         

                                                                                                                    



                                            
                                                                 
         
 
           
                                                
           



                                     


                                                                                           

                                                                                                               
                                        
                                                                                                                        
                                     
                 
                            
         
 


                                                                                          
           


                                                                                  
           


                                                                                     
           

                                                                           



                                                          
 
           

                                                                                   
           






                                                                                  

                                                                                                               
                                                                                                                            
                                                


                                                                                   
                                                     


                                                                                
                               
                 
 

                                                                                                    

                                                                                                                  
 



                                                                                                           

                                               

                                                                            


                                       
                                                                            

                                                                                                                         

                                                                            



                                       
                                               

                                                                                                                            


                                                                                                                            

                                                                    


                               


                                                    
                                                                   
                                     

                                                                                                                            






                                                                                                                          

                                                          
                                                                                                                         
                                                                                                           


                                                                                                                                          




                                                                                      









































                                                                                                                                                                             



                                                              
                                                                                                                 

                                         
 

                                                           
                                                                                                  

                                                                           


                         









                                                                                        


                                                                                          
           

                                               


                                                                                     


               
                                            
                                                                                                              
         
 


                                                                                                     
                                                                                                                         




                                                                                                     
 




                             

                                                                                       

                                                        
 

                                            
                                                                                            



                                                                       






                                                                                                    
 

                                                                                           


                                                                         
 









                                                                                                           





                                                 
 
                

                                                                                           

                                                                           
 

                                            
                                                                                                      



                                                                       
                                                                                                          





                                                 
 
               

                                                                                          

                                                                   

                                                                                                                     




                                                                                                     
 
           
                                                                         
           


                                                                



                                                                                                                 

                                                                                                          





                                                                                                              
 


                                                                                          
           
                                           
           


                                                                                     
           
                                                                    





                                                                             
                                                                               


                                                
 

                                                                                       

                                                                 

                                                 


                                                                                   

                                                                                                                         

                                                                                                       

                                                                                   

                                                                                                      


                                                                         






                                                                                                          
 
                                        

                                                                                                                            

                               



                                                                                         

                                                                              








                                                                                                     
                         












                                                                                                               
                 
 
         
 
           




                                                                                    
           



                                                                                  
                                               
           

                                                                                             

                                                                       
                               










                                                                                                                     
                                         

                                                                                 

                                                                    

                                                                                         


                                                                                                                             


                                                                                        

                                                                                                              
                 



                                                                                                 
                                                        
                               











                                                                                                                     
                                 


                                                                     


                         
 


                                                                                          
           
                                     
           


                                                                                     


                                                     

                                                       













                                                                                                             
 
           

                                                                                 
           


                                                                        
           

                                                                                                
                     

                                                                                                                    

                                                                                                         
                                     
                 
                            
         
 
                                                                                          
           
                           
           




                                                                                     

                      
           

                                              
                                                                                        
 

                                                                                            
 
           
                       
           



                           

                                                                                                
                                                    

                                            
                                                                                                              






                                                                                                               



                                                                       
                                                                                                        





                                                 
 
           
                       
           



                           








                                                                                                                         
 
           

                                                                             
           


                                                                      
                  
           

                                                                                      
                     
                                                                                                                         





                                                                                                           
 

                       
           



                           

                                                                                              


                                                                   

                                                                                                                     




                                                                                                             
         
 
           
                                                                         
           


                                                                



                                                                                                                   

                                                                                                            





                                                                                                              
 
                                                                                          
           
                           
           




                                                                                     


                                                                                  


                                                
 
           
                                                                    




                           

                                                                                   

                                      

                                                                                               

                                                    
 


















                                                                                                                             
 


                                                                                          
           


                                                                                     
           

                                                                       

                                                                 




                                                                                                    
 

                                                            
           



                                                       
 




                                                                                                 
 
package org.openslx.dozmod.thrift;

import java.awt.Frame;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.swing.JFileChooser;

import org.apache.log4j.Logger;
import org.apache.thrift.TException;
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.ImageVersionDetails;
import org.openslx.bwlp.thrift.iface.ImageVersionWrite;
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.SatelliteServer.Client;
import org.openslx.bwlp.thrift.iface.TAuthorizationException;
import org.openslx.bwlp.thrift.iface.TInvocationException;
import org.openslx.bwlp.thrift.iface.TransferInformation;
import org.openslx.bwlp.thrift.iface.TransferState;
import org.openslx.bwlp.thrift.iface.UserInfo;
import org.openslx.bwlp.thrift.iface.WhoamiInfo;
import org.openslx.dozmod.App;
import org.openslx.dozmod.Config;
import org.openslx.dozmod.Config.SavedSession;
import org.openslx.dozmod.authentication.Authenticator.AuthenticationData;
import org.openslx.dozmod.filetransfer.AsyncHashGenerator;
import org.openslx.dozmod.filetransfer.DownloadTask;
import org.openslx.dozmod.filetransfer.TransferEvent;
import org.openslx.dozmod.filetransfer.TransferEventListener;
import org.openslx.dozmod.filetransfer.UploadTask;
import org.openslx.dozmod.gui.GraphicalCertHandler;
import org.openslx.dozmod.gui.Gui;
import org.openslx.dozmod.gui.MainWindow;
import org.openslx.dozmod.gui.helper.MessageType;
import org.openslx.dozmod.gui.helper.QFileChooser;
import org.openslx.dozmod.util.FormatHelper;
import org.openslx.dozmod.util.VmWrapper;
import org.openslx.dozmod.util.VmWrapper.MetaDataMissingException;
import org.openslx.sat.thrift.version.Version;
import org.openslx.thrifthelper.ThriftManager;
import org.openslx.util.QuickTimer;
import org.openslx.util.QuickTimer.Task;
import org.openslx.util.Util;
import org.openslx.util.vm.DiskImage;
import org.openslx.util.vm.DiskImage.UnknownImageFormatException;

public class ThriftActions {

	private static final Logger LOGGER = Logger.getLogger(ThriftActions.class);
	private static final long SIZE_CHECK_EXTRA_DL = 50l * 1024l * 1024l;
	private static final long SIZE_CHECK_EXTRA_UL = 150l * 1024l * 1024l;

	/* *******************************************************************************
	 * 
	 * LOGIN
	 * 
	 * Login methods
	 * 
	 * **************************************************************************
	 * ****
	 */
	/**
	 * @param frame to show user feedback on
	 * @param data AuthenticationData as received from a successful login, or
	 *            null if trying to resume a saved sessions
	 * @return true if initializing the session worked, false otherwise
	 */
	public static boolean initSession(final Frame frame, AuthenticationData data) {
		Client client = null;
		WhoamiInfo whoami = null;
		String address = null;
		String satToken = null;
		String masterToken = null;
		if (data == null) {
			// in session resume
			SavedSession session = Config.getSavedSession();
			if (session != null) {
				address = session.address;
				satToken = session.token;
				masterToken = session.masterToken;

			} else {
				// fail
				return false;
			}
		} else {
			// after login
			// TODO sat adress selection popup!!
			address = "132.230.8.113";
			satToken = data.satelliteToken;
			masterToken = data.masterToken;
		}
		// try to get a new client
		client = ThriftManager.getNewSatelliteClient(GraphicalCertHandler.getSslContext(address), address,
				App.THRIFT_SSL_PORT, App.THRIFT_TIMEOUT_MS);
		if (client == null)
			return false;

		// check our version
		long remoteVersion = 0;
		try {
			remoteVersion = client.getVersion();
		} catch (TException e) {
			LOGGER.debug("Failed to retrieve remote version: ", e);
			return false;
		}
		if (remoteVersion != Version.VERSION) {
			if (data != null) {
				Gui.showMessageBox(frame,
						"Das von Ihnen verwendete Dozentenmodul ist nicht mit dem Satelliten-Server kompatibel.\n"
								+ "Ihre Version: " + Version.VERSION + "\n" + "Satelliten-Version: "
								+ remoteVersion, MessageType.ERROR, LOGGER, null);
			}
			return false;
		}
		// all good, try to get the whoami info
		try {
			whoami = client.whoami(satToken);
		} catch (TAuthorizationException e) {
			if (data != null) {
				Gui.showMessageBox(frame,
						"Authentifizierung erfolgreich, der Satellit verweigert jedoch die Verbindung.\n\n"
								+ "Grund: " + e.number.toString() + " (" + e.message + ")",
						MessageType.ERROR, null, null);
			}
			return false;
		} catch (TInvocationException e) {
			if (data != null) {
				Gui.showMessageBox(
						frame,
						"Authentifizierung erfolgreich, bei der Kommunikation mit dem Satelliten trat jedoch ein interner Server-Fehler auf.",
						MessageType.ERROR, LOGGER, e);
			}
			return false;
		} catch (Exception e) {
			if (data != null) {
				Gui.showMessageBox(
						frame,
						"Authentifizierung erfolgreich, aber der Satellit akzeptiert das Sitzungstoken nicht.",
						MessageType.ERROR, LOGGER, e);
			}
			return false;
		}

		if (whoami != null) {
			Session.initialize(whoami, address, satToken, masterToken);
			ThriftManager.setSatelliteAddress(
					GraphicalCertHandler.getSslContext(Session.getSatelliteAddress()),
					Session.getSatelliteAddress(), App.THRIFT_SSL_PORT, App.THRIFT_TIMEOUT_MS);
			return true;
		}
		return false;
	}

	/* *******************************************************************************
	 * 
	 * IMAGE CREATION
	 * 
	 * Creates a base image with the given name
	 * 
	 * **************************************************************************
	 * ****
	 */
	/**
	 * GUI-BLOCKING Creates the image with the given name. Returns the uuid
	 * returned by the server
	 * 
	 * @param frame calling this action
	 * @return uuid as String, or null if the creation failed
	 */
	public static String createImage(final Frame frame, final String name) {
		String uuid = null;
		try {
			uuid = ThriftManager.getSatClient().createImage(Session.getSatelliteToken(), name);
		} catch (TException e) {
			ThriftError.showMessage(frame, LOGGER, e, "Erstellen des Images fehlgeschlagen");
		} catch (Exception e) {
			Gui.showMessageBox(frame, "Unbekannter Fehler beim Erstellen der VM", MessageType.ERROR, LOGGER,
					e);
		}
		return uuid;
	}

	/**
	 * GUI-BLOCKING Pushes a new image base to the server with the given
	 * imageBaseId and the meta information in meta
	 * 
	 * @param frame to show user feedback on
	 * @param imageBaseId image's id we are writing meta information of
	 * @param meta actual meta information as ImageBaseWrite
	 */
	public static boolean updateImageBase(final Frame frame, final String imageBaseId,
			final ImageBaseWrite meta) {
		try {
			ThriftManager.getSatClient().updateImageBase(Session.getSatelliteToken(), imageBaseId, meta);
		} catch (TException e) {
			ThriftError.showMessage(frame, LOGGER, e, "Konnte Metadaten des Images nicht übertragen");
			return false;
		}
		return true;
	}

	/**
	 * GUI-BLOCKING Pushes the given permission map as custom permission for the
	 * given imageBaseId
	 * 
	 * @param frame to show user feedback on
	 * @param imageBaseId image's id we are writing permissions of
	 * @param permissions actual permissions map to write
	 */
	public static void writeImagePermissions(final Frame frame, final String imageBaseId,
			final Map<String, ImagePermissions> permissions) {
		try {
			ThriftManager.getSatClient().writeImagePermissions(Session.getSatelliteToken(), imageBaseId,
					permissions);
		} catch (TException e) {
			ThriftError.showMessage(frame, LOGGER, e, "Konnte Berechtigungen nicht übertragen");
		}
	}

	/* *******************************************************************************
	 * 
	 * IMAGE VERSION UPLOAD
	 * 
	 * Methods to upload an image version. This is compromised of two distinct
	 * steps: - requestVersionUpload(..) to request the upload at the server -
	 * initUpload(..) to actually start the upload of the file
	 * 
	 * **************************************************************************
	 * ****
	 */
	/**
	 * GUI-BLOCKING Request the upload of an image version. Returns the
	 * TransferInformation received by the server or null if the request failed.
	 * Will give user feedback about failures.
	 * 
	 * @param frame caller of this method
	 * @param imageBaseId uuid of the image to upload a version of
	 * @param fileSize size in bytes(?) of the uploaded file
	 * @param blockHashes
	 * @param machineDescription
	 * @param callback
	 * @return TransferInformation received by the server, null if the request
	 *         failed.
	 */
	public static TransferInformation requestVersionUpload(final Frame frame, final String imageBaseId,
			final long fileSize, final List<ByteBuffer> blockHashes, final ByteBuffer machineDescription) {
		try {
			if (ThriftManager.getSatClient().getStatus().getAvailableStorageBytes() < fileSize
					+ SIZE_CHECK_EXTRA_UL) {
				Gui.showMessageBox(
						frame,
						"Nicht genügend Speicherplatz Satelliten. Löschen Sie nicht verwendete Imageversionen oder kontaktieren sie den Administrator.",
						MessageType.ERROR, LOGGER, null);
				return null;
			}
		} catch (TException e1) {
			ThriftError.showMessage(frame, LOGGER, e1, "Konnte Status des Satelliten nicht abfragen!");
			return null;
		}
		TransferInformation ti = null;
		try {
			ti = ThriftManager.getSatClient().requestImageVersionUpload(Session.getSatelliteToken(),
					imageBaseId, fileSize, null, // TODO remove deprecated parameter
					machineDescription);
			LOGGER.info("Version upload granted, versionId: '" + ti.toString());
		} catch (TAuthorizationException e) {
			ThriftError.showMessage(frame, LOGGER, e, "Upload einer neuen Version nicht erlaubt!");
		} catch (TException e) {
			ThriftError.showMessage(frame, LOGGER, e, "Upload-Anfrage gescheitert!");
		}
		return ti;
	}

	/**
	 * GUI-BLOCKING Starts uploading the given diskFile using the
	 * transferInformation and hashGen
	 * 
	 * @param frame caller of this method
	 * @param transferInformation transfer information to use for the upload
	 * @param hashGen hash generator for this file
	 * @param diskFile the file to upload
	 * @return UploadTask if the uploading initialized, or null if uploading
	 *         failed
	 */
	public static InitUploadStatus initUpload(final Frame frame,
			final TransferInformation transferInformation, final File diskFile) {
		UploadTask uploadTask = null;
		// do actually start the upload now
		LOGGER.debug("Starting upload for: " + diskFile.toPath());
		try {
			uploadTask = new UploadTask(Session.getSatelliteAddress(), transferInformation.getPlainPort(),
					transferInformation.getToken(), diskFile);
		} catch (FileNotFoundException e) {
			Gui.asyncMessageBox(
					"Kann VM nicht hochladen: Datei nicht gefunden\n\n" + diskFile.getAbsolutePath(),
					MessageType.ERROR, LOGGER, e);
			return null;
		}
		AsyncHashGenerator hashGen = null;
		try {
			hashGen = new AsyncHashGenerator(transferInformation.token, diskFile);
			hashGen.start();
			Util.sleep(50);// A little ugly... Give the hash generator a head
							// start
		} catch (FileNotFoundException | NoSuchAlgorithmException e) {
			Gui.showMessageBox(frame, "Kann keine Block-Hashes für den Upload berechnen, "
					+ "automatische Fehlerkorrektur deaktiviert.", MessageType.WARNING, LOGGER, e);
		}
		Util.sleep(50); // A little ugly... Give the hash generator a head start
		Thread uploadThread = new Thread(uploadTask);
		uploadThread.setDaemon(true);
		uploadThread.start();
		do { // Even more ugly - block the GUI thread so we know whether the
				// upload started, and only then switch to the next page
			Util.sleep(5);
		} while (uploadTask.getFailCount() == 0 && uploadTask.getTransferCount() == 0
				&& !uploadTask.isCanceled());

		if (uploadTask.getTransferCount() == 0) {
			Gui.asyncMessageBox("Aufbau der Verbindung zum Hochladen fehlgeschlagen", MessageType.ERROR,
					LOGGER, null);
			hashGen.cancel();
			uploadTask.cancel();
			uploadTask = null;
		}
		return new InitUploadStatus(uploadTask, hashGen);
	}

	/**
	 * GUI-BLOCKING Gives user feedback TODO
	 * 
	 * @param frame
	 * @param transferInformation
	 * @param versionInfo
	 */
	public static boolean updateImageVersion(final Frame frame, final String versionId,
			final ImageVersionWrite versionInfo) {
		try {
			ThriftManager.getSatClient().updateImageVersion(Session.getSatelliteToken(), versionId,
					versionInfo);
		} catch (TException e) {
			Gui.showMessageBox(frame, "Konnte neue Version nicht erstellen!", MessageType.ERROR, LOGGER, e);
			return false;
		}
		return true;
	}

	/* *******************************************************************************
	 * 
	 * IMAGE VERSION DOWNLOAD
	 * 
	 * Download image version action composed of the interface
	 * 'DownloadCallback' and the actual static method 'initDownload' to start
	 * the download.
	 * 
	 * **************************************************************************
	 * ****
	 */
	/**
	 * The callback interface to inform the GUI about the status of the
	 * operation
	 */
	public interface DownloadCallback {
		void downloadInitialized(boolean success);
	}

	/**
	 * NON-BLOCKING Initialises the download of the given imageVersionId saving
	 * it to the given imageName
	 * 
	 * @param frame caller of this method
	 * @param imageVersionId image version id to download
	 * @param imageName destination file name
	 * @param virtualizerId id of the virtualizer
	 * @param imageSize size in bytes of the image to download
	 * @param callback callback function to return status of this operation to
	 *            the GUI
	 */
	public static void initDownload(final Frame frame, final String imageVersionId, final String imageName,
			final String virtualizerId, final int osId, final long imageSize, final DownloadCallback callback) {
		// TODO: Return value? Callback?
		QFileChooser fc = new QFileChooser(Config.getDownloadPath(), true);
		fc.setDialogTitle("Bitte wählen Sie einen Speicherort");
		int action = fc.showSaveDialog(frame);
		File selected = fc.getSelectedFile();
		if (action != JFileChooser.APPROVE_OPTION || selected == null) {
			if (callback != null)
				callback.downloadInitialized(false);
			return;
		}

		final File destDir = new File(selected, generateDirname(imageName, imageVersionId));
		destDir.getAbsoluteFile().mkdirs();
		final File tmpDiskFile = new File(destDir.getAbsolutePath(), VmWrapper.generateFilename(imageName,
				null) + ".part");

		if (destDir.exists()) {
			boolean ret = Gui.showMessageBox(frame, "Verzeichnis '" + destDir.getAbsolutePath()
					+ "' existiert bereits, wollen Sie die VM darin überschreiben?",
					MessageType.QUESTION_YESNO, LOGGER, null);
			if (!ret) {
				// user aborted
				if (callback != null)
					callback.downloadInitialized(false);
				return;
			}
			// delete it
			if (!tmpDiskFile.delete() && tmpDiskFile.exists()) {
				Gui.showMessageBox(frame, "Datei konnte nicht überschrieben werden!", MessageType.ERROR,
						LOGGER, null);
				if (callback != null)
					callback.downloadInitialized(false);
				return;
			}
		}

		// Check the free space on disk
		if (destDir.getUsableSpace() < imageSize + SIZE_CHECK_EXTRA_DL) {
			Gui.showMessageBox(frame, "Nicht genügend Speicherplatz im ausgewählten Verzeichnis verfügbar.\n"
					+ "Brauche: " + FormatHelper.bytes(imageSize + SIZE_CHECK_EXTRA_DL, false) + "\n"
					+ "Habe: " + FormatHelper.bytes(destDir.getUsableSpace(), false), MessageType.ERROR,
					LOGGER, null);
			if (callback != null)
				callback.downloadInitialized(false);
			return;
		}

		QuickTimer.scheduleOnce(new Task() {
			@Override
			public void fire() {
				final TransferInformation transInf;
				try {
					transInf = ThriftManager.getSatClient().requestDownload(Session.getSatelliteToken(),
							imageVersionId);
				} catch (TException e) {
					ThriftError.showMessage(frame, LOGGER, e, "Die Download-Anfrage ist gescheitert");
					if (callback != null)
						callback.downloadInitialized(false);
					return;
				}

				final DownloadTask dlTask;
				try {
					dlTask = new DownloadTask(Session.getSatelliteAddress(), transInf.getPlainPort(),
							transInf.getToken(), tmpDiskFile, imageSize, null);
				} catch (FileNotFoundException e) {
					Gui.asyncMessageBox(
							"Konnte Download nicht vorbereiten: Der gewählte Zielort ist nicht beschreibbar",
							MessageType.ERROR, LOGGER, e);
					if (callback != null)
						callback.downloadInitialized(false);
					return;
				}

				dlTask.addListener(new TransferEventListener() {
					@Override
					public void update(TransferEvent event) {
						if (event.state != TransferState.FINISHED)
							return;
						DiskImage diskImage = null;
						String ext = virtualizerId;
						try {
							diskImage = new DiskImage(tmpDiskFile);
						} catch (IOException | UnknownImageFormatException e) {
							LOGGER.warn("Could not open downloaded image for analyze step", e);
						}
						if (diskImage != null) {
							if (diskImage.format != null) {
								ext = diskImage.format.extension;
							}
							if (diskImage.isCompressed) {
								Gui.asyncMessageBox("Die heruntergeladene VM '" + imageName + "' ist ein"
										+ "\nkomprimiertes Abbild. Sie müssen das Abbild dekomprimieren,"
										+ "\nbevor Sie es verändern können."
										+ "\n\n(TODO: Hinweis vmware disk tools)", MessageType.WARNING, null,
										null); // TODO
							}
						}
						File destImage = new File(destDir.getAbsolutePath(), VmWrapper.generateFilename(
								imageName, ext));
						destImage.delete();
						if (!tmpDiskFile.renameTo(destImage)) {
							destImage = tmpDiskFile; // Must be Windows...
						}
						try {
							VmWrapper.wrapVm(destImage, imageName, transInf.getMachineDescription(),
									virtualizerId, osId);
						} catch (MetaDataMissingException | IOException e) {
							Gui.asyncMessageBox(
									"Zur heruntergeladenen VM konnte keine vmx-Datei angelegt werden."
											+ "\nSie können versuchen, das Abbild manuell in den VMWare-Player zu importieren.",
									MessageType.WARNING, LOGGER, e);
						}
					}
				});

				Gui.asyncExec(new Runnable() {
					@Override
					public void run() {
						MainWindow.addDownload(imageName, tmpDiskFile.getName(), dlTask);
					}
				});

				new Thread(dlTask).start();

				Config.setDownloadPath(destDir.getParentFile().getAbsolutePath());
				if (callback != null)
					callback.downloadInitialized(true);
			}
		});
	}

	private static String generateDirname(String imageName, String imageVersionId) {
		String fileName = imageName.replaceAll("[^a-zA-Z0-9_\\.\\-]+", "_");
		if (fileName.length() > 50) {
			fileName = fileName.substring(0, 50);
		}
		fileName += "--" + imageVersionId.substring(0, 8);
		return fileName;
	}

	/* *******************************************************************************
	 * 
	 * IMAGE METADATA
	 * 
	 * Fetches image details or permissions
	 * 
	 * **************************************************************************
	 * ****
	 */
	/**
	 * TODO
	 */
	public interface ImageMetaCallback {
		void fetchedImageDetails(ImageDetailsRead details, Map<String, ImagePermissions> permissions);
	}

	public static ImageDetailsRead getImageDetails(final Frame frame, final String imageBaseId) {
		ImageDetailsRead details = null;
		try {
			details = ThriftManager.getSatClient().getImageDetails(Session.getSatelliteToken(), imageBaseId);
		} catch (TException e) {
			ThriftError.showMessage(frame, LOGGER, e, "Fehler beim Lesen der Metadaten");
		}
		return details;
	}

	/**
	 * @param frame
	 * @param imageBaseId
	 * @param callback
	 */
	public static void getImageDetails(final Frame frame, final String imageBaseId,
			final ImageMetaCallback callback) {
		QuickTimer.scheduleOnce(new Task() {
			ImageDetailsRead details = null;

			@Override
			public void fire() {
				details = ThriftActions.getImageDetails(frame, imageBaseId);
				Gui.asyncExec(new Runnable() {
					@Override
					public void run() {
						if (callback != null) {
							callback.fetchedImageDetails(details, null);
						}
					}
				});
			}
		});
	}

	public static void getImageFullDetails(final Frame frame, final String imageBaseId,
			final ImageMetaCallback callback) {
		QuickTimer.scheduleOnce(new Task() {
			ImageDetailsRead details = null;
			Map<String, ImagePermissions> permissions = null;

			@Override
			public void fire() {
				// sync calls
				details = ThriftActions.getImageDetails(frame, imageBaseId);
				permissions = ThriftActions.getImagePermissions(frame, imageBaseId);
				Gui.asyncExec(new Runnable() {
					@Override
					public void run() {
						if (callback != null) {
							callback.fetchedImageDetails(details, permissions);
						}
					}
				});
			}
		});
	}

	// async
	public static void getImagePermissions(final Frame frame, final String imageBaseId,
			final ImageMetaCallback callback) {
		QuickTimer.scheduleOnce(new Task() {
			Map<String, ImagePermissions> permissionMap = null;

			@Override
			public void fire() {
				permissionMap = ThriftActions.getImagePermissions(frame, imageBaseId);
				Gui.asyncExec(new Runnable() {
					@Override
					public void run() {
						if (callback != null) {
							callback.fetchedImageDetails(null, permissionMap);
						}
					}
				});
			}
		});
	}

	// sync
	public static Map<String, ImagePermissions> getImagePermissions(final Frame frame,
			final String imageBaseId) {
		Map<String, ImagePermissions> permissionMap = null;
		try {
			permissionMap = ThriftManager.getSatClient().getImagePermissions(Session.getSatelliteToken(),
					imageBaseId);
		} catch (TException e) {
			ThriftError.showMessage(frame, LOGGER, e, "Fehler beim Lesen der Metadaten");
		}
		return permissionMap;
	}

	/**
	 * GUI-BLOCKING Sets the owner of the given lectureId to newOwner
	 * 
	 * @param frame to display user feedback on
	 * @param lectureId lecture's id to set the new owner of
	 * @param newOwner as UserInfo
	 * @return true if it worked, false otherwise
	 */
	public static boolean setImageOwner(final Frame frame, final String lectureId, final UserInfo newOwner) {
		try {
			ThriftManager.getSatClient().setImageOwner(Session.getSatelliteToken(), lectureId,
					newOwner.getUserId());
		} catch (TException e) {
			ThriftError.showMessage(frame, LOGGER, e, "Fehler beim Übertragen der Besitzrechte");
			return false;
		}
		return true;
	}

	/* *******************************************************************************
	 * 
	 * IMAGE / VERSION DELETION
	 * 
	 * Deletes a specific image version
	 * 
	 * **************************************************************************
	 * ****
	 */
	/**
	 * Delete callback interface to be implemented by callers of
	 * ThriftActions.deleteImageVersion(..)
	 */
	public interface DeleteCallback {
		/**
		 * Called once the status of a delete operation is determined
		 * 
		 * @param success true if deleted successfully, false otherwise
		 */
		void isDeleted(boolean success);
	}

	public static void deleteImageBase(final Frame frame, final String imageBaseId,
			final DeleteCallback callback) {
		if (imageBaseId == null || imageBaseId.isEmpty())
			return;
		// first look if we have versions
		ImageDetailsRead details = null;
		// these help construct the question text for the user, bit ugly...
		List<LectureSummary> lecturesToBeDeleted = null;
		List<ImageVersionDetails> versionToBeDeleted = null;
		try {
			details = ThriftManager.getSatClient().getImageDetails(Session.getSatelliteToken(), imageBaseId);
			List<LectureSummary> lectureList = ThriftManager.getSatClient().getLectureList(
					Session.getSatelliteToken(), 100);
			for (LectureSummary lecture : lectureList) {
				if (lecture.getImageBaseId().equals(imageBaseId)) {
					if (lecturesToBeDeleted == null)
						lecturesToBeDeleted = new ArrayList<LectureSummary>();
					lecturesToBeDeleted.add(lecture);
				}
			}
			for (ImageVersionDetails version : details.getVersions()) {
				if (version.isValid) {
					if (versionToBeDeleted == null)
						versionToBeDeleted = new ArrayList<ImageVersionDetails>();
					versionToBeDeleted.add(version);
				}
			}

		} catch (TException e) {
			ThriftError.showMessage(frame, LOGGER, e,
					"Fehler beim Holen der Versionen/Veranstaltung zu folgendes Image: " + imageBaseId);
			return;
		}
		String questionText = "";
		if (versionToBeDeleted != null) {
			questionText = "Dieses Image hat folgende gültige Versionen:\n";
			for (ImageVersionDetails version : versionToBeDeleted) {
				questionText += version.getVersionId() + "\n";
			}
		} else {
			questionText = "Dieses Image hat keine gültige Version.\n";
		}
		if (lecturesToBeDeleted != null) {
			if (!questionText.isEmpty())
				questionText += "\n";
			questionText += "Dieses Image ist zu folgende Veranstaltungen verknüpft:\n";
			for (LectureSummary lecture : lecturesToBeDeleted) {
				questionText += lecture.getLectureName() + "\n";
			}
		} else {
			if (!questionText.isEmpty())
				questionText += "\n";
			questionText += "Dieses Image ist zu keine Veranstaltungen verknüpft.\n";
		}
		questionText += "\nWollen Sie dieses Image wirklich löschen?\n";
		if (!userConfirmed(frame, questionText))
			return;
		try {
			ThriftManager.getSatClient().deleteImageBase(Session.getSatelliteToken(), imageBaseId);
		} catch (TException e) {
			// TODO Auto-generated catch block
			ThriftError.showMessage(frame, LOGGER, e, "Konnte Basis-Image nicht löschen!");
		}

	}

	/**
	 * NON-BLOCKING Deletes either an image base or an image version depending
	 * on the parameters. To delete an image base, give the imageBaseId and let
	 * imageVersionId be null. To delete an image version, set both imageBaseId
	 * and imageVersionId. The success of the operation will be forwarded to the
	 * GUI through the DeleteCallback.
	 * 
	 * @param frame next parent frame of the caller of this method
	 * @param imageBaseId uuid of the image that belongs to the version
	 * @param imageVersionId id of the image version to be deleted
	 * @param callback called to inform the GUI about the deletion status (see
	 *            DeleteCallback interface)
	 */
	public static void deleteImageVersion(final Frame frame, final String imageVersionId,
			final DeleteCallback callback) {
		// TODO async?
		if (imageVersionId == null || imageVersionId.isEmpty())
			return;
		boolean success = false;
		List<LectureSummary> lectureList = null;
		try {
			// fetch lectures
			lectureList = ThriftManager.getSatClient().getLectureList(Session.getSatelliteToken(), 100);
		} catch (TException e) {
			ThriftError.showMessage(frame, LOGGER, e, "Fehler beim Holen der Liste der Veranstaltungen");
			if (callback != null)
				callback.isDeleted(success);
			return;
		}
		String questionText = "";
		// represents if we found matches of not, needed to make a proper
		// message
		boolean matches = false;
		if (lectureList != null && !lectureList.isEmpty()) {
			for (LectureSummary lecture : lectureList) {
				if (lecture.getImageVersionId().equals(imageVersionId)) {
					if (!matches)
						questionText = "Diese Version ist zu folgende Veranstaltungen verknüpft:\n";
					matches = true;
					questionText += lecture.getLectureName() + "\n";
				}
			}
			if (matches)
				questionText += "\nWollen Sie diese Version samt Veranstaltungen löschen?\n";
		}
		if (!matches)
			questionText = "Diese Version ist zu keine Veranstaltungen verknüpft.\n"
					+ "Wollen Sie sie wirklich löschen?";

		if (!userConfirmed(frame, questionText))
			return;
		try {
			ThriftManager.getSatClient().deleteImageVersion(Session.getSatelliteToken(), imageVersionId);
			LOGGER.info("Deleted version '" + imageVersionId + "'.");
			success = true;
		} catch (TException e) {
			ThriftError.showMessage(frame, LOGGER, e, "Fehler beim Löschen der Version");
			if (callback != null)
				callback.isDeleted(success);
			return;
		}
		final boolean fSuccess = success;
		Gui.asyncExec(new Runnable() {
			@Override
			public void run() {
				if (callback != null)
					callback.isDeleted(fSuccess);
			}
		});
	}

	/* *******************************************************************************
	 * 
	 * LECTURE CREATION
	 * 
	 * Methods to create lectures
	 * 
	 * **************************************************************************
	 * ****
	 */
	/**
	 * Creates a lecture with the given meta data
	 * 
	 * @param frame to show user feedback on
	 * @param meta actual meta data as LectureWrite
	 * @return the created lecture's id if it worked, null otherwise
	 */
	public static String createLecture(final Frame frame, final LectureWrite meta) {
		if (meta == null)
			return null;
		String uuid = null;
		try {
			// push to sat
			uuid = ThriftManager.getSatClient().createLecture(Session.getSatelliteToken(), meta);
		} catch (TException e) {
			ThriftError.showMessage(frame, LOGGER, e, "Failed to create lecture");
		}
		return uuid;
	}

	/**
	 * GUI-BLOCKING Writes custom lecture permissions (permissions param) for
	 * the given lectureId.
	 * 
	 * @param frame to show user feedback on
	 * @param lectureId lecture's id to write custom permissions for
	 * @param permissions actual permission map to push
	 */
	public static boolean writeLecturePermissions(final Frame frame, final String lectureId,
			final Map<String, LecturePermissions> permissions) {
		try {
			ThriftManager.getSatClient().writeLecturePermissions(Session.getSatelliteToken(), lectureId,
					permissions);
		} catch (TException e) {
			ThriftError.showMessage(frame, LOGGER, e, "Failed to write lecture permissions");
			return false;
		}
		return true;
	}

	/* *******************************************************************************
	 * 
	 * LECTURE METADATA
	 * 
	 * Methods to push metadata of lectures
	 * 
	 * **************************************************************************
	 * ****
	 */
	/**
	 * @author joe
	 * 
	 */
	public interface LectureMetaCallback {
		void fetchedLectureDetails(LectureRead lecture, ImageDetailsRead image);

		void fetchedLecturePermissions(Map<String, LecturePermissions> permissions);
	}

	/**
	 * NON-BLOCKING
	 * 
	 * @param frame
	 * @param lectureId
	 * @param callback
	 */
	public static void getLectureWithImageDetails(final Frame frame, final String lectureId,
			final LectureMetaCallback callback) {
		QuickTimer.scheduleOnce(new Task() {
			@Override
			public void fire() {
				final LectureRead lecture = ThriftActions.getLectureDetails(frame, lectureId);
				ImageDetailsRead image = null;
				if (lecture != null) {
					image = ThriftActions.getImageDetails(frame, lecture.getImageBaseId());
				}
				if (image == null)
					return;
				final ImageDetailsRead fImage = image;
				Gui.asyncExec(new Runnable() {
					@Override
					public void run() {
						if (callback != null) {
							callback.fetchedLectureDetails(lecture, fImage);
						}
					}
				});
			}
		});
	}

	/**
	 * GUI-BLOCKING
	 * 
	 * @param frame
	 * @param lectureId
	 * @param callback
	 */
	public static LectureRead getLectureDetails(final Frame frame, final String lectureId) {
		LectureRead lecture = null;
		try {
			lecture = ThriftManager.getSatClient().getLectureDetails(Session.getSatelliteToken(), lectureId);
		} catch (TException e) {
			ThriftError.showMessage(frame, LOGGER, e, "Konnte Veranstaltungdaten nicht abrufen");
		}
		return lecture;
	}

	/**
	 * GUI-BLOCKING Updates the lecture with given lectureId to the given
	 * lecture
	 * 
	 * @param frame to show user feedback on
	 * @param lectureId lecture's is to update
	 * @param lecture LectureWrite data to update the lecture with
	 * @return
	 */
	public static boolean updateLecture(final Frame frame, final String lectureId,
			final LectureWrite lectureWrite) {
		try {
			ThriftManager.getSatClient().updateLecture(Session.getSatelliteToken(), lectureId, lectureWrite);
		} catch (TException e) {
			ThriftError.showMessage(frame, LOGGER, e, "Fehler beim Updaten der Veranstaltung");
			return false;
		}
		return true;
	}

	/**
	 * GUI-BLOCKING
	 * 
	 * @param frame
	 * @param lectureId
	 * @param callback
	 */
	public static Map<String, LecturePermissions> getLecturePermissions(final Frame frame,
			final String lectureId) {
		Map<String, LecturePermissions> permissions = null;
		try {

			permissions = ThriftManager.getSatClient().getLecturePermissions(Session.getSatelliteToken(),
					lectureId);
		} catch (TException e) {
			ThriftError.showMessage(frame, LOGGER, e, "Konnte Veranstaltungdaten nicht abrufen");
		}
		return permissions;

	}

	/**
	 * GUI-BLOCKING Sets the owner of the given lectureId to newOwner
	 * 
	 * @param frame to display user feedback on
	 * @param lectureId lecture's id to set the new owner of
	 * @param newOwner as UserInfo
	 * @return true if it worked, false otherwise
	 */
	public static boolean setLectureOwner(final Frame frame, final String lectureId, final UserInfo newOwner) {
		try {
			ThriftManager.getSatClient().setLectureOwner(Session.getSatelliteToken(), lectureId,
					newOwner.getUserId());
		} catch (TException e) {
			ThriftError.showMessage(frame, LOGGER, e, "Fehler beim Übertragen der Besitzrechte");
			return false;
		}
		return true;
	}

	/* *******************************************************************************
	 * 
	 * LECTURE DELETION
	 * 
	 * Methods to delete lectures
	 * 
	 * **************************************************************************
	 * ****
	 */
	/**
	 * Callback interface reporting the status of the deletion back to the gui
	 */
	public interface DeleteLectureCallback {
		void deleted(boolean success);
	}

	/**
	 * NON-BLOCKING Deletes the lecture with the given lectureId
	 * 
	 * @param frame
	 * @param lectureId
	 * @param callback
	 */
	public static void deleteLecture(final Frame frame, final String lectureId,
			final DeleteLectureCallback callback) {
		if (lectureId == null)
			return;
		if (!userConfirmed(frame, "Wollen Sie diese Veranstaltung wirklick löschen?"))
			return;
		QuickTimer.scheduleOnce(new Task() {
			boolean success = false;

			@Override
			public void fire() {
				try {
					ThriftManager.getSatClient().deleteLecture(Session.getSatelliteToken(), lectureId);
					success = true;
				} catch (TException e) {
					ThriftError.showMessage(frame, LOGGER, e, "Konnte Veranstaltungdaten nicht abrufen");
				}
				Gui.asyncExec(new Runnable() {
					@Override
					public void run() {
						if (callback != null) {
							callback.deleted(success);
						}
					}
				});
			}
		});
	}

	/* *******************************************************************************
	 * 
	 * PRIVATE HELPERS
	 * 
	 * **************************************************************************
	 * ****
	 */
	/**
	 * Helper to ask the user for confirmation. Returns his choice.
	 * 
	 * @param frame frame to show this message box on
	 * @param message question message to display to the user
	 * @return true if the user confirmed (clicked yes), false otherwise
	 */
	private static boolean userConfirmed(final Frame frame, final String message) {
		return Gui.showMessageBox(frame, message, MessageType.QUESTION_YESNO, LOGGER, null);
	}

	/**
	 * Wrapper class for UploadTask & AsyncHashGenerator
	 * 
	 */
	public static class InitUploadStatus {
		public final UploadTask task;
		public final AsyncHashGenerator hasher;

		public InitUploadStatus(final UploadTask task, final AsyncHashGenerator hasher) {
			this.task = task;
			this.hasher = hasher;
		}
	}
}