//
//  Copyright (c) 2000-2005 Steven Champeon. All rights reserved.
// 
//  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 2 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, write to the Free Software
//  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
//  The author may be contacted via email at <schampeo@hesketh.com>;
//  other information may be found on the Web at http://hesketh.com/
//
//  Spammer scum address scraper bots make the baby Jesus cry.
//
//  Author: Steven Champeon <schampeo@hesketh.com>
//  Maintainer: same
//  Release: 
//
//  $RCSfile: ipcalc.js,v $
//  $Revision: 1.3 $
//  $Date: 2005/08/18 18:25:52 $
//
//
//  Purpose:
//   Calculate all manner of useful stuff given an IP and netmask
//   and display it in a frame.
//
//  Dependencies:
//   None.
//
//  Usage:
//   call 'calculate()' with a form argument, where one form input is
//   named 'ip'. Output will be written to the frameset 'out'.
//
//  Note: 
//   based on ipcalc by Keith Owens <kaos@ocs.com.au>
//   sprintf stuff cadged from http://aoki2.si.gunma-u.ac.jp/JavaScript/io.js
//    and heavily modified to act like sprintf instead of printf
//
//  Allowing the IP to be entered as a hex string suggested by Henrik Paulini

var ipaddr;           // IP address we want info for
var mask = 0;             // netmask to use (optional)
var ia = new Array(); // individual bytes of ipaddr
var nm = new Array(); // individual bytes of netmask
var netmask;          // netmask as 32 bit value
var maskbits;         // number of bits in netmask

// for sprintf
var string_constant_blank = "                                  ";
var string_constant_zero  = "0000000000000000000000000000000000";

// calculate is our driver
function calculate(f) {
  get_ip_and_mask(f.ip.value);
  show_info();
}

function get_ip_and_mask(i) {
  var j;
  var s = i.split("/");

  if((s[0].indexOf('0') == 0) && 
     (s[0].indexOf('x') == 1)) {
	// we're in hexadecimal mode
    if(s[0].length != 10) {
      alert('Hex format must be 10 characters, including 0x!');
      return false;
    } else {
      var h = s[0].substr(2,8);
	  // alert("h: "+h);
      for(var o = 0; o < 4; o++) {
        var byteoffset = o * 2;
		var hexbyte = h.substr(byteoffset,2);
        var hexstr = "0x"+h.substr(byteoffset,2);
        ia[o] = parseInt(hexstr);
      }
    }
  } else {
	// we're in dotted quad mode
    // alert("s: "+s[0]+" ("+((s[1].length > 0) ? s[1] : "none")+")");
    ipaddr = s[0];
    ia = ipaddr.split(".");
    // alert("ia: "+ia[0]+","+ia[1]+","+ia[2]+","+ia[3]);
  }

  for(j = 0; j < 4; j++) {
    if(ia[j] > 255) {
      alert("Invalid byte " + ia[j] + " in IPv4 address, must be 0-255");
      return false;
    }
  }
  if((ia[0] < 1) || (ia[0] > 223)) {
    alert("First byte must be 1-223, I do not handle multicast or experimental");
    return false;
  }

  if((s[1].indexOf('0') == 0) &&
     (s[1].indexOf('x') == 1)) {
    // we're in hexadecimal mode
    if(s[0].length != 10) {
      alert('Hex format must be 10 characters, including 0x!');
      return false;
    } else {
      var m = s[1].substr(2,8);
      var maskbytes = Array(4);
      for(var o = 0; o < 4; o++) {
        var byteoffset = o * 2;
        var hexbyte = m.substr(byteoffset,2);
        var hexstr = "0x"+m.substr(byteoffset,2);
        maskbytes[o] = parseInt(hexstr);
        if(maskbytes[o] > 0) {
          alert(parseInt(maskbytes[o] +1) / 32);
          mask += parseInt((maskbytes[o] +1) / 32);
        }
      }
    }
  } else if(s[1]) {
    // we're in decimal mode
    mask = s[1];
  } else {
    // treat it as classful
    if(ia[0] < 128) {
      mask = 8;
    } else if(ia[0] < 192) {
      mask = 16;
    } else {
      mask = 24;
    }
  }
  
  if((mask <= 32) || (mask >= 0)) {
    maskbits = mask;
    mask = '';
    if(maskbits <= 32) {
    	j = 0xffffffff << (32 - maskbits) & 0xffffffff;
    	if(maskbits == 0) { j = 0; }
    	nm[0] = j >>> 24; // work around signage problems
    	nm[1] = j >> 16 & 0xff;
    	nm[2] = j >> 8 & 0xff;
    	nm[3] = j & 0xff;
    	//alert("nm[0]: "+nm[0]+"; nm[1]: "+nm[1]+"; nm[2]: "+nm[2]+"; nm[3]: "+nm[3]);
    } else {
    	alert("Netmask bits must be 0-32, "+maskbits+" is invalid");
    	return false;
    }
  } else if(mask.indexOf(".") != 0) {
    nm = mask.split(".");
    mask = '';
	  for(j = 0; j < 4; j++) {
	    if(nm[j] > 255) {
	      alert("Invalid byte " + nm[j] + " in netmask, must be 0-255");
	      return false;
	    }
	  }
  } else {
    alert("Netmask must be 0-32 or dotted quad, "+mask+" is invalid.");
    return false;
  }
  // we're using >>> to fix sign problems with <<
  ipaddr = ((ia[0] << 24) >>> 32);
  ipaddr += ((ia[1] << 16) >>> 32);
  ipaddr += ((ia[2] << 8) >>> 32);
  ipaddr += (ia[3] >>> 32);
  
  netmask = ((nm[0] << 24) >>> 32) + ((nm[1] << 16) >>> 32) + ((nm[2] << 8) >>> 32) + (nm[3] >>> 32);
  for(maskbits = 0, j = netmask; j != 0; ++maskbits) {
  	if((j & 0xc0000000) == 0x40000000) {
  		alert("Netmask must have contiguous leftmost '1' bits. "+ sprintf("%08X", netmask)+" is invalid");
  		return false;
  	}
  	j <<= 1;
  }
}

