/**
 * SLPv2 message parser (protocol stack)
 * Use separate get-methods to obtain each field after parsing 
 *
 */

package eu.artemis.shield.discovery.slpdaemon.impl;

import java.io.*;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.Signature;
import java.security.interfaces.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;
import java.net.InetAddress;
import java.security.InvalidKeyException;
import java.security.SignatureException;

//import eu.artemis.shield.discovery.pdm_slp.sdc.slpapi.ServiceLocationAttribute;

public class slpMsgParser {

  
  private static boolean WRITE_LOG_ON_FILE = false;
  
    da daf;
    Database database;
    int    version, func_id, packet_len, slp_flag, ext_offset, xid, 
           ecode, lifetime,attrlength;
    String ltag, scope, attr, pred, spi, tag, type, prlist, url,_attr1;
    String attrList, typeList, urlList;
    int    daBootTS;              // DA boot timestamp
    int    meshFwdID;             // in MeshFwd ext.
    long   versionTS;             // version TS from the SA for the update
    long   arrivalTS;             // arrival TS at the DA for the update        
    String acceptDA;              // the accept DA for the update
    long   acceptTS;              // the accept TS for the update
    Vector atsList, adaList;      // used in DataRqst
    Vector ssExtList;             // used in SrvRqst
    boolean hasSelectExt;         // whether has Select extensions
    Vector urlVector, attrVector; // used in AttrList extension
    boolean hasAttrListExt;       // whether has AttrList extensions
    int    totalMatch;            // number of matches before selection
    Vector matchedEntry;          // matched entries in database
    int    etrpType;
    PrintStream ps;
    long after, before;
    InetAddress daTCPServeria;
    RSAPublicKey _publicKey;
    
    
    /*
    slpMsgParser(da daf) throws Exception {  // for DA
      init(daf);
    }
    */

