/*
 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
 * This software is the proprietary information of Sun Microsystems, Inc.
 * Use is subject to license terms.
 * 
 * Copyright 2004 Sun Microsystems, Inc.  Tous droits r?serv?s.
 * Ce logiciel est propri?t? de Sun Microsystems, Inc.
 * Distribu? par des licences qui en restreignent l'utilisation.
 *
 * ident        "@(#)MfDiscoveryActualClient.java 1.19     05/04/20 SMI"
 *
 */


package com.sun.mfwk.discovery;

import java.util.*;
import java.net.*;
import java.io.*;

import com.sun.mfwk.discovery.*;
import javax.management.NotificationBroadcasterSupport;
import javax.management.Notification;

//config stuff
import com.sun.mfwk.config.*;

//security stuff
import com.sun.mfwk.security.crypto.MfCrypto;
import java.security.InvalidKeyException;

public class MfDiscoveryActualClient extends MfDiscoveryCommon {

// ----------------------------------------------------------
// variables
// ----------------------------------------------------------

  // ref to MfDiscoveryService MBean
  private MfDiscoveryService discoveryServiceRef = null;

// ----------------------------------------------------------
// Constructor
// ----------------------------------------------------------
       public MfDiscoveryActualClient(String multicastGroup, int multicastPort) throws IOException {
					
     	// ------------------------
     	// Set multicastSocket parameters
     	// ------------------------
     	super(multicastGroup,multicastPort) ;
        logger.entering("MfDiscoveryActualClient", "MfDiscoveryActualClient (after super())");        
        logger.info("group = " + multicastGroup  + ", " + "port  = " + multicastPort ); 
        logger.exiting("MfDiscoveryActualClient", "MfDiscoveryActualClient");        
      }


       public MfDiscoveryActualClient(String multicastGroup, int multicastPort, MfDiscoveryService discoveryService) throws IOException {
						
     	// ------------------------
     	// Set multicastSocket parameters
     	// ------------------------
     	super(multicastGroup,multicastPort) ;
        logger.entering("MfDiscoveryActualClient", "MfDiscoveryActualClient (after super())");        
        logger.info("group = " + multicastGroup  + ", " + "port  = " + multicastPort ); 

	// stores a reference to the calling Mbean in order to be able to send notification
	discoveryServiceRef = discoveryService;   
        logger.exiting("MfDiscoveryActualClient", "MfDiscoveryActualClient");        
      }

// ------------------------------------------
// protocol implemented by the derived object 
// ------------------------------------------
       
       final void applyProtocol() {
          logger.entering("MfDiscoveryActualClient", "applyProtocol");        
          //retrieve keyFile path & reads in key in case we receive encrypted Mcast Messages        
          MfConfig commonConfig = MfConfig.getConfig();
          String KeyFile = commonConfig.getProperty("mfwk.agent.security.disckeyfile");
          byte [] encryptUserDataKey = null;
          
          try {
              //open key file and reads in key              
              FileInputStream f = new FileInputStream(KeyFile);
              int sizef = f.available();
              encryptUserDataKey = new byte[sizef];
              f.read(encryptUserDataKey,0,sizef);
          } catch (FileNotFoundException fnfe) {
              logger.warning("KeyFile: " + KeyFile + " not found. Setting key to null. " + fnfe.getMessage());
              encryptUserDataKey = null;
          } catch (java.io.IOException ioe) {
              logger.warning("problem while reading: " + KeyFile + ". Setting key to null. " + ioe.getMessage());
              encryptUserDataKey = null;
          }
          
          // receives packets on Mcast channel and process them
          while (true) {
              try {
                  String Packet;
                  byte[] inbuf = new byte [65536]; // maximum size of a UDP packet = 64Kbytes
                  DatagramPacket packet = new DatagramPacket(inbuf, inbuf.length);
                  
                  //Blocks untill a packet is received or timeout expires (5s)
                  //if timeout expires we check in the Exception below wether
                  //the thread should be stopped or not
                  logger.finest("waiting for Packet");                
                  setSoTimeout(5000);
                  receive(packet);
                  logger.finer("Packet received");
                  
                  // Data is now in inbuf
                  int numBytesReceived = packet.getLength();
                  logger.finer("Packet received length:" + numBytesReceived);
                  
                  // process the received message
                  
                  // Transfers it in receivedData array first
                  // as the array returned by getData is the full packet (65536)
                  // even though we received only 120 bytes for instance
                  
                  byte[] receivedData = new byte [numBytesReceived];
                  System.arraycopy(packet.getData(), 0, receivedData, 0, numBytesReceived);
                  
                  //checks if it is a Java ES-MF Mcast Packet and if it is a RESP
                  //Packet, if not, discards it
                  // RESP triggers the sending of a notification to all registered clients
                  
                  if ((MfMcastMessage.isMcastMessage(receivedData)) && (MfMcastMessage.isResponsePacket(receivedData))) {
                      //creates a new Mcast object
                      MfMcastMessage receivedMsg = new MfMcastMessage(receivedData);
                      logger.finer("Packet received content:\n" + receivedMsg.printMcastMessage());
                      
                      //check if userData has been encrypted or not
                      //if it has decrypt the userData using the key
                      //before sending the notification
                      if (receivedMsg.isUserDataEncrypted()) {
                          
                          // retrieves encrypted userdata
                          byte[] decryptedUserData = receivedMsg.getUserData(encryptUserDataKey);
                          
                          //rewrites userData in clear and set encrypted flag accordingly
                          receivedMsg.setUserData(decryptedUserData);
                          receivedMsg.setUserDataSize(decryptedUserData.length);
                          receivedMsg.setUserDataEncryptedBit(false);
                      }
                      logger.finer("Packet that will be sent in notification:\n" + receivedMsg.printMcastMessage());
                      Notification respNotif = new Notification(receivedMsg.getProduct(),discoveryServiceRef,1);
                      respNotif.setUserData((com.sun.mfwk.discovery.MfMcastMessage) receivedMsg);
                      discoveryServiceRef.sendNotification(respNotif);
                      
                  }
              } catch (java.net.SocketTimeoutException ste){
                  //check if cacao module has been stopped, if so, then we close the
                  // Mcast channel and return, otherwise we continue.
                  if (super.stopListeningThread) {
                      logger.info ("stopping listening thread");
                      close();
                      return;
                  }
                  logger.finer ("stopping listening thread is false!");

              } catch (java.io.IOException e) {
                  logger.severe("Problem occured while receiving/sending/processing Mcast message, packet dropped: " + e.getMessage());
              } catch (InvalidKeyException e) {
                  logger.severe("Problem occured while decrypting userData of Mcast message, packet dropped: " + e.getMessage());
              }              
          }
       }
}