function show_info() {
	var output = "";
	output = '<html><head><link rel="stylesheet" href="ipcalc.css"></head><body>';
	output += '<table border=0><tr>';
	output += '<td class="label">IP address</td>';
	output += sprintf('<td class="infol">   %3d   .  %3d   .  %3d   .  %3d    / %2d </td><td class="infol"> %d.%d.%d.%d/%d </td>',
	                  ia[0], ia[1], ia[2], ia[3], maskbits, 
	                  ia[0], ia[1], ia[2], ia[3], maskbits);

	output += '</tr><tr>';

	output += '<td class="label">Netmask bits</td>';
	output += sprintf('<td class="infol"> %s %s %s %s</td>',
	                  getbits(nm[0]),
	                  getbits(nm[1]),
	                  getbits(nm[2]),
	                  getbits(nm[3]));

	output += '</tr><tr>';

	output += '<td class="label">Netmask bytes</td>';
	output += sprintf('<td class="infol">   %3d   .  %3d   .  %3d   .  %3d </td><td class="infol"> %d.%d.%d.%d </td>',
	                  nm[0], nm[1], nm[2], nm[3],
	                  nm[0], nm[1], nm[2], nm[3]);

	output += '</tr><tr>';

	var addressbits = getbits(ia[0]) + " " +
                    getbits(ia[1]) + " " +
                    getbits(ia[2]) + " " +
	                  getbits(ia[3]);

	i = maskbits + ((maskbits - 1)/8);
	var hostbits = addressbits.substring(i);   // save host bits
	addressbits = addressbits.substring(0, i); // extract network bits
	addressbits = '<span class="rinfo">' + addressbits + '</span>'; // reverse video for network bits
	hostbits = '<span class="infol">' + hostbits + '</span>';
	
	output += '<td class="label">Address bits</td>';
	output += sprintf('<td class="infol"> %s%s</td><td></td>',
	                  addressbits, hostbits);

	output += '</tr><tr>';

	if(maskbits < 32) {
		var n = new Array();
		i = (ipaddr & netmask) >>> 32;
    n[0] = (i >>> 24)        >>> 32;
    n[1] = (i >>  16 & 0xff) >>> 32;
   	n[2] = (i >>  8  & 0xff) >>> 32;
    n[3] = (i        & 0xff) >>> 32;
    
    output += '<td class="label">Network</td>';
		output += sprintf('<td class="infol">   %3d   .  %3d   .  %3d   .  %3d</td><td class="infol"> %d.%d.%d.%d </td>',
		                  n[0], n[1], n[2], n[3],
		                  n[0], n[1], n[2], n[3]);
	} else {
	  output += '<td class="label">Network</td><td class="infol">None, mask == 32</td><td></td>';
	}
	
	output += '</tr><tr>';

	if(maskbits < 32) {
		i |= (0xffffffff >>> maskbits) >>> 32;
    n[0] = (i >>> 24)        >>> 32;
    n[1] = (i >>  16 & 0xff) >>> 32;
   	n[2] = (i >>  8  & 0xff) >>> 32;
    n[3] = (i        & 0xff) >>> 32;
    output += '<td class="label">Broadcast</td>';	
		output += sprintf('<td class="infol">   %3d   .  %3d   .  %3d   .  %3d</td><td class="infol"> %d.%d.%d.%d </td>',
		                  n[0], n[1], n[2], n[3],
		                  n[0], n[1], n[2], n[3]);
	} else {
		output += '<td class="label">Broadcast</td><td class="infol">None, mask == 32</td><td></td>';
	}

	output += '</tr><tr>';

	if(maskbits < 31) {
		i = ((ipaddr & netmask) + 1) >>> 32;
    n[0] = (i >>> 24)        >>> 32;
    n[1] = (i >>  16 & 0xff) >>> 32;
   	n[2] = (i >>  8  & 0xff) >>> 32;
    n[3] = (i        & 0xff) >>> 32;
    output += '<td class="label">First Host</td>';
		output += sprintf('<td class="infol">   %3d   .  %3d   .  %3d   .  %3d</td><td class="infol"> %d.%d.%d.%d </td>',
		                  n[0], n[1], n[2], n[3],
		                  n[0], n[1], n[2], n[3]);
	}	else {
		output += '<td class="label">First Host</td><td class="infol">None, mask >= 31</td><td></td>';
	}

	output += '</tr><tr>';

	if(maskbits < 31) {
		i |= (0xffffffff >>> maskbits) >>> 32;
		--i;
    n[0] = (i >>> 24)        >>> 32;
    n[1] = (i >>  16 & 0xff) >>> 32;
   	n[2] = (i >>  8  & 0xff) >>> 32;
    n[3] = (i        & 0xff) >>> 32;
    output += '<td class="label">Last Host</td>';
		output += sprintf('<td class="infol">   %3d   .  %3d   .  %3d   .  %3d </td><td class="infol"> %d.%d.%d.%d </td>',
		                  n[0], n[1], n[2], n[3],
		                  n[0], n[1], n[2], n[3]);
	}	else {
		output += '<td class="label">Last Host</td><td class="infol">None, mask >= 31</td>';
	}

	output += '</tr><tr>';

	output += '<td class="label">Total Hosts</td>';
	if(maskbits != 0 && maskbits <= 30) {
		i = ((1 << (32 - maskbits)) - 2) >>> 32;
		output += sprintf('<td class="infol"> %d</td>', i);
	}	else if(maskbits == 32) {
		output += '<td class="infol"> 1</td>';
	}	else if(maskbits == 0) {
		output += '<td class="infol"> None, default route</td>';
	}	else {
		output += '<td class="infol"> None, mask >= 31</td>';
	}
	output += '<td></td>';
	
	output += '</tr><tr>';

	output += '<td class="label">PTR</td>';
	output += sprintf('<td class="infol"> %d.%d.%d.%d.in-addr.arpa</td>',
	                   ia[3], ia[2], ia[1], ia[0]);
	output += '<td></td>';

	output += '</tr><tr>';

	// Javascript's toString only handles up to 2147483647 (2^31-1) 
	// correctly, so we have to do some magic here...
	// first get the low 7 bits
	if(ipaddr > 2147483647) {
		var gruesome_hack = (ipaddr - 2147483648);
		// then get the hi bit
		var gruesome_hack_hi = (ia[0] >>> 32);

		output += '<td class="label">IP Address (hex)</td>';
		output += sprintf('<td class="infol"> 0x%s%s</td>',
		                  gruesome_hack_hi.toString(16).substring(0,1),
		                  gruesome_hack.toString(16).substring(1,8));
	} else {
		var ip_hex = ipaddr.toString(16);
		output += '<td class="label">IP Address (hex)</td>';
		output += sprintf('<td class="infol"> 0x%s%s</td>',
		                  ((ip_hex.length > 7) ? '' : '0'),
		                  ip_hex);
	}	
	
	output += '<td></td>';
	output += '</tr></table></body></html>';
	
	var d = top.out.document;
	d.open("text/html");
	//d.write("<form><textarea rows=8 cols=80>");
	d.write(output);
	//d.write("</textarea></form>");
	d.close();
}

