package de.tudarmstadt.informatik.hostage.persistence; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.content.Context; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.StreamCorruptedException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Random; import de.tudarmstadt.informatik.hostage.R; import de.tudarmstadt.informatik.hostage.model.Profile; import de.tudarmstadt.informatik.hostage.ui2.activity.MainActivity; import de.tudarmstadt.informatik.hostage.ui2.adapter.ProfileManagerListAdapter; /** * The profile manager is responsible for persisting and deleting profiles * * @author Alexander Brakowski * @created 10.02.14 20:24 */ public class ProfileManager { /** * The singleton instance holder */ private static ProfileManager INSTANCE = null; /** * An list adapter, which the profile manager informs about data changes */ private ProfileManagerListAdapter mProfileListAdapter = null; /** * Holds a reference to the currently active profile */ private Profile mCurrentActivatedProfile = null; /** * Holds a reference to the random profile */ private Profile mRandomProfile = null; /** * The profiles are being serialized and persisted into this file */ private static final String PERSIST_FILENAME = "hostage_profiles.json"; /** * Hold the current profile id, it will be incremented each time a new profile is added. * The new profile will get the newly incremented value as an id. */ public int mIncrementValue = 1; /** * Holds all the available profiles. The key in the map is the ID of the profile. */ public HashMap mProfiles; /** * Since the profile manager should only have one instance in the whole app, we are using the singleton pattern. * This method creates a new instance of the profile manager, if no instance already exists, and returns it. * * @return the singleton instance */ public static ProfileManager getInstance(){ if(INSTANCE == null){ INSTANCE = new ProfileManager(); } if(INSTANCE.getNumberOfProfiles() == 0){ INSTANCE.loadData(); } return INSTANCE; } /** * A private constructor, that can/should only be called by getInstance, since we want to enforce the usage of the singleton. */ private ProfileManager(){ mProfiles = new HashMap(); } /** * Reads all data from an inputstream and appends it to a string * * @param is The input stream to read the data from * @return the whole data from the input stream as an string */ public String readAll( final InputStream is ) { if( null == is ) { return ""; } StringBuilder sb = new StringBuilder(); int rc; try { while( (rc = is.read()) >= 0 ){ sb.append( (char) rc ); } } catch (IOException e) { e.printStackTrace(); } return sb.toString(); } /** * Reads all the data of the the profiles, that were persisted and unserializes them. * * The profiles were serialized into JSON and persisted into the android private file. * See {@see ProfileManager#persistData}. */ public void loadData(){ try { FileInputStream fin = MainActivity.getContext().openFileInput(PERSIST_FILENAME); JSONArray arr = new JSONArray(readAll(fin)); fin.close(); for(int i=0; i mIncrementValue){ mIncrementValue = p.mId; } if(p.mActivated){ this.mCurrentActivatedProfile = p; } if(p.mIsRandom){ this.mRandomProfile = p; } } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (StreamCorruptedException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (JSONException e) { e.printStackTrace(); } finally { if(mProfiles.size() == 0){ this.fillWithDefaultData(); } if(this.mRandomProfile != null){ randomizeProtocols(mRandomProfile); } } } /** * All the profiles that are hold by the profile manager are being serialized into JSON and then written into an private android file. */ public void persistData(){ try { FileOutputStream fout = MainActivity.getContext().openFileOutput(PERSIST_FILENAME, Context.MODE_PRIVATE); JSONArray arr = new JSONArray(); for(Profile p: mProfiles.values()){ arr.put(p.toJSON()); } fout.write(arr.toString().getBytes()); fout.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } /** * Retrieves all the profiles as an List * @return a list that holds all the profiles */ public List getProfilesList(){ return new ArrayList(getProfilesCollection()); } /** * Retrieves all the profiles as an collection * @return a collection of all the profiles */ public Collection getProfilesCollection(){ if(mProfiles.size() == 0 || mProfiles == null){ this.loadData(); } return mProfiles.values(); } /** * Retrieves an map of all the profiles. The key in the map is the ID of an profile. * @return a map of profiles */ public Map getMapProfiles(){ return mProfiles; } /** * Activates and deactivates protocols randomly in the given profile * * @param profile the profile to randomize the protocols for */ public void randomizeProtocols(Profile profile){ LinkedList protocols = new LinkedList(Arrays.asList(MainActivity.getContext().getResources().getStringArray(R.array.protocols))); protocols.remove("GHOST"); profile.mActiveProtocols.clear(); Random rand = new Random(); int numberOfProtocolsToActivate = rand.nextInt(protocols.size()) + 1; while(numberOfProtocolsToActivate-- > 0){ int randomIndex = rand.nextInt(protocols.size()); String protocol = protocols.get(randomIndex); profile.mActiveProtocols.put(protocol, true); protocols.remove(protocol); } persistData(); } /** * Adds or updates a given profile. * * @param profile the profile to persist * @return the id the profile was assigned to */ public int persistProfile(Profile profile){ if(profile.mId == -1){ profile.mId = ++mIncrementValue; } mProfiles.put(profile.mId, profile); this.persistData(); if(this.mProfileListAdapter != null){ this.mProfileListAdapter.notifyDataSetChanged(); } return profile.mId; } /** * Retrieves the profile with the given id * * @param id the id of the profile * @return the profile */ public Profile getProfile(int id){ if(mProfiles.size() == 0){ loadData(); } if(this.mProfiles.containsKey(id)){ return this.mProfiles.get(id); } return null; } /** * Adds a profile * * @param profile the profile to add */ public void addProfile(Profile profile){ this.addProfile(profile, true); } /** * Adds a given profile to the profile manager. * * @param profile the profile to add * @param persist true, if the profile should also be persisted immediatly, * false, if the profile should just be added internally without being persisted * (Note: you can still call persistData later to persist all the profiles) */ public void addProfile(Profile profile, boolean persist){ if(profile.mId == -1){ profile.mId = ++mIncrementValue; } mProfiles.put(profile.mId, profile); if(persist){ persistData(); } if(this.mProfileListAdapter != null){ this.mProfileListAdapter.add(profile); this.mProfileListAdapter.notifyDataSetChanged(); } } /** * Deletes a given profile. These changes will be persisted immediatly. * * @param profile the profile to delete */ public void deleteProfile(Profile profile){ if(this.mProfiles.containsKey(profile.mId)){ Profile p = getProfile(profile.mId); this.mProfiles.remove(profile.mId); if(p.mActivated || this.mCurrentActivatedProfile.mId == p.mId){ mCurrentActivatedProfile = mRandomProfile; mRandomProfile.mActivated = true; } this.persistData(); //this.dbh.deleteProfile(profile.mId); if(this.mProfileListAdapter != null){ this.mProfileListAdapter.remove(profile); this.mProfileListAdapter.notifyDataSetChanged(); } } } /** * Removes all profiles. */ public void clearProfiles(){ mProfiles.clear(); persistData(); } /** * Makes a given profile active. * * @param profile the profile to activate */ public void activateProfile(Profile profile){ if(profile.equals(this.mCurrentActivatedProfile)) return; if(this.mCurrentActivatedProfile != null){ this.mCurrentActivatedProfile.mActivated = false; this.persistProfile(this.mCurrentActivatedProfile); } profile.mActivated = true; this.mCurrentActivatedProfile = profile; this.mProfiles.put(profile.mId, profile); persistData(); } /** * Checks if the "random" profile is currently active * * @return true, if active * false, otherwise */ public boolean isRandomActive(){ return this.mCurrentActivatedProfile.equals(this.mRandomProfile); } /** * Retrieves the "random" profile * * @return the "random" profile */ public Profile getRandomProfile(){ return this.mRandomProfile; } /** * Retrieves the currently active profile * * @return the active profile */ public Profile getCurrentActivatedProfile(){ return mCurrentActivatedProfile; } /** * Sets the list adapter that should also be managed by the profile manager * * @param profileListAdapter the list adapter to manage */ public void setProfileListAdapter(ProfileManagerListAdapter profileListAdapter){ this.mProfileListAdapter = profileListAdapter; } /** * Retrieves the list adapter, that is being managed by the profile manager * @return the list adapter */ public ProfileManagerListAdapter getProfileListAdapter(){ return this.mProfileListAdapter; } /** * Retrieves the number of profiles * * @return the number of profiles */ public int getNumberOfProfiles(){ return mProfiles.size(); } /** * Fills the profiles manager with default profiles */ public void fillWithDefaultData(){ Profile windowsVista = new Profile( 0, "Windows Vista", "This profile will imitate a Windows Vista machine", R.drawable.ic_profile_vista, false ); windowsVista.mActiveProtocols.put("ECHO", true); windowsVista.mActiveProtocols.put("TELNET", true); this.addProfile(windowsVista, false); Profile windowsXP = new Profile( 1, "Windows XP", "This profile will activate Windows XP typical services", R.drawable.ic_profile_xp, false ); windowsXP.mActiveProtocols.put("ECHO", true); windowsXP.mActiveProtocols.put("TELNET", true); windowsXP.mActiveProtocols.put("MySQL", true); this.addProfile(windowsXP, false); Profile serverHTTP = new Profile( 2, "Webserver HTTP", "This profile will imitate a simple webserver, which just supports the HTTP protocol", R.drawable.ic_profile_apache, false ); serverHTTP.mActiveProtocols.put("HTTP", true); this.addProfile(serverHTTP, false); Profile serverWeb = new Profile( 3, "Webserver", "This profile will imitate a simple webserver, which supports both the HTTP and HTTPS protocol", R.drawable.ic_profile_apache, false ); serverWeb.mActiveProtocols.put("HTTP", true); serverWeb.mActiveProtocols.put("HTTPS", true); this.addProfile(serverWeb, false); Profile unixMachine = new Profile( 4, "Unix", "This profile monitors unix typical services", R.drawable.ic_profile_unix, false ); unixMachine.mActiveProtocols.put("SSH", true); unixMachine.mActiveProtocols.put("ECHO", true); this.addProfile(unixMachine, false); Profile linuxMachine = new Profile( 5, "Linux", "This profile will imitate a linux machine by monitoring linux typical services", R.drawable.ic_profile_linux, false ); linuxMachine.mActiveProtocols.put("SSH", true); linuxMachine.mActiveProtocols.put("TELNET", true); linuxMachine.mActiveProtocols.put("ECHO", true); linuxMachine.mActiveProtocols.put("SMB", true); this.addProfile(linuxMachine, false); Profile voipServer = new Profile( 6, "VOIP Server", "This profile imitates a VOIP Server by monitoring the SIP service", R.drawable.ic_profile_asterisks, false ); voipServer.mActiveProtocols.put("SIP", true); this.addProfile(voipServer, false); Profile randomProfile = new Profile( 7, "Random", "This profile monitors services randomly", R.drawable.ic_launcher, false ); randomProfile.mIsRandom = true; this.addProfile(randomProfile, false); Profile paranoidProfile = new Profile( 8, "Paranoid", "This profile monitors all available services", R.drawable.ic_profile_paranoid, false ); for(String protocol: MainActivity.context.getResources().getStringArray(R.array.protocols)){ if(protocol.equals("GHOST")) continue; paranoidProfile.mActiveProtocols.put(protocol, true); } paranoidProfile.mActivated = true; this.addProfile(paranoidProfile, false); mIncrementValue = 8; this.mCurrentActivatedProfile = paranoidProfile; persistData(); } }