    slpMsgParser(da daf, InetAddress iathrows Exception {  // for DA
      init(daf);
      daTCPServeria = ia;
      url = ia.getHostAddress();
      da.appendDebug("slpMsgParser::slpMsgParser->ia.hostAdrr ="+ ia.getHostAddress());
    }
    
    public InetAddress getTCPServerInetAddress()
    {
      return daTCPServeria;
    }
    
    private void init(da dafthrows Exception {  // for DA
        this.daf = daf;
        database = daf.getDatabase();
        atsList = new Vector(10);
        adaList = new Vector(10);
        ssExtList = new Vector(10);
        urlVector  = new Vector(10);
        attrVector = new Vector(10);
        if (WRITE_LOG_ON_FILE) {
          File f = new File(".","logTest.txt");
          FileOutputStream fos = new FileOutputStream(f);
      ps = new PrintStream(fos);
        }
    }
    
    
    slpMsgParser() {        // for UA/SA
        ssExtList = new Vector(10);
        urlVector  = new Vector(10);
        attrVector = new Vector(10);
    }

/**
 * SLP common message header, not including language tag
 *+--------------+-----------------+----------------------------------+
 *|  Version     |   Function-ID   |            Length                |
 *+--------------+-+-+-+-----------+---------------+------------------+
 *| Length cont. |O|F|R|       Reserved            | Next Ext. Offset |
 *+--------------+-+-+-+-----------+---------------+------------------+
 *|     Next Ext. Offset  Cont.    |              XID                 |
 *+--------------------------------+----------------------------------+
 */
    public void Header(byte[] buf) {  // parse header
        int[] ia = };
        version    = Util.parseInt(buf, ia, 1);         // index=0 
        func_id    = Util.parseInt(buf, ia, 1);         // index=1
        packet_len = Util.parseInt(buf, ia, 3);         // index=2
        slp_flag   = Util.parseInt(buf, ia, 2);         // index=5
        ext_offset = Util.parseInt(buf, ia, 3);         // index=7
        xid        = Util.parseInt(buf, ia, 2);         // index=10
    }

    public void LangTag(byte[] buf, int ia[]) {  // parse language tag
        ltag = Util.parseString(buf, ia);       
    }

    public int getPacketLen() {
        return packet_len;
    }

    public int getFuncID() {
        return func_id;
    }

    public int getFlag() {
        return slp_flag;
    }

    public int getXID() {
        return xid;
    }

    public String getLtag() {
        return ltag;
    }

    public String getURL() {
        return url;
    }

    public String getScope() {
        return scope;
    }

    public String getAttr() {
        return attr;
    }

    public String getAttrList() {
        return attrList;
    }

    public String getTypeList() {
        return typeList;
    }

    public String getUrlList() {
        return urlList;
    }

    public int getEcode() {
        return ecode;
    }

    public int getDaBootTS() {
        return daBootTS;
    }

    public int getMeshFwdID() {
        return meshFwdID;
    }

    public long getVersionTS() {
        return versionTS;
    }

    public String getAcceptDA() {
        return acceptDA;
    }

    public long getAcceptTS() {
        return acceptTS;
    }

    public Vector getAtsList() {          // be careful Object CANNOT be
        return (VectoratsList.clone();  // shared between two threads
    }

    public Vector getAdaList() {          // CANNOT be shared!
        return (VectoradaList.clone();
    }

    public int findTotalMatch() {
        if (ssExtList.size() 0) {
            SelectSortExt ss = (SelectSortExtssExtList.elementAt(0);
            if (ss.getID() == Const.SelectExtreturn ss.getBound();
        }
        return 0;
    }

    public int getTotalMatch() {
        return totalMatch;
    }

    public int getEtrpType() {
        return etrpType;
    }

    public boolean hasSelectExt() {
        return hasSelectExt;
    }

    public boolean hasAttrListExt() {
        return hasAttrListExt;
    }

    public Vector getMatchedEntry() {
        return matchedEntry;
    }

    public Vector getUrlVector() {
        return urlVector;
    }

    public Vector getAttrVector() {
        return attrVector;
    }

/**
 * parse URL entry, to get the lifetime & URL string
 *+---------------+---------------------------------+----------------+
 *|    Reserved   |          Lifetime               |   URL length   |
 *+---------------+---------------------------------+----------------+
 *| URL len cont. |          URL (variable length)                   \
 *+---------------+--------------------------------------------------+
 *| # of URL auths|          Auth. blocks (if any)                   \
 *+------------------------------------------------------------------+
 */
    public String parseURL(byte[] buf, int[] ia) {
      da.appendDebug("slpMsgParser::parseURL");
      
      ia[0+= 1;                             // skip one byte for reserved
        lifetime = Util.parseInt(buf, ia, 2);   // lifetime
        url      = Util.parseString(buf, ia);   // URL
        if (Util.parseInt(buf, ia, 1!= 0) {
            da.appendDebug("slpMsgParser::parseURL -> URL authentication blocks are present");
        }
        return url;
    }

/**
 * service request <#1>
 *+----------------------------+---------------------------+
 *| length of <PRList>         |   <PRList> string         \
 *+----------------------------+---------------------------+
 *| length of <service-type>   <service-type> string     \
 *+----------------------------+---------------------------+
 *| length of <scope-list>     <scope-list> string       \
 *+----------------------------+---------------------------+
 *| length of predicate string | service request predicate \
 *+----------------------------+---------------------------+
 *| length of <SLP SPI> string |   <SLP SPI> string        \
 *+----------------------------+---------------------------+ 
 */
    public byte[] SrvRqst(byte[] buf, int[] ia) {
        prlist = Util.parseString(buf, ia);             // PRList
        type   = Util.parseString(buf, ia);             // service type
        scope  = Util.parseString(buf, ia);             // scope list
        pred   = Util.parseString(buf, ia);             // predicate
        spi    = Util.parseString(buf, ia);             // SLP SPI string
        if (type.equalsIgnoreCase(Const.DAAdvert_Rqst)) return null;        
        byte[] tmp = database.getMatchedURL(type, scope, pred, ltag, ssExtList, ecode, getFuncID()null);        
                
        totalMatch = database.getTotalMatch();
        matchedEntry = database.getMatchedEntry();        
        if (Const.MESSAGE_LOG_ENABLED && Const.SrvRqst_LOG_ENABLEDda.append("\nINCOMING SRVRQST MESSAGE" +
                              "\n- Xid = "+ xid +
                           "\n- PRList = "+ prlist +
                              "\n- Type = " + type +
                              "\n- Scope = " + scope +                            
                              "\n- Predicate = " + pred + 
                              "\n- SPI = " + spi +                    
                              "\n");        
        return tmp;
    }
    /**
     * secure service request <#13>
     *+----------------------------+---------------------------+
     *| length of <PRList>         |   <PRList> string         \
     *+----------------------------+---------------------------+
     *| length of <service-type>   <service-type> string     \
     *+----------------------------+---------------------------+
     *| length of <scope-list>     <scope-list> string       \
     *+----------------------------+---------------------------+
     *| length of predicate string | service request predicate \
     *+----------------------------+---------------------------+
     *| length of <SLP SPI> string |   <SLP SPI> string        \
     *+----------------+-----------+---------------------------+
     *| # of AttrAuths |      Attribute Authentication Blocks  \
     *+----------------+---------------------------------------+
     */  
        public byte[] SrvRqstAuth(byte[] buf, int[] ia) { 
            prlist = Util.parseString(buf, ia);             // PRList
            type   = Util.parseString(buf, ia);             // service type
            scope  = Util.parseString(buf, ia);             // scope list
            pred   = Util.parseString(buf, ia);             // predicate
            spi    = Util.parseString(buf, ia);             // SLP SPI string
            int authBlockNum = Util.parseInt(buf, ia, 1);                         
            da.appendDebug("slpMsgParser::SrvRqstAuth -> " + authBlockNum + " Attribute Authentication Block(s) found");        
            if(authBlockNum!=0){
              AuthBlock(buf,ia,authBlockNum);
              }
            else ecode=Const.AUTHENTICATION_ABSENT;
            if (type.equalsIgnoreCase(Const.DAAdvert_Rqst)) return null;        
            byte[]  tmp = database.getMatchedURL(type, scope, pred, ltag, ssExtList, ecode,getFuncID(), _publicKey);        
                    
            totalMatch = database.getTotalMatch();
            matchedEntry = database.getMatchedEntry();        
            if (Const.MESSAGE_LOG_ENABLED && Const.SrvRqstAuth_LOG_ENABLEDda.append("\nINCOMING SECURESRVRQST MESSAGE" +
                                  "\n- Xid = "+ xid +
                              "\n- PRList = "+ prlist +
                                  "\n- Type = " + type +
                                  "\n- Scope = " + scope +                            
                                  "\n- Predicate = " + pred + 
                                  "\n- SPI = " + spi +                    
                                  "\n");        
            return tmp;
        }

/**
 * service reply (reply for service request) <#2>
 *+----------------------------+---------------------------+
 *|       Error Code           |    URL entry count        |
 *+----------------------------+---------------------------+
 *|   <URL entry 1>           ...     <URL entry N>        \
 *+----------------------------+---------------------------+
 */
    public void SrvReply(byte[] buf, int[] ia) {         
        ecode = Util.parseInt(buf, ia, 2);
        if (Const.MESSAGE_LOG_ENABLED && Const.SrvRply_LOG_ENABLEDda.append("\nINCOMING SRVREPLY MESSAGE" 
                              "\n- Xid = "+ xid + 
                          "\n- Error Code = " + ecode);        
        int n = Util.parseInt(buf, ia, 2);
        if (Const.MESSAGE_LOG_ENABLED && Const.SrvRply_LOG_ENABLEDda.append("\n- URLs = " + n);
        StringBuffer tl = new StringBuffer();
        for (int i=0; i<n; i++) {
            if (tl.length() 0tl.append(",");
            String strurl = parseURL(buf, ia);
            if (Const.MESSAGE_LOG_ENABLED && Const.SrvRply_LOG_ENABLEDda.append("\n "(i+1+"> " + strurl);
            tl.append(url);       // URL only, no lifetime
        }
        urlList = tl.toString();
        if (Const.MESSAGE_LOG_ENABLED && Const.SrvRply_LOG_ENABLEDda.append("\n");
    }

/**
 * service registration <#3>
 *+----------------------------------------------------------------+
 *|                          <URL-Entry>                           \
 *+---------------------------------+------------------------------+
 *|  length of service type string  |     <service-type>           \
 *+---------------------------------+------------------------------+
 *|  length of <scope-list>         |     <scope-list>             \
 *+---------------------------------+------------------------------+
 *|  length of attr-list string     |     <attr-list>              \
 *+----------------+----------------+------------------------------+
 *| # of AttrAuths | (if present) Attribute Authentication Blocks  \
 *+----------------+-----------------------------------------------+
 * Need to set error code (ecode)
 * Extensions have been parsed, so versionTS/acceptDA/acceptTS are known
 */
    public void SrvReg(byte[] buf, int[] ia
    {
        parseURL(buf, ia);                      // URL
        type  = Util.parseString(buf, ia);      // service type 
        scope = Util.parseString(buf, ia);      // scope list
        attrlength=Util.parseInt(buf, ia, 2);   // length of attr-list
        ia[0]=ia[0]-2;
        attr  = Util.parseString(buf, ia);      // attribute list
        _attr1=attr;
        
        int authBlockNum = Util.parseInt(buf, ia, 1);                         
        da.appendDebug("slpMsgParser::SrvReg -> " + authBlockNum + " Attribute Authentication Block(s) found");        
        
        if(authBlockNum!=0)
        
          AuthBlock(buf,ia,authBlockNum);                
        }
        else if (authBlockNum==0)
        {
          if (Const.SERVICE_REGISTRATION_AUTH_REQUIRED)
          {
            da.appendDebug("> Error: Authentication Absent");
              ecode=Const.AUTHENTICATION_ABSENT;
              da.displayMessage ("Authentication Block NOT found", Const.EXCLAMATION, "Authentication check");
          }          
        }
        
        if (lifetime < 0//Lifetime == 0 will be considered VALID and as a service with an INFINITE Lifetime 
        {
            ecode = Const.INVALID_REGISTRATION;
        
        else if (!Util.shareString(daf.getScope(), scope, ",")) 
        {                              
            ecode = Const.SCOPE_NOT_SUPPORTED;
        
        else 
        {
            if (acceptDA.equalsIgnoreCase(daf.getFQDN())) 
            {
                arrivalTS = acceptTS;
            
            else 
            {
                arrivalTS = System.currentTimeMillis();
            }            
            
            if(ecode==0)
            {
            ecode = database.addEntry(false, ltag, type, url, lifetime, scope,
                  attr, slp_flag, versionTS, arrivalTS, acceptDA, acceptTS);
            }         
        }
        
        if (Const.MESSAGE_LOG_ENABLED && Const.SrvReg_LOG_ENABLED
        {             
          String token = null;
          String attribute = null
          StringTokenizer st = new StringTokenizer(attr,",");
          attr = "";
         while (st.hasMoreTokens())
         {          
           token = st.nextToken();
           if (token.startsWith("("))
           {         
             if (token.endsWith(")"))
             {
               /*
                * We have a valid attribute in token, with one value
                * (attr_tag=attr_values) 
                */                      
               attr += "\n                   " + token.substring(1,token.length()-1);                   
               
             else
             {   
               /*
                * We have the first part of a valid attribute in token, with more than one value
                * (attr_tag=attr_values) 
                */
               attribute = token.substring(1);
             }
           }       
           else
           {   
             if (attribute != null)
             {
               if (token.endsWith(")"))
               {   
                 /*
                   * We have the last part of a valid attribute in token, with more than one value
                   * (attr_tag=attr_values) 
                   */
                 attr += "\n                   " + attribute + "," + token.substring(0,token.length()-1);
                 attribute = null;
               }
               else
               {
                 /*
                   * We have the N part of a valid attribute in token, with more than one value
                   * (attr_tag=attr_values) 
                   */
                 attribute += "," + token;
               }
             }
             else
             {
               /*
                * We have an attribute in token with no values
                * attr_tag 
                */
               //da.appendDebug("Attribute Name = " + token);
               attr += "\n                   " + token;
             }                         
           }
         }                                  
                   da.append("\nINCOMING SRVREG MESSAGE"+
                              "\n- Xid = "+ xid + 
                          "\n- Type = " + type + 
                          "\n- Scope = " + scope + 
                          "\n- Url = " + url + 
                          "\n- Lifetime = " + lifetime +                           
                          "\n- Attribute List = " + attr + 
                          "\n")
                  
         }            
    }      
        
 /**
 * Authentication Block
 *+---------------------------------+------------------------------+
 *|  Block   Structure  Descriptor  | Authentication Block Length  \
 *+---------------------------------+------------------------------+
 *|                             Timestamp                          \
 *+---------------------------------+------------------------------+
 *|           SPI Length            |             SPI              \
 *+---------------------------------+------------------------------+
 *|                             SIGNATURE                          \
 *+----------------------------------------------------------------+
  */
    
    public void AuthBlock(byte[] buf,int[] ia,int authblocknum){
        
        for (int i = 1; i <= authblocknum; i++)
        {
          da.appendDebug("slpMsgParser::SrvReg -> Parsing Authentication Block [" + i + "]");
          int BSD = Util.parseInt(buf, ia, 2);
            da.appendDebug(" > BSD = " + BSD);
            int blockLength = Util.parseInt(buf, ia, 2);
            da.appendDebug(" > Block Length = " + blockLength);
            int timeStamp = Util.parseInt(buf, ia, 4);            
            da.appendDebug(" > Time Stamp = " (new Date((long)timeStamp*1000)).toString());
            
            /*Estrazione chiave Pubblica*/
            ia[0]=ia[0]+2;
             byte[] _spi=new byte[162];
              for(int k=0; k < 162; k++)
              {
                 _spi[k= buf[ia[0]+k];
              
              ia[0]=ia[0]+162;
              try{
              X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(_spi);
              KeyFactory keyFactory = KeyFactory.getInstance("RSA");
               _publicKey =(RSAPublicKeykeyFactory.generatePublic(pubKeySpec);      
              
              da.append(" > SPI = "+_publicKey.toString());
              }catch (NoSuchAlgorithmException e) {
                   // TODO Auto-generated catch block
                   e.printStackTrace();
              }catch (InvalidKeySpecException e) {
                  // TODO Auto-generated catch block
                   e.printStackTrace();
              }
                     
            /*Signature*/
            int signLength = blockLength - 10 - _spi.length;
            byte[] _sign = new byte[signLength];            
            
            for (int j = 0; j < signLength; j++)
            {
              _sign[j= buf[ia[0]+j];
            }            
            da.append(" > SIGN = "+ _sign);
              
            /*Sign Verify*/
        if(func_id==Const.SrvReg)
        
                                  
            try{
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            DataOutputStream dos = new DataOutputStream(bos);
                        
            dos.writeShort(_spi.length);
            dos.write(_spi);
            dos.writeShort((shortattrlength);
            
            String token1 = null;          
          StringTokenizer st1 = new StringTokenizer(_attr1,",");
          _attr1="";
          
          while (st1.hasMoreTokens())
         {          
           token1 = st1.nextToken();
           if (token1.startsWith("("))
           {         
             if (token1.endsWith(")"))
             
               
               int indexuguale= token1.indexOf("=");    
               dos.write("(".getBytes());
                   dos.write(token1.substring(1,indexuguale).getBytes());
                 dos.write("=".getBytes());
                 dos.write(token1.substring(indexuguale+1,token1.length()-1).getBytes());
               dos.write(")".getBytes());
             }
           }       
           else
           {   
             dos.write(token1.getBytes());         
           }
         
         }
                        
            dos.writeInt(timeStamp);
            dos.write(getURL().getBytes());
           
            byte[] data=bos.toByteArray();
            Signature signature = Signature.getInstance("SHA1withRSA");
            signature.initVerify(_publicKey);
            signature.update(data);
            
            boolean verify=signature.verify(_sign);
               if(verify){
                            da.append("> Athentication Executed Correctly");
                            }
              else{
              da.append("> Error: Authentication Failed");
              da.displayMessage ("Authentication Failed", Const.CRITICAL, "Authentication check");
              ecode=Const.AUTHENTICATION_FAILED;
            }
            
            }catch(IOException e1){
              //DO nothing???
            }
            catch (NoSuchAlgorithmException e2) {
              da.appendDebug("Algorithm not supported");
                da.displayMessage ("Encryption Algorithm NOT supported", Const.EXCLAMATION, "Authentication check");
               
              }

            catch (InvalidKeyException e3) {
              da.appendDebug("Invalid public key");
                da.displayMessage ("Invalid public key", Const.EXCLAMATION, "Authentication check");
                
              }
            catch (SignatureException e4) {
              da.appendDebug("Invalid signature");
                da.displayMessage ("Invalid signature", Const.EXCLAMATION, "Authentication check");
              }
         }    
        else if(func_id==Const.SrvRqstAuth)
              {
          try{
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                DataOutputStream dos = new DataOutputStream(bos);
                            
                dos.writeShort(_spi.length);
                dos.write(_spi);
                dos.writeShort(type.getBytes().length);
                dos.write(type.getBytes());
                dos.writeShort(scope.getBytes().length);
                dos.write(scope.getBytes());
                dos.writeShort(pred.getBytes().length);
                dos.write(pred.getBytes());
                dos.writeInt(timeStamp);
                
                byte[] data=bos.toByteArray();
                Signature signature = Signature.getInstance("SHA1withRSA");
                signature.initVerify(_publicKey);
                signature.update(data);
                
                boolean verify=signature.verify(_sign);
                   if(verify){
                                da.append("> Athentication Executed Correctly");
                                }
                  else{
                  da.append("> Error: Authentication Failed");
                  ecode=Const.AUTHENTICATION_FAILED;
                }
                
                }catch(IOException e1){
                  //DO nothing???
                }
                catch (NoSuchAlgorithmException e2) {
                    da.appendDebug("Encryption Algorithm not supported");
                    da.displayMessage ("Encryption Algorithm NOT supported", Const.EXCLAMATION, "Authentication check");
                   
                  }

                catch (InvalidKeyException e3) {
                  da.appendDebug("Invalid public key");
                    da.displayMessage ("Invalid public key", Const.EXCLAMATION, "Authentication check");
                    
                  }
                catch (SignatureException e4) {
                  da.appendDebug("Invalid signature");
                    da.displayMessage ("Invalid signature", Const.EXCLAMATION, "Authentication check");
                  }

              }
        }
        
        
    }

/**
 * service De-registration <#4>
 *+-----------------------------+---------------------------+
 *|  Length of <scope-list>     |        <scope-list>       \
 *+-----------------------------+---------------------------+
 *|                         <URL-entry>                     \
 *+-----------------------------+---------------------------+
 *|  Length of <tag-list>       |        <tag-list>         \
 *+-----------------------------+---------------------------+
 * Need to set error code, 0 is for OK
 */
    public void SrvDeReg(byte[] buf, int[] ia) {
        scope = Util.parseString(buf, ia);              // scope list
        parseURL(buf, ia);                              // URL
        tag   = Util.parseString(buf, ia);              // tag list
        if (Util.shareString(daf.getScope(), scope, ",")) {
            ecode = database.rmEntry(ltag, url, scope, tag, versionTS,
                                     acceptDA, acceptTS);
        else {                              
            ecode = Const.SCOPE_NOT_SUPPORTED;
        }
        if (Const.MESSAGE_LOG_ENABLED && Const.SrvDeReg_LOG_ENABLEDda.append("\nINCOMING SRVDEREG MESSAGE" +
        "\n- Xid = "+ xid + 
        "\n- Scope = " + scope +
        "\n- Url = " + url + 
        "\n- Tag List = " + tag + 
        "\n");
    }

/**
 * service ack (reply for SrvReg & SrvDeReg) <#5>
 *+-----------------------------+
 *|        Error Code           |
 *+-----------------------------+
 * return the error code
 */
    public void SrvAck(byte[] buf, int[] ia) {
        ecode = Util.parseInt(buf, ia, 2);
        if (Const.MESSAGE_LOG_ENABLED && Const.SrvAck_LOG_ENABLEDda.append("\nINCOMING SRVACK MESSAGE" +
                              "\n-Xid = "+ xid + 
                          "\n- Error = " + ecode +
                          "\n");
    }

/**
 * attribute request <#6>
 *+-------------------------------+----------------------------+
 *|  length of PRList             |   <PRList> string          \
 *+-------------------------------+----------------------------+
 *|  length of URL                |         URL                \
 *+-------------------------------+----------------------------+
 *|  length of <scope-list>       |   <scope-list> string      \
 *+-------------------------------+----------------------------+
 *|  length of <tag-list> string  |   <tag-list> string        \
 *+-------------------------------+----------------------------+
 *|  length of <SLP SPI> string   |   <SLP SPI> string         \
 *+-------------------------------+----------------------------+
 */
    public void AttrRqst(byte[] buf, int[] ia) {
      da.appendDebug("slpMsgParser::AttrRqst");
      prlist = Util.parseString(buf, ia);             // PRList
        url    = Util.parseString(buf, ia);             // URL or Service type
        scope  = Util.parseString(buf, ia);             // scope list
        tag    = Util.parseString(buf, ia);             // tag list
        spi    = Util.parseString(buf, ia);             // SLP SPI string
        
        /*
         * Check if at least one scope is in the list of the DA
         */        
        if (!Util.shareString(daf.getScope(), scope, ",")) 
        {
          // NO SCOPES...
          ecode = Const.SCOPE_NOT_SUPPORTED;
            attrList = "";
        
        else 
        {
            ecode = Const.OK;                        
            attrList = database.getAttrList(url, scope, tag, ltag);                       
        }
        if (Const.MESSAGE_LOG_ENABLED && Const.AttrRqst_LOG_ENABLED) {
          
          da.append("\nINCOMING ATTRRQST MESSAGE"+
        "\n- Xid = "+ xid +         
        "\n- Scope = " + scope +         
        "\n- Attribute List = " + tag +
        "\n- Service URL or Type = " + url +
        "\n- Prlist = " + prlist +
        "\n- Spi = " + spi +
        "\n");        
        }
    }

/**
 * attribute reply (reply for attribute request) <#7>
 *+-----------------------------+---------------------------------+
 *|         Error Code          |   length of <attr-list>         |
 *+-----------------------------+---------------------------------+
 *|                          <attr-list>                          \
 *+----------------+----------------------------------------------+
 *| # of AttrAuths |  Attribute authentication block (if present) \
 *+----------------+----------------------------------------------+
 */
    public void AttrReply(byte[] buf, int[] ia) {
        ecode = Util.parseInt(buf, ia, 2);
        attrList = Util.parseString(buf, ia);
        if (Const.MESSAGE_LOG_ENABLED && Const.AttrRply_LOG_ENABLED){
          da.append("\nINCOMING ATTRREPLY MESSAGE"+
        
        "\n- Xid = "+ xid + 
        "\n- Error = " + ecode +         
        "\n- Attribute List = " + attrList + 
        "\n");
        }
    }

/**
 * directory agent advertisement <#8>
 *+-------------------------------+--------------------------------+
 *|     Error Code                |   DA Stateless Boot Timestamp  |
 *+-------------------------------+--------------------------------+
 *| DA Stateless Boot Time cont.  |      length of URL             |
 *+-------------------------------+--------------------------------+
 *|                              URL                               \
 *+-------------------------------+--------------------------------+
 *|   length of <scope-list>      |      <scope-list>              \
 *+-------------------------------+--------------------------------+
 *|   length of <attr-list>       |      <attr-list>               \
 *+-------------------------------+--------------------------------+
 *|   length of SLP <SPI>         |      SLP <SPI> string          \
 *+---------------+---------------+--------------------------------+
 *| # Auth Blocks |       Authentication block (if any)            \
 *+---------------+---------------+--------------------------------+
 */
    public void DAAdvert(byte[] buf, int[] ia) {
      /*da.appendDebug("\n\n\n\nslpMsgPrs::DAADVERT\n\n\n\n");
      for (int i=0; i<buf.length;i++){
        da.appendDebug(buf[i]);
      }*/
      ecode = Util.parseInt(buf, ia, 2);
        daBootTS = Util.parseInt(buf, ia, 4);           // boot timestamp
        url   = Util.parseString(buf, ia);              // URL
        scope = Util.parseString(buf, ia);              // scope-list
        attr  = Util.parseString(buf, ia);              // attr-list
        da.appendDebug("slpMsgParser::DAAdvert-URL ="+url);
        
        if (Const.MESSAGE_LOG_ENABLED && Const.DAAdvert_LOG_ENABLEDda.append("\nINCOMING DAADVERT MESSAGE"+
        "\n- Xid = "+ xid + 
        "\n- Url = " + url + 
        "\n- Scope = " + scope + 
        "\n- Attribute List = " + attr + 
        "\n");
    }

/**
 * service type request <#9>
 *+-------------------------------+-----------------------------+
 *|     length of PRList          |     <PRList> string         |
 *+-------------------------------+-----------------------------+
 *| length of Naming Authority    |  <Naming Authority String>  |
 *+-------------------------------+-----------------------------+
 *| length of <scope-list>        |    <scope-list> string      |
 *+-------------------------------+-----------------------------+
 */
    public void SrvTypeRqst(byte[] buf, int[] ia) {
        prlist = Util.parseString(buf, ia);             // PRList
        String na = Util.parseString(buf, ia);          // Naming authority
        scope  = Util.parseString(buf, ia);             // scope list
        if (!Util.shareString(daf.getScope(), scope, ",")) 
        {                            
            ecode = Const.SCOPE_NOT_SUPPORTED;
            typeList = "";
        
        else 
        {
            ecode = Const.OK;
            typeList = database.getServiceTypeList(na, scope);            
        }
        if (Const.MESSAGE_LOG_ENABLED && Const.SrvTypeRqst_LOG_ENABLED
        {
          
          da.append("\nINCOMING SRVTYPERQST MESSAGE"+
        "\n- Xid = "+ xid + 
        "\n- PRList = " + prlist + 
        "\n- Naming Authority = " + na + 
        "\n- Scope = " + scope + 
        "\n");        
        }
    }

/**
 * service type reply (reply for service type request) <#10>
 *+-------------------------------+-------------------------------+
 *|      Error Code               |    length of <srvType-list>   |
 *+-------------------------------+-------------------------------+
 *|                       <srvType-list>                          |
 *+---------------------------------------------------------------+
 */
    public void SrvTypeReply(byte[] buf, int[] ia) {
        ecode = Util.parseInt(buf, ia, 2);
        typeList = Util.parseString(buf, ia);
        if (Const.MESSAGE_LOG_ENABLED && Const.SrvTypeRply_LOG_ENABLED
        {        
          da.append("\nINCOMING SRVTYPEREPLY MESSAGE"+
        
        "\n- Xid = "+ xid + 
        "\n- Type List = " + typeList + 
        "\n");
        }
    }

/**
 * AntiEtrpRqst <#12> message
 *+-------------------------------+-------------------------------+
 *|      Anti-entropy type ID     |   Number of Accept ID entries |
 *+-------------------------------+-------------------------------+
 *|    Accept ID Entry 1         ...          Accept ID Entry k   \
 *+---------------------------------------------------------------+
 */
    public void AntiEtrpRqst(byte[] buf, int[] ia) {
        etrpType = Util.parseInt(buf, ia, 2);
        int k  = Util.parseInt(buf, ia, 2);
        atsList.clear();
        adaList.clear();
        for (int i=0; i<k; i++) {
            atsList.addElement(new Long(Util.parseLong(buf, ia)));
            adaList.addElement(Util.parseString(buf, ia));
        }
        if (Const.MESSAGE_LOG_ENABLED && Const.AntiEtrpRqst_LOG_ENABLEDda.append("\nINCOMING ANTIETRPRQST MESSAGE"+
        "\n- Xid = "+ xid + 
        "\n- Entropy Type = " + etrpType + 
        "\n- ATS List = " + atsList.toString() 
        "\n- ADA List = " + adaList.toString() 
        "\n");
    }

/**
 * Mesh Forwarding extension parser: 
 *    (1) initialize (turn off previous value)
 *    (2) if MeshFwdExt, 
 *           get Fwd-ID & versionTS
 *           if Fwd-ID == Const.RqstFwd, change it to Const.Fwded
 *           if Fwd-ID == Const.Fwded, get acceptDA & acceptTS
 *+--------------------------------+----------------------------------+
 *|  MeshFwd Extension ID = 0x0006 |  Next Extension Offset (NEO)     |
 *+--------------+-----------------+----------------------------------+
 *|  NEO Contd.  |     Fwd-ID      |         Version Timestamp        |
 *+--------------+-----------------+----------------------------------+
 *|                     Version Timestamp, contd.                     |
 *+--------------------------------+----------------------------------+
 *|    Version Timestamp, contd.   |           Accept ID              \
 *+--------------------------------+----------------------------------+
 */
    public void MeshFwdExt(byte[] buf, String fromPeer) { // buf: whole msg 
        meshFwdID = -1;                   // no MeshFwdExt
        acceptDA  = daf.getFQDN();
        acceptTS  = System.currentTimeMillis();
        versionTS  = acceptTS;
        int[] ia = ext_offset };        // initial extension offset
        while (ia[0!= Const.EndOfExt) { // while has more extensions
            int extID = Util.parseInt(buf, ia, 2);      // extension ID
            int nextExt = Util.parseInt(buf, ia, 3);    // next extension
            if (extID == Const.MeshFwdExt) {    // mesh-forwarding extension
                int idAddr = ia[0];       // may need to MeshFwdID
                meshFwdID = Util.parseInt(buf, ia, 1);
                versionTS  = Util.parseLong(buf, ia);
                if (meshFwdID == Const.Fwded) {
                    acceptTS = Util.parseLong(buf, ia);
                    acceptDA = Util.parseString(buf, ia);
                    daf.setSummary(acceptDA, acceptTS, fromPeer);    
                else if (meshFwdID == Const.RqstFwd) { 
                    Util.writeInt(buf, idAddr, Const.Fwded, 1);  
                    Util.writeLong(buf, idAddr+9, acceptTS);  
                }
                break// at most one MeshFwd extension
            }
            ia[0= nextExt;
        }
        if (meshFwdID != Const.Fwded) {         // accepted by local host
            daf.setSummary(acceptDA, acceptTS, acceptDA);       
        }
    }

    public void SelectSortExt(byte[] buf) { // buf: whole message 
        ssExtList.clear();
        hasSelectExt = false;
        int[] ia = ext_offset };        // initial extension offset
        while (ia[0!= Const.EndOfExt) { // while has more extensions
            int extID = Util.parseInt(buf, ia, 2);      // extension ID
            int nextExt = Util.parseInt(buf, ia, 3);    // next extension
            if (extID == Const.SelectExt) {     // selection extension
                hasSelectExt = true;
                int num = Util.parseInt(buf, ia, 2);
                ssExtList.addElement(new SelectSortExt(Const.SelectExt, num));
            else if (extID == Const.SortExt) {  // sort extension
                String key = Util.parseString(buf, ia);
                ssExtList.addElement(new SelectSortExt(Const.SortExt, key));
            }
            ia[0= nextExt;
        }
    }

    public void AttrListExt(byte[] buf) { // buf: whole message 
        urlVector.clear();
        attrVector.clear();
        hasAttrListExt = false;
        int[] ia = ext_offset };        // initial extension offset
        while (ia[0!= Const.EndOfExt) { // while has more extensions
            int extID = Util.parseInt(buf, ia, 2);      // extension ID
            int nextExt = Util.parseInt(buf, ia, 3);    // next extension
            if (extID == Const.AttrListExt) {   // AttrList extension
                hasAttrListExt = true;
                urlVector.addElement(Util.parseString(buf, ia));  // url
                attrVector.addElement(Util.parseString(buf, ia))// attr
            }
            ia[0= nextExt;
        }
    }
}