//========================================================================
//$Id: IJettyService.java 474 2012-01-23 03:07:14Z janb.webtide $
//Copyright 2008 Mort Bay Consulting Pty. Ltd.
//------------------------------------------------------------------------
//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//http://www.apache.org/licenses/LICENSE-2.0
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.
//========================================================================
//========================================================================
//Note:This file is modified by STC for Maxwell to start jetty on device boot
//Owner: Ravindra Singhai [STC]
//Change History
//[1] [Date: 2013-28-05] No need to show Notification on status bar when
// jetty is running
//[2] No need to Display Toasts
//[3] [Date: 2013-19-07] MAX-182: Web UI Security
// Added code to reset Web Ui password in realm.properties
//========================================================================
//========================================================================

package org.mortbay.ijetty;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.eclipse.jetty.util.IO;

import org.mortbay.ijetty.deployer.AndroidContextDeployer;
import org.mortbay.ijetty.deployer.AndroidWebAppDeployer;
import org.mortbay.ijetty.handler.DefaultHandler;
import org.mortbay.ijetty.util.AndroidInfo;
import org.mortbay.ijetty.util.IJettyToast;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.http.HttpGenerator;
import org.eclipse.jetty.http.ssl.SslContextFactory;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.bio.SocketConnector;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.security.HashLoginService;
import org.eclipse.jetty.server.ssl.SslSelectChannelConnector;
import org.eclipse.jetty.server.ssl.SslSocketConnector;
import org.eclipse.jetty.util.security.Credential;
import org.mortbay.ijetty.log.AndroidLog;


import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.res.Resources;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Message;
import android.os.PowerManager;
import android.preference.PreferenceManager;
import android.util.Log;

//STC added for MAX-182: Web UI Security
import android.content.res.AssetManager;
import java.util.Properties;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.OutputStreamWriter;

import com.maxwell.provisioning.util.ForwardPort;
import com.maxwell.provisioning.util.common;
import org.mortbay.ijetty.IIjettyService;
import org.mortbay.ijetty.IServletRequest;
import org.mortbay.ijetty.JContext;
import android.os.Environment;
import java.io.File;
import android.os.StatFs;
import com.maxwell.shared.Databaseutil;
import org.eclipse.jetty.security.Authenticator;

/**
 * IJettyService
 *
 * Android Service which runs the Jetty server
 */

public class IJettyService extends Service implements IServletRequest
{
    private static final String TAG = "Jetty";;
    private static final File __JETTY_DIR;
    private static Resources __resources;
    private static final String CONTENT_RESOLVER_ATTRIBUTE = "org.mortbay.ijetty.contentResolver";
    private static final String ANDROID_CONTEXT_ATTRIBUTE = "org.mortbay.ijetty.context";
    private static final String JETTY_STATE_CHANGE = "org.mortbay.ijetty.statechage";
    private final boolean DEBUG = Common.__DEBUG;
    public static final String[] __configurationClasses =
        new String[]
        {
            "org.mortbay.ijetty.webapp.AndroidWebInfConfiguration",
            "org.eclipse.jetty.webapp.WebXmlConfiguration",
            "org.eclipse.jetty.webapp.JettyWebXmlConfiguration",
            "org.eclipse.jetty.webapp.TagLibConfiguration"
        };

    private static boolean __isRunning;

    private NotificationManager mNM;
    private Server server;
    private ContextHandlerCollection contexts;
    private boolean _useNIO;
    private boolean _useSSL;      /*HTTPS Case*/
    private boolean _useNonSSL;   /*HTTP Case*/
    private int _port;            /*HTTP Case*/
    private int _sslPort;         /*HTTPS Case*/
    private String _consolePassword;
    private String _keymgrPassword;
    private String _keystorePassword;
    private String _truststorePassword;
    private String _keystoreFile;
    private String _truststoreFile;
    private SharedPreferences _preferences;
    private PackageInfo pi;
    private android.os.Handler _handler;
    private static final int WEBUI_DEFAULT_HTTP_PORT = 80;
    private static final int WEBUI_DEFAULT_HTTPS_PORT = 443;
    private static final int WEBUI_HTTP_PORT_FORWARD = 8080;
    private static final int WEBUI_HTTPS_PORT_FORWARD = 8443;
    private static IIjettyService.Stub _binder ;
    private PowerManager.WakeLock wakeLock;
    private Context __context;
    class jettyAPI extends IIjettyService.Stub{



