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


                                  
                       


                                     
                           
                         
                          
                      
                     




                                    
                                                       
                                                    

                                                      
                                                      
                                                         
                                                       
                                                        
                                                 
                                                    
                                                  
                                               
                                                            
                                                             
                                                          
                                                        
                                                         
                                                   
                                              

                                                
                                   
                                 

                                                                          
                                                    

                                                             
                                                   
                                  


                                                  
                                                         



                                                     
                                            

                                                                  
                                              


                                              

                                                                 

                            
 
                                                                                   
                                                                            
 

                                                                                          
                  
           
                                 
           
                                                                                          
           
           
                                         
                                                                                 
                                                               


                                                                                    
                                                                           
           
                                                                                                                 
                                                           




                                          
                                        

                                                                                                                     
                                                                                                                












                                                                                                                              
                                

                                                                            
                                                     



                                                                                                
                                 
                                                                        
                                                  












                                                                                                                                       

                                                         








                                                                                                                                     
                                         
                                 

                                                                                           
                                                                                                                               






                                                                                                                                               

                                                                                                         
                         
 



                                                                                                                 
                                            

                                             
                                                                                           



                                                                                  
                         
 


                                                                    
                                                                                                                                                           

                                                                                                                                                                   


                                                                          


                                                 
                         
 
                                                                                                     

                                                                                                                      
                                                                                                                         
                                                                                                                          



                                                                                         
                         
 





                                                                                  
                                                                                                                                                 
                                                                                                                      





                                                                                                                  
                                                                                                                                        




                                                  
                                                                                                                          



                                                                                      
                         
                                                        
 
                                     
                                                                                                  

                                                                                                          
                                                                                                                   










                                                                            



                                    
 


                                                                                          
           

                                                   
                                                                                          
           
           

                                                                               
           
                                           





                                                                                                           
                                        
                                                                                                     
                                       

                                                                                                                        


                            
 
           

                                                                            
           

                                                                           



                                          
           


                                                                                                              
         
 
           

                                                                                    
           

                                                                      



                                          
           




                                                                                                            
         
 


                                                                                          
           


                                                                                  
           
                                                                                         
           
 
           
                       
           


                                     



                                          
           


                                                                                                                     
         
 


                                                                                          
           


                                                                                  
           
                                                                                          
           
           

                                                                           



                                                          
 
           

                                                                                   
           






                                                                                  

                                                                                                               
                                                                                                                            
                                                


                                                                                   
                                                     


                                                                                
                               
                 
 
                                                                                                    

                                                                                                                  
 


                                                                                                           
                                                                                  

                                               

                                                                            


                                       
                                                                            

                                                                                                                         

                                                                            

                                       

                                                           

                 
                                               

                                                                                                                            


                                                                                                                            

                                                                    


                               


                                                    






                                                                                                                                           
                                     

                                                                                                                            
                                                                                  
                                                        















                                                                                                                                     



                                                                                    

                                                                               

                                                          
                                                                                                      
                                                                                                           


                                                                                                                                          




                                                                                      
 
                                                                                              
















                                                                                                                           



                                                                                                                                                              
                                                                                                                                        



                                                                                                                   








                                                                                                                                
                                                                                                                                 
                                                                                                        



                                                                                                                                                                             
                                                                                                        


                                                 



                                                              
                                                                                                                 

                                         
 

                                                           
                                                                                                  

                                                                           


                         
 
           



                                                                                 


                                             
                                           

                                                     








                                                                                        


                                                                                          
           

                                                                                           
           

                                                    
           
                                            
                                                                                                              
         
 
           

                                                                         




                                                                                 


                                                                                                     
                                                                                                                         




                                                                                                     
 
           



                                                                                 




                                                                          
           

                                                                                       

                                                        
 

                                            
                                                                                            



                                                                       






                                                                                                    














                                                                                                                      
           

                                                                            




                                                                                  

                                                                                           

                                                                           
 

                                            
                                                                                                      



                                                                       
                                                                                                          





                                                 
 






                                                                                    

                                                                                          

                                                                   

                                                                                                                     




                                                                                                     
 
           
                                                                     
           


                                                                



                                                                                                                 

                                                                                                          





                                                                                                              
 


                                                                                          
           
                                                                                           
           
                                                             





                                                                             
                                                                               


                                                
 





                                                                                 
           
                                                                                         

                                                                 

                                                 


                                                                                   

                                                                                                                         

                                                                                                       
                                                                    
                                                                                   

                                                                                                      


                                                                         






                                                                                                          
 
                                        
                                                                 
                                                                                                                         

                               

                                                                                  
                                                                                                                      
                                                                                

                                                                              
                                             
                        
                                          
                 
                                                                                    
                                                                                                     

                                                                                
                         
                                             
                 
                                                                                   




                                                                                                               
                                                                                                     

                 
 
           
                                                                              



                                                                                    
           

                                                                           
                                                               
                                                                                  
                                               
           
                                                                                                   
                                                        
                                                                                                
                               
                                                     










                                                                                                                     
                                         

                                                                                 

                                                                    
                                                                    
                                                                                    


                                                                                                                             


                                                                                        

                                                                                                              
                 
                             

                                                                                                                 
 
                                                        
                               
                     

                                                                                                                








                                                                                                      
                                 


                                                                     


                         
 


                                                                                          
           
                                                                                           
           
                                                              
           

                                                       













                                                                                                             
 
           
                                                                             
                               
           


                                                                        
           

                                                                                                
                     

                                                                                                                    

                                                                                                         
                                     
                 
                            
         
 
                                                                                          
           
                           
           

                                                                                           
           
                                                                     

                                              
                                                                                                
         
 
           



                                                                                   
           


                                                                     
           
                                                                                               
                                                             
                                                    

                                            
                                                                                                              




                                                                                                                
                                 



                                                                       
                                                                                                                





                                                 
 
           
                                                                                
           


                                                                         
           








                                                                                                                         
 
           
                                                                                 
           


                                                                      
                  
           

                                                                                      
                     
                                                                                                                         





                                                                                                           
 
           

                                                                                  
                          
           

                                                                      

                                                                             
           

                                                                                              


                                                                   

                                                                                                                     




                                                                                                             
         
 
           
                                                                     
           


                                                                



                                                                                                                   

                                                                                                            





                                                                                                              
 
                                                                                          
           
                           
           
                                                                                           


                                                                                  
                                                
                                                                             
         
 
           
                                                              
           
                                                   


                                                                               
           
                                                                                                
                                                               
                                     
                               


                                                                                                               
                               
 
                                                    
                                                                                                                   
 

                                            







                                                                                                                                     




                                                                       
                                                                                         





                                                 















                                                                                                                      
                                                                                                                                 


                            


                                                                                          
           

                                                                                           
           

                                                                       

                                                                 


                                                                                       
                                                                                                  
         
 
 
package org.openslx.dozmod.thrift;