function sprintf(arg) {
	var i, c, c2, c3, ww, pointer = 1, w = 0, d = 0, left, period;
	var result = "";
	var format = sprintf.arguments[0];
	for (i = 0; i < format.length; i++) {
		c = format.charAt(i);
		if (c == "\n") {
			if (result == "") {
				result = " ";
			}
			result += "\n";
		} else if (c == "%") {
			left = 0;
			i++;
			c2 = format.charAt(i);
			if (c2 == "%") {
				result += c2;
				continue;
			} else if (c2 == "-") {
				left = 1;
				c2 = format.charAt(++i);
			}
			if (is_digit(c2)) {
				period = 0;
				w = 0;
				d = 0;
				while ((c3 = format.charAt(i)) != "s" && c3 != "f" && c3 != "g" && c3 != "i" && c3 != "d") {
					if (c3 == ".") {
						period = 1;
					} else if (is_digit(c3) == 0) {
						alert("non digit after % ("+c3+")");
					} else if (period == 0) {
						w = w*10+eval(c3);
					} else if (period == 1) {
						d = d*10+eval(c3);
					}
					i++;
				}
				if (c3 == "s") {
					result += print_string(left, w, sprintf.arguments[pointer++]);
				}
				else if (c3 == "g" || c3 == "f") {
					result += print_number(c3, w, d, sprintf.arguments[pointer++]);
				}
				else if (c3 == "i" || c3 == "d") {
					result += print_number(c3, w, 0, sprintf.arguments[pointer++]);
				}
			} else if (c2 == "s") {
				result += print_string(left, 0, sprintf.arguments[pointer++]);
			}
			else if (c2 == "f") {
				result += print_number(c2, 0, 3, sprintf.arguments[pointer++]);
			}
			else if (c2 == "g") {
				result += print_number(c2, 0, 6, sprintf.arguments[pointer++]);
			}
			else if (c2 == "i" || c2 == "d") {
				result += print_number(c2, 0, 0, sprintf.arguments[pointer++]);
			}
		} else {
			result += c;
		}	
	}
	return result;
}

