content: /*----------------------------------------------------------------------------+
   *|                                                                           |
   *|                          Android's Hooker                                 |
   *|                                                                           |
   *+---------------------------------------------------------------------------+
   *| Copyright (C) 2011 Georges Bossert and Dimitri Kirchner                   |
   *| This program 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.                                       |
   *|                                                                           |
   *| This program 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 this program. If not, see <http://www.gnu.org/licenses/>.      |
   *+---------------------------------------------------------------------------+
   *| @url      : http://www.amossys.fr                                         |
   *| @contact  : android-hooker@amossys.fr                                     |
   *| @sponsors : Amossys, http://www.amossys.fr                                |
   *+---------------------------------------------------------------------------+
   */
   
  package com.amossys.hooker.reporting;
  
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.UnsupportedEncodingException;
  import java.net.URI;
  import java.net.URISyntaxException;
  import java.util.Locale;
  import java.util.Queue;
  
  import org.apache.http.HttpResponse;
  import org.apache.http.client.HttpClient;
  import org.apache.http.client.methods.HttpPost;
  import org.apache.http.entity.StringEntity;
  import org.apache.http.impl.client.DefaultHttpClient;
  
  import com.amossys.hooker.SubstrateMain;
  import com.amossys.hooker.common.InterceptEvent;
  
  /**
   * @author Georges Bossert
   * 
   */
  public class NetworkEventSenderThread implements Runnable {
  
    private static final int VERY_LONG_SLEEP_TIME = 2000;
    
    /**
     * Long sleep time (when connection fails with remote server)
     */
    private static final int LONG_SLEEP_TIME = 1000;
  
    /**
     * Short sleep time (when no event is available)
     */
    private static final int SHORT_SLEEP_TIME = 300;
  
    // Events to send
    private Queue<InterceptEvent> events;
  
    // The HttpClient
    private HttpClient httpClient;
    // Target URI
    private URI targetURI;
  
    // Flag to raise if you want this thread to stop
    private boolean finish;
  
    private int nbErrors = 0;
    
    private String esIndex;
    private String esDoctype;
  
    private String host;
    private int port;
  
  
    public NetworkEventSenderThread(String host, int port, String esIndex, String esDoctype, Queue<InterceptEvent> events) {
      this.events = events;
      this.host = host;
      this.port = port;
      this.esIndex = esIndex;
      this.esDoctype = esDoctype;
  
      this.httpClient = new DefaultHttpClient();
      this.finish = false;
    }
  
    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Runnable#run()
     */
    @Override
    public void run() {
  
      if (this.events == null) {
        SubstrateMain.log("EventSenderThread (" + Thread.currentThread().getPriority()
            + ") fails to start since no Queue is available. ");
        return;
      }
  
      SubstrateMain.log("EventSenderThread (" + Thread.currentThread().getPriority()
          + ") has started.");
  
      int sleepTime;
  
      while (!this.isFinish()) {
      	
        sleepTime = 0;
  
        // !!!
        // This may cause a problem when we launch from Snapshot. We have to be careful that snapshot 
        // doesn't have any IDXP
        // Retrieves the current id XP
        //this.idXP = fetchIDXPFromSdcard();
  
        InterceptEvent eventToSend = this.events.poll();
        if (eventToSend != null) {
          if (!this.send(eventToSend)) {
            this.events.add(eventToSend);
            sleepTime = LONG_SLEEP_TIME;
            nbErrors++;
          }
        } else {
          sleepTime = SHORT_SLEEP_TIME;
        }
  
        if ( (nbErrors > 10) && (nbErrors % 5 == 0) ) {
          SubstrateMain.log("[E] Too many network errors, we stop the thread...");
          sleepTime = VERY_LONG_SLEEP_TIME;
        }
  
        if (sleepTime > 0) {
          try {
            Thread.sleep(sleepTime);
          } catch (InterruptedException e) {
            SubstrateMain.log("[E] Error while sleeping the EventSenderThread", e);
          }
        }
      }
    }
  
    public void stopThread() {
      this.finish = true;
    }
  
    public boolean send(InterceptEvent event) {
  
      try {
        this.targetURI = new URI("http://"+host+":"+port+"/"+this.esIndex.toLowerCase()+"/"+this.esDoctype.toLowerCase()+"?parent=" + event.getIDXP()+"&op_type=create");
      } catch (URISyntaxException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
      
      SubstrateMain.log("Sending Event to "+this.targetURI);
  
      boolean result = false;
      StringEntity se;
      try {
        se = new StringEntity(event.toJson());
  
        HttpPost httpPost = new HttpPost(this.targetURI);
        httpPost.setEntity(se);
        httpPost.setHeader("Accept", "application/json");
        httpPost.setHeader("Content-type", "application/json");
  
        // Executes POST request to the given URL
        HttpResponse httpResponse = this.httpClient.execute(httpPost);
  
        // Receives response as inputStream
        InputStream inputStream = httpResponse.getEntity().getContent();
  
        // Converts inputStream to string
        // TODO: we should parse the elasticsearch result
        result = inputStream != null;
  
        SubstrateMain.log("Event succesfully sent !");
  
        if (inputStream != null) {
          inputStream.close();
        }
  
      } catch (UnsupportedEncodingException e) {
        SubstrateMain.log(
            "Impossible to send data to remote database due to encoding issues (" + e.getMessage()
                + ")", e);
      } catch (IOException e) {
        SubstrateMain.log("Impossible to connect to the remote database, check the connectivity ("
            + e.getMessage() + ")", e);
      }
      return result;
    }
  
    /**
     * @return the finish
     */
    public boolean isFinish() {
      return finish;
    }
  
  }
  