          public boolean isServerRunning()throws android.os.RemoteException{
                if(DEBUG)Log.e(TAG, "isServerRunning");
                return __isRunning;
          }
          public boolean stopServer() throws android.os.RemoteException{
                if(DEBUG)Log.e(TAG, "stopServer");
                new JettyStopperThread(IJettyService.this._handler).start();
                return true;
          }
          public boolean startServer() throws android.os.RemoteException{
                if(DEBUG)Log.i(TAG, "startServer");

                return startNewJettyService(); // This calls the Context.StartService to retain the lifecycle

          }

          public boolean restartServer() throws android.os.RemoteException{
                if(DEBUG)Log.i(TAG, "restartServer");
                new JettyReStarterThread(IJettyService.this._handler).start();
                return true;

          }

          public String webuiVersion() throws android.os.RemoteException{
                if(DEBUG)Log.i(TAG, "startServer");

                return common.getWebUIVersion();

          }


    }

    static
    {
        __isRunning = false;
        __JETTY_DIR = new File(Common.__ROOT_DIR,Common.__JETTY_MAIN_DIR);
        System.setProperty("org.eclipse.jetty.xml.XmlParser.Validating","false");
        System.setProperty("org.eclipse.jetty.util.log.class","org.mortbay.ijetty.AndroidLog");
        org.eclipse.jetty.util.log.Log.setLog(new AndroidLog());
    }


    public void restartJettyOnDBvalueChange(){
            if(DEBUG)Log.i(TAG, "restartJettyOnDBvalueChange");
            new JettyReStarterThread(_handler).start();
            return ;

    }

    public boolean restartJettyOnPasswordChange(String pwd, String pwdHidden){
            if(DEBUG)Log.i(TAG, "startServer");
            try{
                EditRealm(pwd, pwdHidden);
            }catch(IOException e){
                if(DEBUG)Log.e(TAG, "Error: startServer: " + e);
                return false;
            }
            new JettyReStarterThread(_handler).start();
            return true;

    }


  /***
     * JettyStarterThread
     *
     *
     ***/
    public class JettyStarterThread extends Thread
    {
        android.os.Handler _handler;

        public JettyStarterThread(android.os.Handler handler)
        {
            _handler = handler;
        }
        public void run ()
        {
            try
            {

                /*JettyForwardPort*/
                ForwardPort.handleJettyPortForward(__context, ForwardPort.PORT_FORWARD_RULES.AT_START);
                startJetty();
                sendMessage(Common.__STARTED);

                if(DEBUG)Log.i(TAG, "Jetty started");
            }
            catch (Exception e)
            {
                sendMessage(Common.__ERROR);
                /*JettyForwardPort*/
                ForwardPort.handleJettyPortForward(__context, ForwardPort.PORT_FORWARD_RULES.AT_STOP);
                if(DEBUG)Log.e(TAG, "Error starting jetty", e);

            }
        }

        public void sendMessage(int state)
        {
            Message msg = _handler.obtainMessage();
            Bundle b = new Bundle();
            b.putInt("state", state);
            msg.setData(b);
            _handler.sendMessage(msg);
        }
    }


   /***
      * JettyStopperThread
      *
      *
      ***/
    public class JettyStopperThread extends Thread
    {
        android.os.Handler _handler;

        public JettyStopperThread(android.os.Handler handler)
        {
            _handler = handler;
        }

        public void run ()
        {
            try
            {
                stopJetty();
                if(DEBUG)Log.i(TAG, "Jetty stopped");

            }
            catch (Exception e)
            {

                if(DEBUG)Log.e(TAG, "Error stopping jetty", e);
            }
            finally{

                /*JettyForwardPort*/
                ForwardPort.handleJettyPortForward(__context, ForwardPort.PORT_FORWARD_RULES.AT_STOP);
                sendMessage(Common.__STOPPED);
            }
        }

        public void sendMessage(int state)
        {
            Message msg = _handler.obtainMessage();
            Bundle b = new Bundle();
            b.putInt("state", state);
            msg.setData(b);
            _handler.sendMessage(msg);
        }
    }

   /***
      * JettyRestarterThread
      *
      *
      ***/
    public class JettyReStarterThread extends Thread
       {
           android.os.Handler _handler;

