package com.Series40Book.HttpClient;
 
import javax.microedition.io.*;

public class DigestAuthHandler implements Handler {
 
  private String username;
  private String password;

  private String httpMethod;
  private String uri;
  private String realm;
  private String nonce;
  private String count;
  private String cnonce;
  private String qop;
  private String opaque;
  private String algor;
  private boolean stale;
  private int ncount;

  public DigestAuthHandler (String u, String p) {
    username = u;
    password = p;
  }
 
  public void prepareHeaders(HttpConnection c)
                               throws Exception {
    String h = "Digest ";

    if(username != null)
      h = h + "username=\"" + username + "\", ";
    if(realm != null)
      h = h + "realm=\"" + realm + "\", ";
    if(nonce != null)
      h = h + "nonce=\"" + nonce + "\", ";
    if(uri != null)
      h = h + "uri=\"" + uri + "\", ";
    if(opaque != null)
      h = h + "opaque=\"" + opaque + "\", ";

    if(qop != null) {
      h = h + "qop=\"" + qop + "\", ";
      // cnonce and nonce-count are required
      // if qop is present.
      //
      // cnonce is a random number generated by the client.
      // You should use your device build-in random number
      // generator to produce it.
      cnonce = "0123456789";
      h = h + "cnonce=\"" + cnonce + "\", ";
      h = h + "nc=" + count + ", ";

      // Increase counter by one.
      // The counter will be reset
      // when a new nonce comes in.
      ncount++;
      String nc = Integer.toHexString(ncount);
      count =
          new String("00000000").substring(nc.length())
          + nc;
    }

// For some reason, apache sends back algorithm "D".
// MD5 is assumed here.
//    if(algor != null)
//      h = h + "algorithm=\"" + algor + "\", ";
//    else
      h = h + "algorithm=\"MD5\", ";

    h = h + "response=\"" + getDigest() + "\"";

    c.setRequestProperty("Authorization", h);
  }
 
  public boolean processHeaders (HttpConnection c)
                                  throws Exception {
    if ( c.getResponseCode() == 401 ) {
      httpMethod = c.getRequestMethod();
      uri = c.getFile();

      parse (c.getHeaderField("WWW-Authenticate"));

      // need to re-send request
      return true;
    } else {
      return false;
    }
  }


  private void parse (String header) {
    int i, j;
    String propvalue, prop, value;

    // Get rid of string "Digest"
    i = header.indexOf(" ");
    header = header.substring(i+1).trim();

    while ( (i = header.indexOf(",")) != -1 ) {
      propvalue = header.substring(0, i).trim();
      header = header.substring(i+1).trim();

      j = propvalue.indexOf("=");
      prop = propvalue.substring(0, j).toLowerCase();
      value = propvalue.substring(j+1).trim();
      // Get rid of quote marks
      value = value.substring(1, value.length() - 1); 

      if(prop.compareTo("qop") == 0)
        qop = value;
      else if(prop.compareTo("nonce") == 0)
        setNonce(value);
      else if(prop.compareTo("realm") == 0)
        realm = value;
      else if(prop.compareTo("opaque") == 0)
        opaque = value;
      else if(prop.compareTo("algorithm") == 0)
        algor = value;

    }
  }

  private void setNonce(String n) {

    // If this is a new nonce, reset counter
    String c;
    if(nonce == null) ncount = 1;
    else if(n.compareTo(nonce) != 0) ncount = 1;
    // else ncount++;
    c = Integer.toHexString(ncount);
    count =
        new String("00000000").substring(c.length())
        + c;

    nonce = n;
  }

  private String getDigest () {
    String plaintext;
    String A1, A2;

    if (username == null || password == null || 
        realm == null || uri == null || 
        nonce == null) 
      return "";

    if (username.length() == 0 ||
        password.length() == 0 ||
        realm.length() == 0 || uri.length() == 0 || 
        nonce.length() == 0) 
      return "";
 
    A1 = getA1();
    A2 = getA2();
 
 
    if(qop == null)
      plaintext = A1 + ":" + nonce + ":" + A2;
    else
      plaintext = A1 + ":" + nonce + ":" + count + ":"
          + cnonce + ":" + qop + ":" + A2;

    MD5Digest md5 = new MD5Digest ();
    byte [] plainbytes = plaintext.getBytes();
    md5.update( plainbytes, 0, plainbytes.length );
    byte [] digestbytes = new byte[md5.getDigestSize()];
    md5.doFinal(digestbytes, 0);
 
    return convert2Hex(digestbytes, 16);
  }

  private String getA1() {
    String plaintext, digest;
 
    plaintext = username + ":" + realm + ":" + password;
 
    MD5Digest md5 = new MD5Digest ();
    byte [] plainbytes = plaintext.getBytes();
    md5.update( plainbytes, 0, plainbytes.length );
    byte [] digestbytes = new byte[md5.getDigestSize()];
    md5.doFinal(digestbytes, 0);
 
    digest = convert2Hex(digestbytes, 16);
 
    /* No MD5-sess support right now
    if(algor.toLowerCase().compareTo("md5-sess") != 0)
      return digest;

    digest += ":" + nonce + ":" + cnonce;

    MD.update(digest.getBytes());

    digest = convert2Hex(MD.digest(), 16);
    */

    return digest;
  }

  private String getA2() {
    String plaintext;
 
    plaintext = httpMethod + ":" + uri;
 
    MD5Digest md5 = new MD5Digest ();
    byte [] plainbytes = plaintext.getBytes();
    md5.update( plainbytes, 0, plainbytes.length );
    byte [] digestbytes = new byte[md5.getDigestSize()];
    md5.doFinal(digestbytes, 0);
 
    return convert2Hex(digestbytes, 16);
  }

  private String convert2Hex(byte [] buf, int length) {

    String T = "";
 
    for(int x = 0; x < length; x++) {
      int y = buf[x];
      if(y < 0) y += 256;
      String d = Integer.toHexString(y);
      if(d.length() == 1) T += "0";
      T += d;
    }
    return T;
  }
}