summaryrefslogblamecommitdiffstats
path: root/dozentenmodul/src/main/java/org/openslx/dozmod/thrift/ThriftActions.java
blob: ddac7c679a279b247738a86a130a5720c552aaff (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.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.UploadTask;
import org.openslx.dozmod.gui.GraphicalCertHandler;
import org.openslx.dozmod.gui.Gui;
import org.openslx.dozmod.gui.Gui.GuiCallable;
import org.openslx.dozmod.gui.MainWindow;
import org.openslx.dozmod.gui.helper.MessageType;
import org.openslx.dozmod.gui.helper.QFileChooser;
import org.openslx.thrifthelper.ThriftManager;
import org.openslx.util.QuickTimer;
import org.openslx.util.QuickTimer.Task;
import org.openslx.util.Util;

public class ThriftActions {

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

	/* *******************************************************************************	
	 * 
	 *                                  LOGIN
	 *
	 * Login methods
	 * 
	 ********************************************************************************/
	/**
	 * @param frame to show user feedback on
	 * @param data AuthenticationData as received from a successfull login, or null if trying to resume a saved sessions
	 * @return true if initiliazing 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
			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;

		// all good, try to get the whoami info
		try {
			whoami = client.whoami(satToken);
		} catch (TAuthorizationException e) {
			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) {
			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) {
			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) {
		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
	 * Initializes 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 be downloaded
	 * @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 long imageSize, final DownloadCallback callback) {
		QFileChooser fc = new QFileChooser(Config.getDownloadPath(), true);
		fc.setDialogTitle("Bitte wählen Sie einen Speicherort");
		int action = fc.showSaveDialog(frame);
		final File file = fc.getSelectedFile();
		if (action != JFileChooser.APPROVE_OPTION || file == null)
			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;
				}

				File df = null;
				try {
					file.getAbsoluteFile().mkdirs();
					df = new File(file.getAbsolutePath(), generateFilename(imageName, virtualizerId));
					final File ff = df;
					if (df.exists()) {
						if (!Gui.syncExec(new GuiCallable<Boolean>() {
							@Override
							public Boolean run() {
								return Gui.showMessageBox(frame,
										"Datei '" + ff.getAbsolutePath() + "' existiert bereits, wollen Sie sie überschreiben?", MessageType.QUESTION_YESNO, LOGGER, null);
							}
						})) {
							// user aborted
							return;
						} else {
							// delete it
							if (!df.delete()) {
								// TODO what?
								Gui.showMessageBox(frame, "Datei konnte nicht überschrieben werden!", MessageType.ERROR, LOGGER, null);
								return;
							}
						}
					}
					df.createNewFile();
				} catch (IOException e) {
					LOGGER.warn("Cannot prepare download destination", e);
				}
				final File destFile = df;
				final DownloadTask dlTask;
				try {
					dlTask = new DownloadTask(Session.getSatelliteAddress(), transInf.getPlainPort(), transInf
							.getToken(), destFile, imageSize, null);
				} catch (final 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;
				}
				new Thread(dlTask).start();

				Gui.asyncExec(new Runnable() {
					@Override
					public void run() {
						Config.setDownloadPath(file.getAbsolutePath());
						MainWindow.addDownload(imageName, destFile.getName(), dlTask);
						if (callback != null)
							callback.downloadInitialized(true);
					}
				});
			}
		});
	}
	/**
	 * Generates a filename based on the given imageName and with the proper extension
	 * depending on the virtualizer
	 * 
	 * @param imageName
	 * @param virtualizerId
	 * @return the generated name as String
	 */
	private static String generateFilename(String imageName, String virtualizerId) {
		String fileName = imageName.replaceAll("[^a-zA-Z0-9_\\.\\-]+", "_");
		if (fileName.length() > 50) {
			fileName = fileName.substring(0, 50);
		}
		if ("vmware".equals(virtualizerId)) {
			fileName += ".vmdk";
		} else if ("virtualbox".equals(virtualizerId)) {
			fileName += ".vdi";
		} else {
			fileName += ".img";
		}
		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;
		List<LectureSummary> lecturesToBeDeleted = new ArrayList<>();
		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)) {
					lecturesToBeDeleted.add(lecture);
				}
			}
			
		} catch (TException e) {
			ThriftError.showMessage(frame, LOGGER, e, "Fehler");
			return;
		}
		String questionText = "Dieses Image ist verknüpft zu folgende Veranstaltungen:\n";
		if (details != null && lecturesToBeDeleted != null) {
			for (LectureSummary lecture : lecturesToBeDeleted) {
				questionText += lecture.getLectureName() + "\n";
			}
			questionText += "\nUnd folgende Versionen:\n";
			for (ImageVersionDetails version : details.getVersions()) {
				if (!version.isValid)
					continue;
				questionText += version.getVersionId() + "\n";
			}
			questionText += "\nWollen Sie diese Image samt Veranstaltungen und Versionen 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 = "Diese Version ist verknüpft zu folgende Veranstaltungen:\n";
		if (lectureList != null) {
			for (LectureSummary lecture : lectureList) {
				if (lecture.getImageVersionId().equals(imageVersionId)) {
					questionText += lecture.getLectureName() + "\n";
				}
			}
		}
		questionText += "\nWollen Sie diese Version samt Veranstaltungen löschen?\n";
		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 
	 * 
	 ********************************************************************************/
	public interface DeleteLectureCallback {
		void deleted(boolean success);
	}
	public static void deleteLecture(final Frame frame, final String lectureId, final DeleteLectureCallback callback) {
		if (lectureId == null)
			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;
		}
	}
}