           public JettyReStarterThread(android.os.Handler handler)
           {
               _handler = handler;
           }
           public void run ()
           {
               try
               {
                   stopJetty();
                   startNewJettyService();
                   if(DEBUG)Log.i(TAG, "Jetty restarted");
               }
               catch (Exception e)
               {
                   sendMessage(Common.__ERROR);
                   /*JettyForwardPort*/
                   ForwardPort.handleJettyPortForward(__context, ForwardPort.PORT_FORWARD_RULES.AT_STOP);
                   if(DEBUG)Log.e(TAG, "Error restarting jetty", e);

               }
           }

           public void sendMessage(int state)
           {
               Message msg = _handler.obtainMessage();
               Bundle b = new Bundle();
               b.putInt("state", state);
               msg.setData(b);
               _handler.sendMessage(msg);
           }
       }

  /**
     *
     **/
   private boolean startNewJettyService()
   {
        Intent serviceIntent = new Intent(__context,IJettyService.class);
        return Common.startJetty(__context, serviceIntent);

   }


  /**
     * Hack to get around bug in ResourceBundles
     *
     * @param id
     * @return
     */
    public static InputStream getStreamToRawResource(int id)
    {
        if (__resources != null)
            return __resources.openRawResource(id);
        else
            return null;
    }



    public static boolean isRunning ()
    {
        return __isRunning;
    }

  /**
     *
     **/
    public IJettyService()
    {
        super();

        _binder =  new jettyAPI();
        _handler = new android.os.Handler ()
        {
            public void handleMessage(Message msg) {
                if (null == __context)
                    __context = getApplicationContext();
                switch (msg.getData().getInt("state"))
                {
                    case Common.__STARTED:
                        {

                            IJettyToast.showServiceToast(IJettyService.this,R.string.jetty_started);
                            common.setJettyState(__context, common.WEBUI_STARTED);
                            sendBroadCastToSettingsApp(common.WEBUI_STARTED);
                            break;
                        }
                    case Common.__STOPPED:
                        {

                            common.setJettyState(__context, common.WEBUI_STOPPED);
                            sendBroadCastToSettingsApp(common.WEBUI_STOPPED);
                            break;
                        }

                    case Common.__STARTING:
                        {
                            common.setJettyState(__context, common.WEBUI_STARTING);
                            sendBroadCastToSettingsApp(common.WEBUI_STARTING);
                            break;
                        }
                    case Common.__ERROR:
                        {
                            common.setJettyState(__context, common.WEBUI_STOPPED);
                            sendBroadCastToSettingsApp(common.WEBUI_ERROR);
                            break;
                        }
                    default:
                        if(DEBUG)Log.i(TAG, "Not Handled");
                    }


            }

        };
    }

    private void sendMessage(int state)
    {
        Message msg = _handler.obtainMessage();
        Bundle b = new Bundle();
        b.putInt("state", state);
        msg.setData(b);
        _handler.sendMessage(msg);
    }


    @Override
        public IBinder onBind(Intent intent) {
            if(DEBUG)Log.i(TAG, "onBind");
            return _binder;
        }

    /**
     * Android Service create
     * @see android.app.Service#onCreate()
     */
    public void onCreate()
    {
        __resources = getResources();
        __context = getApplicationContext();

        try
        {
            pi = getPackageManager().getPackageInfo(getPackageName(), 0);
        }
        catch (Exception e)
        {
            if(DEBUG)Log.e(TAG, "Unable to determine running jetty version");
        }
    }


   /**
     * Android Service onStartCommand
     * @see android.app.Service#onStartCommand()
     */
    @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            try
            {
                HandleCommand(intent, flags, startId);

            }
            catch (Exception e)
            {
                if(DEBUG)Log.e(TAG, "Error starting jetty", e);
            }