function is_digit(c) {
	return "0123456789.".indexOf(c) == -1 ? 0 : 1;
}

function print_string(left, w, arg) {
	var ww, temp, s = "";
	if (w != 0) {
		temp = "" + arg;
		ww = temp.length;
		if (left == 1) {
			if (ww > w) {
				s += arg;
			} else {
				s += arg+string_constant_blank.substring(0, w-ww);
			}
		}	else {
			if (ww > w) {
				s += arg;
			} else {
				s += string_constant_blank.substring(0, w-ww)+arg;
			}
		}
	} else {
		s += arg;
	}
	return s;
}

function print_number(gf, w, d, number) {
	var s = "";
	if (gf == "g") {
		s += print_number_g(w, d, number);
	}
	else if (gf == "f") {
		s += print_number_f(w, d, number);
	}
	else if (gf == "i" || gf == "d") {
		s += print_number_i(w, d, number);
	}
	return s;
}

function print_number_i(w, d, number) {
	var s, p, ww;
	s = fixed2(number, d);
	ww = (""+s).length;
	if (ww < w) {
		s = string_constant_blank.substring(0, w-ww)+s;
	}
	return s;
}

function print_number_f(w, d, number)	{
	var s, p, ww;
	s = fixed2(number, d);
	if ((""+s).charAt(0) == ".") {
		s = "0"+s;
	}	else if ((""+s).indexOf("-.") != -1) {
		s = "-0"+(""+s).substring(1, (""+s).length);
	}
	if ((""+s).indexOf(".") == -1) {
		s += ".";
	}
	ww = (""+s).length-1;
	p = (""+s).indexOf(".");
	 
	if (ww-p < d) {
		s += string_constant_zero.substring(0, d-(ww-p));
	}

	ww = (""+s).length;
	if (ww < w) {
		s = string_constant_blank.substring(0, w-ww)+s;
	}
	return s;
}

