package org.openslx.satellitedaemon.filetransfer; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import org.apache.log4j.Logger; import org.apache.thrift.TException; import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.protocol.TProtocol; import org.apache.thrift.transport.TFramedTransport; import org.apache.thrift.transport.TSocket; import org.apache.thrift.transport.TTransport; import org.apache.thrift.transport.TTransportException; import org.openslx.encryption.AsymEncryptionHandler; import org.openslx.imagemaster.crcchecker.CrcFile; import org.openslx.imagemaster.thrift.iface.AuthenticationException; import org.openslx.imagemaster.thrift.iface.AuthorizationError; import org.openslx.imagemaster.thrift.iface.AuthorizationException; import org.openslx.imagemaster.thrift.iface.DownloadData; import org.openslx.imagemaster.thrift.iface.ImageData; import org.openslx.imagemaster.thrift.iface.ImageDataError; import org.openslx.imagemaster.thrift.iface.ImageDataException; import org.openslx.imagemaster.thrift.iface.ImageServer; import org.openslx.imagemaster.thrift.iface.ServerSessionData; import org.openslx.imagemaster.thrift.iface.UploadData; import org.openslx.imagemaster.thrift.iface.UploadError; import org.openslx.imagemaster.thrift.iface.UploadException; import org.openslx.imagemaster.thrift.iface.UserInfo; import org.openslx.satellitedaemon.Globals; import org.openslx.satellitedaemon.Identity; import org.openslx.satellitedaemon.db.DbImage; import org.openslx.satellitedaemon.db.DbImage.Status; /***********************************************************************************************/ /** * Handles the authentication with the Satellite Server and sends the * FILTRANSFERCredentials, which are necessary for the upload of the image. */ public class ThriftConnection { private static ThreadLocal client = new ThreadLocal(); private static ServerSessionData sSD = null; private static Logger log = Logger.getLogger( ThriftConnection.class ); private static CrcFile crc = null; /***********************************************************************************************/ /** * Method for getting UploadeInfos * * when the CRCsum need to be transfered. The method calls getConnection() * to check if the connection is ok and to get the ServerSessionData. If * connection is ok, it calls submitImage with CRCsum in List. * * @return returns 'null' if there is a problem. */ public static UploadData getUploadInfos( ImageData imDat, String path ) { ImageServer.Client theClient = null; String crcPath = path.concat( ".crc" ); theClient = getConnection(); if ( theClient == null ) { log.error( "Client was null!" ); return null; } List crcSums = null; try { crc = new CrcFile( crcPath ); log.info( "Made CRCFile from " + crcPath ); log.info( "crc.getCrcSums( ).size = " + crc.getCrcSums().size() ); crcSums = crc.getCrcSums(); } catch ( FileNotFoundException e ) { log.debug( "crcFile " + crcPath + " not found." ); } catch ( IOException e ) { log.error( "IOException", e ); } try { return theClient.submitImage( sSD.sessionId, imDat, crcSums ); } catch ( AuthorizationException e ) { if ( e.number == AuthorizationError.NOT_AUTHENTICATED ) { log.warn( "Suddenly nut authenticated anymore.", e ); } else if ( e.number == AuthorizationError.NO_PERMISSION ) { log.error( "No permission for uploading.", e ); // TODO: add error message into database. } else { log.warn( "Unknown authorization error.", e ); } } catch ( ImageDataException e ) { if ( e.number == ImageDataError.INVALID_DATA ) { log.warn( "Data for image not valid", e ); // TODO: add e.message into DB; } else { log.warn( "ImageDataException", e ); } } catch ( UploadException e ) { if ( e.number == UploadError.BROKEN_BLOCK ) { // A Block was transmitted 20 times unsuccessfully. // TODO: Mark the Image as corrupted. } else if ( e.number == UploadError.INVALID_CRC ) { // The CRC sum contained errors if ( crc != null && !crc.isValid() ) crc.delete(); } else { e.printStackTrace(); } } catch ( TException e ) { log.error( "TException", e ); } return null; } /***********************************************************************************************/ /** * Method for getting DonwloadInfos. Calls getConnection if client was null. * You need to specify all Blocks you want to have in an List. * * @return returns 'null' if there is a problem. */ public static DownloadData getDownloadInfos( DbImage imDat ) { ImageServer.Client theClient = null; theClient = getConnection(); if ( theClient == null ) { log.error( "Client was null!" ); return null; } try { return theClient.getImage( sSD.sessionId, imDat.guid ); } catch ( ImageDataException e ) { log.debug( "In catch - blog of thrift connection" ); if ( e.isSetNumber() && e.getNumber().equals( ImageDataError.INVALID_DATA ) ) { // Data in the db is not valid // TODO: add e.message into DB; } else if ( e.getNumber().equals( ImageDataError.UNKNOWN_IMAGE ) ) { // The image requested is not known. // TODO: change field image_syncMode, so the image is not asked // for again. // For now just changed status of image. Currently no // possibility for creating new useful state in DB. (Offenburg) log.info( "Image not known. For skipping next time, mark as only_local." ); imDat.updateStatus( Status.only_local ); // Plus add a note in some way to mark as unknown by Server } else { e.printStackTrace(); } } catch ( AuthorizationException e ) { if ( e.isSetNumber() && e.getNumber().equals( AuthorizationError.NOT_AUTHENTICATED ) ) { log.error( "Not authenticated. SessionID is not valid.", e ); // SessionID is not valid // TODO: Code for new SSID } else if ( e.getNumber().equals( AuthorizationError.NO_PERMISSION ) ) { log.error( "No permission error.", e ); // TODO: add e.message into database. } else { e.printStackTrace(); } } catch ( TException e ) { log.error( "TException", e ); } return null; } /***********************************************************************************************/ /** * This method checks if there is already a working connection. If not, * newClient() establishes one. Also it does the Authentication if not done * yet. * * @return returns the client if successful. */ private static ImageServer.Client getConnection() { ImageServer.Client theClient = client.get(); boolean isAuthenticated; if ( theClient == null ) { // There is no client instance for this thread, create a new one log.info( "No existing client for this thread. Making a new client ..." ); theClient = newClient(); isAuthenticated = false; if ( theClient == null ) { log.debug( "The client was null after newClient()" ); return null; } } else { // There is a client instance for this thread, see if it's still usable and authenticated try { isAuthenticated = theClient.isServerAuthenticated( sSD.sessionId ); log.debug( "Existing client is " + ( isAuthenticated ? "still" : "not" ) + " authenticated." ); } catch ( TException x ) { theClient = newClient(); if ( theClient == null ) { log.warn( "Masterserver connection fail" ); return null; } log.debug( "Existing client was not connected anymore." ); isAuthenticated = false; } } if ( !isAuthenticated ) { try { ByteBuffer tmpBuffer = theClient .startServerAuthentication( Identity .getOrganizationName() ); byte[] toEncrypt = new byte[ tmpBuffer.remaining() ]; tmpBuffer.get( toEncrypt ); AsymEncryptionHandler aeh = new AsymEncryptionHandler( Identity.getPrivateKey() ); byte[] byteArray = aeh.encryptMessage( toEncrypt ); sSD = theClient.serverAuthenticate( Identity.getOrganizationName(), ByteBuffer.wrap( byteArray ) ); } catch ( AuthenticationException e ) { log.error( "ThriftConnection: AuthenticationException: Server Authetication was not sucessful.", e ); return null; } catch ( TException e ) { log.error( "ThriftConnection: TException: Server Authetication was not sucessful.", e ); return null; } log.info( "is Authenticated." ); } return theClient; } /***********************************************************************************************/ /** * Method for creating a new Client for Thrift communication. * * @throws IOException */ private static ImageServer.Client newClient() { final ImageServer.Client newClient; try { TTransport transport = new TFramedTransport( new TSocket( Globals.getMasterserverHost(), Globals.getThriftPort() ) ); transport.open(); TProtocol protocol = new TBinaryProtocol( transport ); newClient = new ImageServer.Client( protocol ); log.debug( "ThriftConnection: Made a new Client" ); } catch ( TTransportException e ) { log.error( "Transport could not be opened. Couldn't create new client.", e ); return null; } client.set( newClient ); return newClient; } /** * Publish new user to master-server, which insert it to his db. * @param userInfo * @return true, if successful. */ public static boolean publishUser( UserInfo userInfo ) { ImageServer.Client theClient = null; theClient = getConnection(); if ( theClient == null ) { log.error( "Client was null!" ); return false; } try { return theClient.publishUser( sSD.sessionId, userInfo ); } catch ( AuthorizationException e ) { log.error( "AutorizationException", e ); } catch ( TException e ) { log.error( "TException", e ); } return false; } /** * Register new, by master unknown satellite - server with organizationId, * ipAddress and key - information. * @param organizationId * @param ipAddress * @param modulus * @param exponent * @return true, if successful. */ public static boolean registerSatellite( String organizationId, String ipAddress, String modulus, String exponent ) { ImageServer.Client theClient = null; theClient = getConnection(); // No check for valid connection. --> not needed, because this satellite is not known yet by master. try { return theClient.registerSatellite( organizationId, ipAddress, modulus, exponent ); } catch ( TException e ) { log.error( "TException", e ); return false; } } /** * Update in master - DB existing satellite - ipAddress. * @param ipAddress * @return true, if successful. */ public static boolean updateSatelliteAddress(String ipAddress) { ImageServer.Client theClient = null; theClient = getConnection(); if ( theClient == null) { log.error( "Client was null!" ); return false; } try { return theClient.updateSatelliteAddress( sSD.sessionId, ipAddress ); } catch ( TException e ) { log.error( "TException", e ); return false; } } }