            // To restart for any crash or system initiated kill
            return START_STICKY;
        }


   /**
     * Android Service HandleCommand
     * @see HandleCommand(android.content.Intent, int, int)
     */
    public synchronized void HandleCommand(Intent intent, int flags, int startId)
    {
        if ( intent ==  null || intent.getStringExtra(JContext.__INTENT_TYPE).matches(JContext.__INTENT_TYPE_START_JETTY_VALUE))
        {
            if(DEBUG)Log.i(TAG, "HandleCommand Intent to Start Jetty server");
            if (server != null)
            {
                if(DEBUG)Log.i(TAG, "jetty server already running");
                return;
            }

            try
            {
                _preferences = PreferenceManager.getDefaultSharedPreferences(this);
                this.sendMessage(Common.__STARTING);
                File isAvailable = new File(getFilesDir()+"/"+Common.__REALM_COPY_CONFIRMATION);
                if(!isAvailable.exists()){
                    Log.i(TAG,"File is not there");
                    performSetupEtcDir();
                }

                File isAvailablehidden = new File(getFilesDir()+"/"+Common.__MAXWELLHIDDEN_REALM_PROPERTIES   );
                if(!isAvailablehidden.exists()){
                    setUpMaxwellHiddenProperties();
                }
                //Get a wake lock to stop the cpu going to sleep
                PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
                //STC modified from SCREEN_DIM_WAKE_LOCK to PARTIAL_WAKE_LOCK to unblock screensaver feature
                wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "IJetty");
                wakeLock.acquire();

                new JettyStarterThread(_handler).start();
            }
            catch (Exception e)
            {
                Log.e(TAG, "Error starting jetty", e);
                this.sendMessage(Common.__ERROR);
            }
        }else if (intent.getStringExtra(JContext.__INTENT_TYPE).matches(JContext.__INTENT_TYPE_RESTART_JETTY_VALUE)){

            if (server == null)
            {
                if(DEBUG)Log.i(TAG, "jetty server not running currently. Restart ignored.");
                return;
            }
            try{
                  new JettyReStarterThread(_handler).start();
            }catch (Exception e)
            {
                Log.e(TAG, "Error restarting jetty", e);
                this.sendMessage(Common.__ERROR);
            }
        }

        else
        {
            if(DEBUG)Log.e(TAG, " No intent/event matching ");
        }
        if(DEBUG)Log.i(TAG, "HandleCommand >>");
    }


   private void performSetupEtcDir()
    {
        try
        {
            FileOutputStream fos = openFileOutput(Common.__REALM_PROPERTIES,MODE_PRIVATE);
            File dataEtcDir = getFilesDir();
            File realm = null;
            if(dataEtcDir != null){
                realm = new File(dataEtcDir +"/"+Common.__REALM_PROPERTIES);
            }


            //get the realm.properties file out resources
            InputStream is = __context.getResources().openRawResource(R.raw.realm_properties);
            OutputStream os = new FileOutputStream(realm);
            IO.copy(is,os);
            FileOutputStream confirmationFile = openFileOutput(Common.__REALM_COPY_CONFIRMATION,MODE_PRIVATE);
            if(DEBUG)Log.i(TAG,"Loaded realm.properties");
        } catch (Exception e){
            Log.e(TAG,"Error loading realm.properties"+e);
        }
        if(DEBUG)Log.i(TAG,"performSetupEtcDir >>" );

    }

    /*
    * Copy maxwellhidden realm properties file
    */

    private void setUpMaxwellHiddenProperties()
    {
        try
        {
            FileOutputStream fos = openFileOutput(Common.__MAXWELLHIDDEN_REALM_PROPERTIES,MODE_PRIVATE);
            File dataEtcDir = getFilesDir();
            File realm = null;
            if(dataEtcDir != null){
                realm = new File(dataEtcDir +"/"+Common.__MAXWELLHIDDEN_REALM_PROPERTIES);
            }


            //get the maxwellhidden.realm.properties file out resources
            InputStream is = __context.getResources().openRawResource(R.raw.maxwellhidden_realm_properties);
            OutputStream os = new FileOutputStream(realm);
            IO.copy(is,os);

        } catch (Exception e){
            Log.e(TAG,"Error loading realm.properties"+e);
        }
        if(DEBUG)Log.i(TAG,"setUpMaxwellHiddenProperties >>" );

    }

    /**
     * Android Service destroy
     * @see android.app.Service#onDestroy()
     */
    public void onDestroy()
    {
        if(DEBUG)Log.i(TAG, "onDestroy");
        try
        {
            if (wakeLock != null)
            {
                wakeLock.release();
                wakeLock = null;
            }

            if (server != null)
            {
                new JettyStopperThread(_handler).start();

            }else
            {
                if(DEBUG)Log.i(TAG, "Jetty not running");

            }
        }
        catch (Exception e)
        {
            if(DEBUG)Log.e(TAG, "Error stopping jetty", e);
        IJettyToast.showServiceToast(IJettyService.this,R.string.jetty_not_stopped);
        }
    }





    public void onLowMemory()
    {
        super.onLowMemory();
    }



    /**
     * Get a reference to the Jetty Server instance
     * @return
     */
    public Server getServer()
    {
        return server;
    }



    protected Server newServer()
    {
        return new Server();
    }

    protected ContextHandlerCollection newContexts()
    {
        return new ContextHandlerCollection();
    }

    private void configureHttps() {
        SslContextFactory sslContextFactory = new SslContextFactory();
        sslContextFactory.setKeyStore(_keystoreFile);
        sslContextFactory.setTrustStore(_truststoreFile);
        sslContextFactory.setKeyStorePassword(_keystorePassword);
        sslContextFactory.setKeyManagerPassword(_keymgrPassword);
        sslContextFactory.setKeyStoreType("bks");
        sslContextFactory.setTrustStorePassword(_truststorePassword);
        sslContextFactory.setTrustStoreType("bks");

        //TODO SslSelectChannelConnector does not work on android 1.6, but does work on android 2.2
        if (_useNIO)
        {
            SslSelectChannelConnector sslConnector = new SslSelectChannelConnector(sslContextFactory);
            sslConnector.setPort(_sslPort);
            server.addConnector(sslConnector);
            if(DEBUG)Log.i(TAG, "Configured "+sslConnector.getClass().getName()+" on port "+_sslPort);
        }
        else
        {
            SslSocketConnector sslConnector = new SslSocketConnector(sslContextFactory);
            sslConnector.setPort(_sslPort);
            server.addConnector(sslConnector);
            if(DEBUG)Log.i(TAG, "Configured "+sslConnector.getClass().getName()+" on port "+_sslPort);
        }
    }

    private void configureHttp() {
        if (_useNIO)
        {
            SelectChannelConnector nioConnector = new SelectChannelConnector();
            nioConnector.setUseDirectBuffers(false);
            nioConnector.setPort(_port);
            server.addConnector(nioConnector);
            if(DEBUG)Log.i(TAG, "Configured "+SelectChannelConnector.class.getName()+" on port "+_port);
        }
        else
        {
            SocketConnector bioConnector = new SocketConnector();
            bioConnector.setPort(_port);
            server.addConnector(bioConnector);
            if(DEBUG)Log.i(TAG, "Configured "+SocketConnector.class.getName()+" on port "+_port);
        }
    }

    protected void configureConnectors()
    {
        if (server != null)
        {
            String nioKey = getText(R.string.pref_nio_key).toString();
            String nioEnabledDefault = getText(R.string.pref_nio_value).toString();
            String sslEnabledDefault = getText(R.string.pref_ssl_value).toString();
            String sslKey = getText(R.string.pref_ssl_key).toString();
            String portKey = getText(R.string.pref_port_key).toString();
            String portDefault = getText(R.string.pref_port_value).toString();
            String sslPortKey = getText(R.string.pref_ssl_port_key).toString();
            String sslPortDefault = getText(R.string.pref_ssl_port_value).toString();

            _useNIO = _preferences.getBoolean(nioKey, Boolean.valueOf(nioEnabledDefault));
            getHttpConnection(sslKey, sslEnabledDefault);

            /* HTTP case*/
            if(_useNonSSL) {
               getHttpPort(portKey, portDefault);
            }

            /*HTTPS case*/
            if (_useSSL){
               getHttpsPort(sslPortKey, sslPortDefault);
               String defaultValue = getText(R.string.pref_keystore_pwd_value).toString();
               String key = getText(R.string.pref_keystore_pwd_key).toString();
               _keystorePassword = _preferences.getString(key, defaultValue);

               defaultValue = getText(R.string.pref_keymgr_pwd_value).toString();
               key = getText(R.string.pref_keymgr_pwd_key).toString();
               _keymgrPassword = _preferences.getString(key, defaultValue);

               defaultValue = getText(R.string.pref_truststore_pwd_value).toString();
               key = getText(R.string.pref_truststore_pwd_key).toString();
               _truststorePassword = _preferences.getString(key, defaultValue);

               defaultValue = getText(R.string.pref_keystore_file_value).toString();
               key = getText(R.string.pref_keystore_file_key).toString();
               _keystoreFile = _preferences.getString(key, defaultValue);

               defaultValue = getText(R.string.pref_truststore_file_value).toString();
               key = getText(R.string.pref_truststore_file_key).toString();
                    _truststoreFile = _preferences.getString(key, defaultValue);
            }
            if(DEBUG)Log.d("Jetty", "preff port = "+_port);
            if(DEBUG)Log.d("Jetty", "preff use nio = "+_useNIO);
            if(DEBUG)Log.d("Jetty", "preff use ssl = "+_useSSL);
            if(DEBUG)Log.d("Jetty", "preff ssl port = "+_sslPort);
            if (_useSSL == false && _useNonSSL == true) /*HTTP Case*/
            {
                configureHttp();
            } else if (_useSSL == true && _useNonSSL == false) /*HTTPS Case*/
            {
                configureHttps();
            } else if (_useSSL == true && _useNonSSL == true) /*HTTP+HTTPS Case*/
            {
                configureHttp();
                configureHttps();
            }
        }
    }

    protected void configureHandlers()
    {
        if (server != null)
        {
            HandlerCollection handlers = new HandlerCollection();
            contexts = new ContextHandlerCollection();
            handlers.setHandlers(new Handler[] {contexts, new DefaultHandler()});
            server.setHandler(handlers);
        }
    }

    protected void configureDeployers () throws Exception
    {
        AndroidWebAppDeployer staticDeployer =  new AndroidWebAppDeployer();
        AndroidContextDeployer contextDeployer = new AndroidContextDeployer();

        File jettyDir = __JETTY_DIR;

        if (jettyDir.exists())
        {
            // If the webapps dir exists, start the static webapp deployer
            if (new File(jettyDir, Common.__WEBAPP_DIR).exists())
            {
                staticDeployer.setWebAppDir(__JETTY_DIR+"/"+Common.__WEBAPP_DIR);
                staticDeployer.setDefaultsDescriptor(__JETTY_DIR+"/"+Common.__ETC_DIR+"/webdefault.xml");
                staticDeployer.setContexts(contexts);
                staticDeployer.setAttribute(CONTENT_RESOLVER_ATTRIBUTE, getContentResolver());
                staticDeployer.setAttribute(ANDROID_CONTEXT_ATTRIBUTE, (Context) IJettyService.this);
                staticDeployer.setConfigurationClasses(__configurationClasses);
                staticDeployer.setAllowDuplicates(false);
            }

            // Use a ContextDeploy so we can hot-deploy webapps and config at startup.
            if (new File(jettyDir,Common.__CONTEXTS_DIR).exists())
            {
                contextDeployer.setScanInterval(10); // Don't eat the battery
                contextDeployer.setConfigurationDir(__JETTY_DIR+"/"+Common.__CONTEXTS_DIR);
                contextDeployer.setAttribute(CONTENT_RESOLVER_ATTRIBUTE, getContentResolver());
                contextDeployer.setAttribute(ANDROID_CONTEXT_ATTRIBUTE, (Context) IJettyService.this);
                contextDeployer.setContexts(contexts);
            }

            if (server != null)
            {
                if(DEBUG)Log.i(TAG, "Adding context deployer: ");
                server.addBean(contextDeployer);
                if(DEBUG)Log.i(TAG, "Adding webapp deployer: ");
                server.addBean(staticDeployer);
            }
        }
        else
        {
            if(DEBUG)Log.w(TAG, "Not loading any webapps - none on SD card.");
        }
    }

    public synchronized void configureRealm () throws IOException
    {

        String realmPath = new String(getFilesDir()+"/"+Common.__REALM_PROPERTIES);
        File realmProps = new File(realmPath);

        String realmPathMaxwellHidden = new String(getFilesDir()+"/"+Common.__MAXWELLHIDDEN_REALM_PROPERTIES);
        File realmPropsMaxwellHidden = new File(realmPathMaxwellHidden);
        HashLoginService realmMaxwellhidden = new HashLoginService("realm", realmPathMaxwellHidden);
        server.addBean(realmMaxwellhidden);
        if (realmProps.exists())
        {
            String pwdDefault = getText(R.string.pref_console_pwd_value).toString();
            String pwdKey = getText(R.string.pref_console_pwd_key).toString();

            _consolePassword = _preferences.getString(pwdKey, pwdDefault);
            HashLoginService realm = new HashLoginService("Console", realmPath);
            realm.setRefreshInterval(0);
            if (_consolePassword != null)
            {
                if(DEBUG)Log.i("IJettyService", "realm.putUser");
                realm.putUser("admin", Credential.getCredential(_consolePassword), new String[]{"admin"}); //set the admin password for console webapp
            }
            server.addBean(realm);
        }
    }

    public synchronized void EditRealm (String newPassword, String pwdHidden) throws IOException
    {
        if (null == newPassword || "" == newPassword){
           if(DEBUG)Log.i(TAG, "EditRealm Password is null");
           throw new IOException();
        }
        if (null == pwdHidden || "" == pwdHidden){
           if(DEBUG)Log.i(TAG, "EditRealm hidden Password is null");
           throw new IOException();
        }
        
        String realmPath = new String(getFilesDir()+"/"+Common.__REALM_PROPERTIES);
        String realmPathHidden = new String(getFilesDir()+"/"+Common.__MAXWELLHIDDEN_REALM_PROPERTIES);

        File realmPropsHidden = new File(realmPathHidden);
        if (realmPropsHidden.exists())
        {

            try
            {
                Runtime.getRuntime().exec( "chmod 666 " + realmPropsHidden);
            }
            catch (Exception e)
            {
                if(DEBUG)Log.e("Exception","failed due to:"+e);
            }

            HashLoginService realmHidden = new HashLoginService("realm", realmPathHidden);
            realmHidden.setRefreshInterval(2);
            realmHidden.putUser("admin", Credential.getCredential(pwdHidden), new String[]{"admin"}); //set the admin password for console webapp
            if(DEBUG)Log.i("IJettyService", "EditRealm  hidden realm putUSer");
            server.addBean(realmHidden);
        }

        File realmProps = new File(realmPath);
        if (realmProps.exists())
        {

            try
            {
                Runtime.getRuntime().exec( "chmod 666 " + realmProps);
            }
            catch (Exception e)
            {
                if(DEBUG)Log.e("Exception","failed due to:"+e);
            }

            HashLoginService realm = new HashLoginService("Console", realmPath);
            realm.setRefreshInterval(2);
            realm.putUser("admin", Credential.getCredential(newPassword), new String[]{"admin"}); //set the admin password for console webapp
            if(DEBUG)Log.i("IJettyService", "EditRealm realm putUSer");
            server.addBean(realm);
        }


        if (false == writeToFile(realmPath, newPassword))
            throw new IOException("Cant write new password to file ");
        if (false == writeToFile(realmPathHidden, pwdHidden))
            throw new IOException("Cant write new hidden password to file ");
    }

    private boolean writeToFile(String filename, String newPassword) {

        if(DEBUG)Log.i(TAG,"writeToFile filename :" + filename);
        BufferedWriter bufferedWriter = null;
        boolean isWritten = true;

        try {

            if(DEBUG)Log.i(TAG,"Construct the BufferedWriter object");
            //Construct the BufferedWriter object
            bufferedWriter = new BufferedWriter(new FileWriter(filename));

            if(DEBUG)Log.i(TAG,"Start writing to the output stream");
            //Start writing to the output stream
            bufferedWriter.newLine();
            if(DEBUG)Log.i(TAG,"write newline");
            bufferedWriter.write("admin: " + newPassword +",admin");
            if(DEBUG)Log.i(TAG,"newline");
            bufferedWriter.newLine();
            bufferedWriter.write("S30853-S4001-R101: " + newPassword +",admin");
            if(DEBUG)Log.i(TAG,"newline");
            bufferedWriter.newLine();

        } catch (FileNotFoundException ex) {
            ex.printStackTrace();
            isWritten = false;
        } catch (IOException ex) {
            ex.printStackTrace();
            isWritten = false;
        } finally {
            //Close the BufferedWriter
            try {
                if (bufferedWriter != null) {
                    if(DEBUG)Log.i(TAG,"Close the BufferedWriter");
                    bufferedWriter.flush();
                    bufferedWriter.close();
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        if(DEBUG)Log.i(TAG,"writeToFile >>");
        return isWritten;

    }

    protected synchronized void startJetty() throws Exception
    {

        if (null == server){
            //Set jetty.home
            System.setProperty ("jetty.home", __JETTY_DIR.getAbsolutePath());

        //ipv6 workaround for froyo
        System.setProperty("java.net.preferIPv6Addresses", "false");

        // If device memory is less than 2 MB, temporary file will be deleted in /data folder to create space to launch WebUI
        // If temporary file is not present for deletion, 503 error will be shown in WebUI
        try {
              StatFs stats = new StatFs(Environment.getExternalStorageDirectory().getPath());
              long bytesAvailable = (long)stats.getBlockSize() * (long)stats.getAvailableBlocks();
              long megAvailable = bytesAvailable / (1024 * 1024);
              if(DEBUG)Log.d(TAG,"Available MB : "+megAvailable);

              if(megAvailable <=2){
                // delete some memory and start jetty
                File toDelete = new File("/data/jetty/lowmemory.recovery");
                if(DEBUG)Log.i(TAG,"toDelete.exists() : " + toDelete.exists());
                if(toDelete.exists()){
                    toDelete.delete();
                }

               }
        }catch (Exception e) {
                Log.i(TAG,"File deletion Exception");
        }
        server = newServer();

        configureConnectors();
        configureHandlers();
        configureDeployers();
        configureRealm ();

        server.start();

        __isRunning = true;

        //TODO
        // Less than ideal solution to the problem that dalvik doesn't know about manifests of jars.
        // A as the version field is private to Server, its difficult
        //if not impossible to set it any other way. Note this means that ContextHandler.SContext.getServerInfo()
        //will still return 0.0.
        HttpGenerator.setServerVersion("i-jetty "+pi.versionName);
        }
    }

    protected synchronized void stopJetty() throws Exception
    {
        try
        {
            if(DEBUG)Log.i(TAG, "Jetty stopping");

            server.stop();

        }
        finally
        {
            server = null;
            __resources = null;
            __isRunning = false;
            stopSelf();
            if(DEBUG)Log.i(TAG,"Finally stopped");
        }
    }

    private void getHttpPort(String portKey, String portDefault) {
        int port = Databaseutil.getHTTPPortFrmDB(IJettyService.this);
        if(-1 == port) {
            if(DEBUG)Log.d(TAG, "http port from xml rersource");
            _port = Integer.parseInt(_preferences.getString(portKey, portDefault));
        } else {
            if(DEBUG)Log.d(TAG, "http port from prov db");
            if(WEBUI_DEFAULT_HTTP_PORT == port) { /*Port forward*/
                port = WEBUI_HTTP_PORT_FORWARD;
            }
            _port = port;
        }
    }

    private void getHttpsPort(String sslPortKey, String sslPortDefault) {
        int ports = Databaseutil.getHTTPSPortFrmDB(IJettyService.this);
        if(-1 == ports) {
            if(DEBUG)Log.d(TAG, "https port from xml resource");
            _sslPort = Integer.parseInt(_preferences.getString(sslPortKey, sslPortDefault));
        } else {
            if(DEBUG)Log.d(TAG, "https port from prov db");
            if(WEBUI_DEFAULT_HTTPS_PORT == ports) { /*Port forward*/
                ports = WEBUI_HTTPS_PORT_FORWARD;
            }
            _sslPort = ports;
        }
    }

    private void getHttpConnection(String sslKey, String sslEnabledDefault) {
        int httpConType = Databaseutil.getHTTPConnectionTypeFrmDB(IJettyService.this);
        if(-1 == httpConType) {
            if(DEBUG)Log.d(TAG, "http connection from xml resource");
            _useSSL = _preferences.getBoolean(sslKey, Boolean.valueOf(sslEnabledDefault));
            _useNonSSL = true;
        } else if (0 == httpConType) { /*HTTP Case*/
            if(DEBUG)Log.d(TAG, "http connection from prov db");
            _useSSL = false;
            _useNonSSL = true;
        } else if (1 == httpConType) { /*HTTPS Case*/
            if(DEBUG)Log.d(TAG, "https connection from prov db");
            _useSSL = true;
            _useNonSSL = false;
        } else if (2 == httpConType) { /*HTTP+HTTPS Case*/
            if(DEBUG)Log.d(TAG, "http+https connection from prov db");
            _useSSL = true;
            _useNonSSL = true;
        }
    }

    private synchronized void sendBroadCastToSettingsApp(int state) {
         if(null != __context) {
             if(DEBUG)Log.d(TAG, "sendBroadCastToSettingsApp<<" + state);
             Intent intent = new Intent();
             intent.setAction(JETTY_STATE_CHANGE);
             intent.putExtra("jettystate", state);
             __context.sendStickyBroadcast(intent);
         }
     }
}