function print_number_g(w, d, number)	{
	var abs = Math.abs(number);
	if (number == 0 || (0.001 <= abs && abs < Math.pow(10, d))) {
		return print_number_g2(w, d, number);
	}	else {
		return print_number_e(w, d, number);
	}
}

function print_number_g2(w, d, number) {
	var s, p, ww, abs, temp
	abs = Math.abs(number)
	if (number == 0) {
		temp = 1;
	} else if (abs >= 1) {
		temp = d-(""+Math.floor(abs)).length;
	}	else {
		temp = d-1;
		while (abs < 1) {
			temp++;
			abs *= 10;
		}
	}
	s = fixed2(number, temp);
	if ((""+s).charAt(0) == ".") {
		s = "0"+s;
	}	else if ((""+s).indexOf("-.") != -1) {
		s = "-0"+(""+s).substring(1, (""+s).length);
	}
	if ((""+s).indexOf(".") == -1) {
		s += ".";
	}
	ww = (""+s).length;
	ww -= (number < 0 ? 1 : 0)+(Math.abs(number) < 1 ? 1 : 0)+1;
	if (ww < d) {
		s += string_constant_zero.substring(0, d-ww);
	}

	ww = (""+s).length;
	if (ww < w) {
		s = string_constant_blank.substring(0, w-ww)+s;
	}
	return s;
}

function print_number_e(w, d, number) {
	var s, p, ww, exponent, sign, abs, temp;
	w -= 4;
	exponent = 0;
	abs = Math.abs(number);
	while (abs < 1) {
		exponent--;
		abs *= 10;
	}
	while (abs >= 10) {
		exponent++;
		abs /= 10;
	}
	temp = (number < 0 ? -1 : 1)*abs;
	return print_number_f(w, d-1, temp)+"e"+(exponent < 0 ? "-" : "+")+rec(exponent);
}

function fixed2(x, d) {
	return Math.round(x*Math.pow(10,d))/Math.pow(10,d);
}

function getbits(b) {
	var s = "";
	s += ((b &  0x80) ? 1 : 0);  // 128
	s += ((b &  0x40) ? 1 : 0);  // 64
	s += ((b &  0x20) ? 1 : 0);  // 32
	s += ((b &  0x10) ? 1 : 0);  // 16
	s += ((b &  0x08) ? 1 : 0);  // 8
	s += ((b &  0x04) ? 1 : 0);  // 4
	s += ((b &  0x02) ? 1 : 0);  // 2
	s += ((b &  0x01) ? 1 : 0);  // 1
	return s;
}