import java.awt.Frame;
import java.awt.Window;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.swing.JFileChooser;

import org.apache.log4j.Logger;
import org.apache.thrift.TException;
import org.apache.thrift.transport.TTransportException;
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.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.Satellite;
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.TNotFoundException;
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.Branding;
import org.openslx.dozmod.Config;
import org.openslx.dozmod.Config.SavedSession;
import org.openslx.dozmod.authentication.Authenticator.AuthenticationData;
import org.openslx.dozmod.filetransfer.DownloadTask;
import org.openslx.dozmod.filetransfer.TransferEvent;
import org.openslx.dozmod.filetransfer.TransferEventListener;
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.gui.window.SatelliteListWindow;
import org.openslx.dozmod.thrift.cache.ImageCache;
import org.openslx.dozmod.thrift.cache.LectureCache;
import org.openslx.dozmod.thrift.cache.MetaDataCache;
import org.openslx.dozmod.thrift.cache.UserCache;
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.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;

	/* *******************************************************************************
	 * 
	 * SESSION
	 * 
	 * Session initialization
	 * 
	 * *******************************************************************************
	 */
	/**
	 * @param window the parentWindow
	 * @param data AuthenticationData as received from a successful login, or
	 *            null if trying to resume a saved sessions
	 * @param forceCustomSatellite show satellite selection dialog even if there
	 *            is just one
	 * @param loginWindow
	 * @return true if initialising the session worked, false otherwise
	 */
	public static boolean initSession(AuthenticationData data, boolean forceCustomSatellite, Window window) {
		final boolean interactive = (data != null);
		Client client = null;
		WhoamiInfo whoami = null;
		String address = null;
		String satToken = null;
		String masterToken = null;
		long remoteVersion = -1;

		if (interactive && !forceCustomSatellite && (data.satellites == null || data.satellites.isEmpty())) {
			Gui.asyncMessageBox("Login erfolgreich, aber es wurde kein Satellitenserver gefunden.\n"
					+ " Bitte geben Sie die Adresse Ihres Servers manuell an.", MessageType.ERROR, LOGGER,
					null);
		}

		do {
			if (!interactive) {
				// in session resume
				SavedSession session = Config.getSavedSession();
				if (session == null)
					return false;
				address = session.address;
				satToken = session.token;
				masterToken = session.masterToken;
			} else {
				// determine which sat to connect to
				// first check if a sat selection was forced
				Satellite sat = null;
				if (forceCustomSatellite) {
					sat = SatelliteListWindow.open(window, data.satellites);
					if (sat == null)
						return false;
				}
				// not forced, do regular sat processing
				if (sat == null) {
					// if we received only one sat, use that one
					if (data.satellites != null && data.satellites.size() == 1) {
						sat = data.satellites.get(0);
					}
					// we had more than one, so check the list
					if (sat == null) {
						if (!forceCustomSatellite) {
							// Remove testing servers (those starting with {x}) if shift was not pressed
							for (Iterator<Satellite> it = data.satellites.iterator(); it.hasNext();) {
								Satellite entry = it.next();
								if (entry.displayName != null && entry.displayName.startsWith("{x}")) {
									it.remove();
								}
							}
						}
						// after removing the test sats, check if we have only one left and directly use that
						// instead of asking the user
						if (data.satellites.size() != 1) {
							sat = SatelliteListWindow.open(window, data.satellites);
							if (sat == null)
								return false;
						} else {
							sat = data.satellites.get(0);
						}
					}
				}
				if (sat.addressList == null || sat.addressList.isEmpty()) {
					Gui.asyncMessageBox(
							"Login erfolgreich, aber für den ausgewählten Satellitenserver ist\n"
									+ "keine Adresse hinterlegt. Kann nicht verbinden.", MessageType.ERROR,
							LOGGER, null);
					continue;
				}
				address = sat.addressList.get(0);
				satToken = data.satelliteToken;
				masterToken = data.masterToken;
				// In case we loop, make sure the window is shown on the second iteration
				forceCustomSatellite = true;
			}

			// common for resume and fresh login
			// try to get a new client
			client = ThriftManager.getNewSatelliteClient(GraphicalCertHandler.getSslContext(address),
					address, App.THRIFT_SSL_PORT, 5000);
			// RPC version check
			if (client != null) {
				try {
					remoteVersion = client.getVersion(Version.VERSION);
				} catch (TTransportException e) {
				} catch (TException e) {
					remoteVersion = 1; // Assume something old
				}
			}

			if (client == null || remoteVersion == -1) {
				if (interactive) {
					Gui.asyncMessageBox(
							"Authentifizierung erfolgreich, die Verbindung zum Satellitenserver ist jedoch nicht möglich.\n\n"
									+ "Möglicherweise ist der Server nicht verfügbar, oder die Netzwerkverbindung gestört.",
							MessageType.ERROR, null, null);
					if (data.satellites.size() == 1) {
						return false;
					}
					continue;
				}
				return false;
			}

			if (remoteVersion < Version.MIN_VERSION || remoteVersion > Version.VERSION) {
				if (interactive) {
					Gui.asyncMessageBox("Das von Ihnen verwendete Dozentenmodul ist nicht mit dem"
							+ " gewählten Satellitenserver kompatibel.\n" + "Ihre Version: "
							+ Version.VERSION + "\n" + "Satelliten-Version: " + remoteVersion,
							MessageType.ERROR, LOGGER, null);
					continue;
				}
				return false;
			}

			// all good, try to get the whoami info
			try {
				whoami = client.whoami(satToken);
			} catch (TAuthorizationException e) {
				if (interactive) {
					ThriftError.showMessage(window, LOGGER, e,
							"Authentifizierung erfolgreich, der Satellitenserver verweigert jedoch die Verbindung.\n"
									+ "Versuchen Sie, sich erneut anzumelden.\n");
				}
				return false;
			} catch (TException e) {
				if (interactive) {
					ThriftError.showMessage(window, LOGGER, e,
							"Authentifizierung erfolgreich, bei der Kommunikation mit"
									+ " dem Satellitenserver trat jedoch ein interner Fehler auf.");
					continue;
				}
				return false;
			} catch (Exception e) {
				if (interactive) {
					Gui.asyncMessageBox("Unbekannter Fehler beim Verbinden mit dem Satellitenserver.",
							MessageType.ERROR, LOGGER, e);
					continue;
				}
				return false;
			}
		} while (interactive && whoami == null);

		if (whoami != null) {
			Session.initialize(whoami, address, satToken, masterToken, remoteVersion);
			ThriftManager.setSatelliteAddress(
					GraphicalCertHandler.getSslContext(Session.getSatelliteAddress()),
					Session.getSatelliteAddress(), App.THRIFT_SSL_PORT, App.THRIFT_TIMEOUT_MS);
			QuickTimer.scheduleOnce(new Task() {
				@Override
				public void fire() {
					// Cache useful data from server
					MetaDataCache.getOperatingSystems();
					MetaDataCache.getVirtualizers();
					UserCache.getAll();
					ImageCache.get(false);
					LectureCache.get(false);
				}
			});
			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 der VM 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 imageBaseId image's id we are writing meta information of
	 * @param meta actual meta information as ImageBaseWrite
	 * @throws TException
	 * @throws TInvocationException
	 * @throws TNotFoundException
	 * @throws TAuthorizationException
	 */
	public static void updateImageBase(final String imageBaseId, final ImageBaseWrite meta)
			throws TAuthorizationException, TNotFoundException, TInvocationException, TException {
		ThriftManager.getSatClient().updateImageBase(Session.getSatelliteToken(), imageBaseId, meta);
	}

	/**
	 * GUI-BLOCKING Pushes the given permission map as custom permission for the
	 * given imageBaseId
	 * 
	 * @param imageBaseId image's id we are writing permissions of
	 * @param permissions actual permissions map to write
	 * @throws TException
	 * @throws TInvocationException
	 * @throws TNotFoundException
	 * @throws TAuthorizationException
	 */
	public static void writeImagePermissions(final String imageBaseId,
			final Map<String, ImagePermissions> permissions) throws TAuthorizationException,
			TNotFoundException, TInvocationException, TException {
		ThriftManager.getSatClient().writeImagePermissions(Session.getSatelliteToken(), imageBaseId,
				permissions);
	}

	/* *******************************************************************************
	 * 
	 * 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
	 * 
	 * @param frame
	 * @param transferInformation
	 * @param versionInfo
	 * @throws TException
	 * @throws TInvocationException
	 * @throws TNotFoundException
	 * @throws TAuthorizationException
	 */
	public static void updateImageVersion(final String versionId, final ImageVersionWrite versionInfo)
			throws TAuthorizationException, TNotFoundException, TInvocationException, TException {
		ThriftManager.getSatClient().updateImageVersion(Session.getSatelliteToken(), versionId, versionInfo);
	}

	/* *******************************************************************************
	 * 
	 * 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));
		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;
			}
		} else {
			destDir.getAbsoluteFile().mkdirs();
		}

		// 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() {
				// since this function (initDownload) supports both download from the satellite and from the masterserver
				// we simply try to download from the sat first and if that fails, we try to download from the masterserver
				// this has the nice side effect, that if a download from the masterserver is requested, it tries
				// to download that image from the sat first (which is probably faster than from the masterserver)
				TException transEx = null;
				String transHost = null;
				TransferInformation transInf = null;
				try {
					transInf = ThriftManager.getSatClient().requestDownload(Session.getSatelliteToken(),
							imageVersionId);
					transHost = Session.getSatelliteAddress();
				} catch (TException e) {
					transEx = e;
				}
				if (transInf == null) {
					// satellite denied download, try master
					transHost = null;
					try {
						transInf = ThriftManager.getMasterClient().downloadImage(Session.getSatelliteToken(),
								imageVersionId);
						transHost = App.getMasterServerAddress();
					} catch (TException e) {
						transEx = e;
					}
				}
				if (transInf == null) {
					// both download request failed, show user feedback
					ThriftError.showMessage(frame, LOGGER, transEx, "Die Download-Anfrage ist gescheitert");
					if (callback != null)
						callback.downloadInitialized(false);
					return;
				}
				final String fTransHost = transHost;
				final TransferInformation fTransInf = transInf;
				final DownloadTask dlTask;
				try {
					dlTask = new DownloadTask(fTransHost, 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.setMinConnections(Config.getTransferConnectionCount());
				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) {
								String msg = "<html>Die heruntergeladene VM '" + imageName + "' ist ein komprimiertes "
										+ "Abbild.<br>Sie müssen das Abbild dekomprimieren, bevor Sie es verändern "
										+ "können.<br> Die VM wird lokal voraussichtlich nicht startfähig sein!"
										+ "<br><br>Bitte lesen Sie die Hinweise unter "
										+ "<a href=\"" + Branding.getServiceFAQWebsite() + "\">"
										+ "VMDK Disk Types</a>";

								Gui.asyncMessageBox(msg, MessageType.WARNING, null,
										null);
							}
						}
						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, fTransInf.getMachineDescription(),
									virtualizerId, osId, diskImage);
						} 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);
			}
		});
	}

	/**
	 * Helper to generate a directory name for the given imageName and
	 * imageVersionId.
	 * Uses the given parameters to build a hopefully unique name as it takes
	 * the first 8
	 * chars of the version id...
	 * 
	 * @param imageName name of the image
	 * @param imageVersionId version id
	 * @return generated directory name as String
	 */
	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
	 * 
	 * *******************************************************************************/

	/**
	 * Callback interface for image meta calls. 
	 * 
	 */
	public interface ImageMetaCallback {
		void fetchedImageDetails(ImageDetailsRead details, Map<String, ImagePermissions> permissions);
	}

	/**
	 * BLOCKING Gets the image details (w/o permissions) of the given
	 * imageBaseID
	 * 
	 * @param frame to display user feedback on
	 * @param imageBaseId image's id to get the details of
	 * @return details as ImageDetailsRead if fetching worked, null otherwise
	 */
	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;
	}

	/**
	 * NON-BLOCKING Gets the image details (without permissions) of the given
	 * imageBaseId.
	 * Will return the details (or null if fetching failed) back to the
	 * gui-thread
	 * through the given callback
	 * 
	 * @param frame to display user feedback on
	 * @param imageBaseId image's id to get the details of
	 * @param callback interface to return the details back to the gui
	 */
	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);
						}
					}
				});
			}
		});
	}
	/**
	 * BLOCKING Gets the data for image with UUID imageBaseId
	 * 
	 * @param imageBaseId
	 * @return ImagePublishData image's data if sucessful, null otherwise.
	 */
	public static ImagePublishData getImageData(final String imageBaseId) {
		ImagePublishData data = null;
		try {
			data = ThriftManager.getMasterClient().getImageData(Session.getSatelliteToken(), imageBaseId);
		} catch (TException e) {
			LOGGER.error("Could not query sat for ImagePublishData for '" + imageBaseId + "':", e); 
		}
		return data;
	}
	/**
	 * NON-BLOCKING Gets the user-specific permission list for the given
	 * imageBaseId
	 * 
	 * @param frame to display user feedback on
	 * @param imageBaseId image's id to get the permission list of
	 * @param callback interface to return the permission list back to the gui
	 */
	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);
						}
					}
				});
			}
		});
	}

	/**
	 * BLOCKING Gets the user-specific permission list for the given imageBaseId
	 * 
	 * @param frame to display user feedback on
	 * @param imageBaseId image's base id to get the user permissions of
	 * @return the map userid-permissions if fetching worked, null otherwise
	 */
	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;
	}

	/**
	 * 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
	 * 
	 * *******************************************************************************/
	/**
	 * 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);
	}

	/**
	 * BLOCKING Deletes the base image with the given id. Will call the given
	 * callback to report about the status of this operation
	 * 
	 * @param frame to display user feedback on
	 * @param imageBaseId image base id to delete
	 */
	public static void deleteImageBase(final Frame frame, final String imageBaseId) {
		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 (imageBaseId.equals(lecture.getImageBaseId())) {
					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 folgender VM: " + imageBaseId);
			return;
		}
		String questionText;
		if (versionToBeDeleted != null && !versionToBeDeleted.isEmpty()) {
			questionText = "Die VM \"" + details.getImageName() + "\" hat folgende gültige Versionen:\n";
			for (ImageVersionDetails version : versionToBeDeleted) {
				questionText += version.getVersionId() + "\n";
			}
			questionText += "\n";
		} else {
			questionText = "";
		}
		if (lecturesToBeDeleted != null && !lecturesToBeDeleted.isEmpty()) {
			questionText += "Folgende Veranstaltungen sind mit dieser VM verknüpft: \n";
			for (LectureSummary lecture : lecturesToBeDeleted) {
				questionText += lecture.getLectureName() + "\n";
			}
			questionText += "\n";
		}
		questionText += "Wollen Sie wirklich mit dem Löschen fortfahren?";
		if (!userConfirmed(frame, questionText))
			return;
		try {
			ThriftManager.getSatClient().deleteImageBase(Session.getSatelliteToken(), imageBaseId);
		} catch (TException e) {
			ThriftError.showMessage(frame, LOGGER, e, "Konnte VM-Daten nicht löschen!");
		}
	}

	/**
	 * 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 version 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 ImageVersionDetails version,
			final DeleteCallback callback) {
		if (version == null || version.versionId == null || version.versionId.isEmpty())
			return;
		String versionId = version.versionId;
		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 (versionId.equals(lecture.getImageVersionId())) {
					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 = "Wollen Sie die VM-Image-Version vom "
					+ FormatHelper.shortDate(version.createTime) + " Uhr wirklich löschen?";

		if (!userConfirmed(frame, questionText))
			return;
		try {
			ThriftManager.getSatClient().deleteImageVersion(Session.getSatelliteToken(), versionId);
			LOGGER.info("Deleted version '" + versionId + "'.");
			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
	 * 
	 * *******************************************************************************/
	/**
	 * BLOCKING 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;
	}

	/**
	 * 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
	 * 
	 * *******************************************************************************/

	/**
	 * Interface for GUI-classes running async lecture meta calls
	 */
	public interface LectureMetaCallback {
		void fetchedLectureAndImageDetails(LectureRead lecture, ImageDetailsRead image);
	}

	/**
	 * NON-BLOCKING Gets the lecture and image details of the given lectureId.
	 * IF the lecture does not exist, both fields returned will be null. If the
	 * lecture exists, the image can still be null, since not all lectures link
	 * to an image at all times.
	 * 
	 * @param frame to display user feedback on
	 * @param lectureId lecture to get the full details of
	 * @param callback interface to return the details to the gui
	 */
	public static void getLectureAndImageDetails(final Frame frame, final String lectureId,
			final LectureMetaCallback callback) {
		QuickTimer.scheduleOnce(new Task() {
			@Override
			public void fire() {
				final LectureRead lecture = ThriftActions.getLectureDetails(frame, lectureId);
				final ImageDetailsRead fImage;
				if (lecture != null && lecture.imageBaseId != null) {
					fImage = ThriftActions.getImageDetails(frame, lecture.getImageBaseId());
				} else {
					fImage = null;
				}
				Gui.asyncExec(new Runnable() {
					@Override
					public void run() {
						if (callback != null) {
							callback.fetchedLectureAndImageDetails(lecture, fImage);
						}
					}
				});
			}
		});
	}

	/**
	 * BLOCKING Gets the lecture details of the lecture with given lectureId
	 * 
	 * @param frame to display user feedback on
	 * @param lectureId lecture to get the details of
	 * @return LectureRead if fetching details worked, null otherwise
	 */
	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;
	}

	/**
	 * 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;
	}

	/**
	 * BLOCKING Gets the list of user-specific permission for the lecture with
	 * the
	 * given lectureId
	 * 
	 * @param frame to display user feedback on
	 * @param lectureId lecture's id to get the permission list of
	 * @return the map of the userid-permissions if fetching worked, null
	 *         otherwise
	 */
	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;

	}

	/**
	 * 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
	 * 
	 * *******************************************************************************/
	/**
	 * Callback interface reporting the status of the deletion back to the gui
	 */
	public interface DeleteLectureCallback {
		void deleted(Map<LectureSummary, TException> failedLectures);
	}

	/**
	 * NON-BLOCKING Deletes the lectures of the given list
	 * 
	 * @param frame to display user feedback on
	 * @param list of lectures to be deleted
	 * @param callback interface to report the lectures, which haven't been
	 *            deleted.
	 */
	public static void deleteLecture(final Frame frame, final List<LectureSummary> lectures,
			final DeleteLectureCallback callback) {
		if (lectures == null)
			return;
		String messageText = lectures.size() == 1 ? "Wollen Sie diese Veranstaltung wirklich löschen?"
				: "Wollen Sie die " + lectures.size() + " Veranstaltungen wirklich löschen?";
		if (!userConfirmed(frame, messageText))
			return;

		QuickTimer.scheduleOnce(new Task() {
			Map<LectureSummary, TException> failedLectures = new HashMap<LectureSummary, TException>();

			@Override
			public void fire() {
				for (final LectureSummary l : lectures) {
					if (l == null)
						continue;
					try {
						ThriftManager.getSatClient().deleteLecture(Session.getSatelliteToken(), l.lectureId);
					} catch (TException e) {
						failedLectures.put(l, e);
					}
				}
				Gui.asyncExec(new Runnable() {
					@Override
					public void run() {
						if (callback != null) {
							callback.deleted(failedLectures);
						}
					}
				});
			}
		});
	}
	/* *******************************************************************************
	 * 
	 * PUBLIC IMAGES
	 * 
	 * *******************************************************************************/
	/**
	 * BLOCKING Gets the image details of an image on the masterserver
	 * 
	 * @param imageVersionId
	 * @return ImageDetailsRead if fetching the details worked, null otherwise.
	 */
	public static ImageDetailsRead getPublishedImageDetails(final String imageBaseId) {
		ImageDetailsRead data = null;
		try {
			data = ThriftManager.getMasterClient().getImageDetails(Session.getMasterToken(), imageBaseId);
		} catch (TException e) {
			LOGGER.error("Could not query masterserver for ImageDetailsRead for version '" + imageBaseId + "':", e); 
		}
		return data;
	}
	/* *******************************************************************************
	 * 
	 * 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, null, null);
	}

}