<%@ page language="java" import="java.io.*, java.util.*, com.cisco.ipphone.sdk.*, javax.naming.*, javax.naming.directory.*" %>
<%
///////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Title:  		Click2Dial
// Author: 		kstearns
// Source File(s):  	click2dial.jsp
//
///////////////////////////////////////////////////////////////////////////////////////////////////////
// Description:
//
// Click2Dial accesses an LDAP directory and generates a table of users with their directory
// information. The page returns the directory numbers as hyperlinks which allows you to click
// on a users phone number and your phone automatically goes off-hook (speakerphone) and dials.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////
// Requirements and Caveats:
//
// -  Client: HTML Browser (PC-based web browser)
//
// -  Click2Dial uses the source IP address of the web browser request (your PC) to query against the
//    LDAP directory. It first finds the User with that Associated PC and then finds the first
//    'hard' IP phone associated with that User. Once it has the target IP phone for dialing,
//    the script HTTP PUSHes a URI Dial: command to the phone to dial the number.
//    This means that the PC accessing the Click2Dial.jsp script MUST have been defined as an
//    Associated PC for a user AND that user must have an associated phone.
//
// - In order for Click2Dial to work, the PUSH userID and PUSH password hard-coded below
//   MUST have authority to PUSH to all phones used by the application.
//   For example, a new application user ID of 'sdkapps' could be created in the Global Directory.
//   All phones using the application are then selected as Associated Devices for user 'sdkapps'.
//
//   NOTE: For large-scale PUSH applications, associating hundreds or even thousands of devices to a single user
//         (for purposes of PUSH authentication) is *NOT ACCEPTABLE*.
//         In this case, the PUSH authentication *MUST* be offloaded from the CallManager server to an external
//         web server - see the PushAuthenticate ASP sample script for more information.
//
// - Click2Dial is Extension-Mobility aware in that it will first check to see if the user is
//   logged into a phone via EM. If they are, that phone is used to dial instead of their statically
//   associated phone.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////

//
//  Edit the following Strings for your environment.
//  This is for sample purposes only - placing passwords and other
//  sensitive information in an open text file is obviously NOT recommended !!!
//

  String callManager = "10.0.0.1";
  String callManagerUserId = "administrator";
  String callManagerPassword = "cisco";

  String ldapServer = "10.0.0.1";
  int ldapPort = 8404;
  String ldapRoot = "o=cisco.com";
  String ldapUserId = "cn=Directory Manager, o=cisco.com";
  String ldapPassword = "cisco";
  String ldapSearchbase = "ou=Users";

  String pushUserId = "sdkapps";
  String pushPassword = "sdkapps";

  String extMobUserId = "sdkapps";
  String extMobPassword = "sdkapps";

/////////////////////////////////////////////////////////////////////

  // Create Service Providers needed for accessing LDAP, AXL, and Extension Mobility APIs

  LDAPProvider lp = new LDAPProvider(ldapServer, ldapUserId, ldapPassword, ldapPort, ldapRoot);
  
  // The old IPAddressProvider was used on older CallManager servers that only supported the DeviceListX page
  // Recent CUCM versions support the new RIS interface for realtime information
  // IPAddressProvider ipp = new IPAddressProvider(callManager, callManagerUserId, callManagerPassword);
  IPAddressProviderIF ipp = new RisProvider(callManager, callManagerUserId, callManagerPassword);
 
  EMProvider emp = new EMProvider(callManager, callManagerUserId, callManagerPassword, extMobUserId, extMobPassword);

  String pushauth = Text2Base64.getBase64(pushUserId + ":" + pushPassword);

  String number = request.getParameter("number");

  %>
  <html>
  <head><h2>Click2Dial Web Directory</h2></head>
  <body><br><br>
  <%

  ///////////////////////////////////////////////////////////
  // If the 'number' is null then list the directory
  ///////////////////////////////////////////////////////////

  if (number == null)
  {
    %>
    <table border='1'>
    <tr>
    <td width='150'><h2>Last</h2></td>
    <td width='150'><h2>First</h2></td>
    <td width='150'><h2>UserID</h2></td>
    <td width='150'><h2>Department</h2></td>
    <td width='150'><h2>Extension</h2></td>
    </tr>
    <%

    // Query the LDAP Directory for all Users, returning all attributes

    Vector users = lp.sendRequest(ldapSearchbase, "(cn=*)", true, null);

    //  Step thru each user in the response and output to the response HTML page

    for (int i=0; i < users.size(); i++) {
      HashMap userAttrs = (HashMap)users.get(i);

      out.print("<tr><td width='150'>");
      if (userAttrs.get("sn")!=null) {
        out.print((String)((Vector)userAttrs.get("sn")).get(0));
      }
      out.print("</td><td width='150'>");
      if (userAttrs.get("givenName")!=null) {
        out.print((String)((Vector)userAttrs.get("givenName")).get(0));
      }
      out.print("</td><td width='150'>" + (String)((Vector)userAttrs.get("cn")).get(0) + "</td>");
      out.println("<td width='150'>");
      if (userAttrs.get("departmentNumber")!=null) {
        out.print((String)((Vector)userAttrs.get("departmentNumber")).get(0));
      }
      out.print("&nbsp</td>");
      out.println("<td width='150'>");

      // If a telephoneNumber exists for the user, then display it as a hyperlink which points back to
      // this page with a querystring parameter 'number' containing the number to dial.

      if (userAttrs.get("telephoneNumber")!=null) {
        out.print("<a href=click2dial.jsp?number=" + (String)((Vector)userAttrs.get("telephoneNumber")).get(0) + ">" + (String)((Vector)userAttrs.get("telephoneNumber")).get(0) + "</a>");
      }
      out.print("&nbsp</td>");
    }

    %>
    </table>
    </body>
    <%
  }

  // If a 'number' is present then dial the number

  else
  {
    PC thePC = PC.getPC(request.getRemoteAddr());

    response.setHeader("Refresh", "3; URL=click2dial.jsp");

    //  First we must find the user in the LDAP directory with this associated PC (browser IP address).
    //  Notice that this assumes the LDAP directory contains IP addresses not hostnames - if
    //  you are using DHCP and configuring hostnames in the Associated PC field in CallManager, then
    //  you would need to do an inverse DNS lookup at this point to get the hostname based on that IP.

    //  The user associated with this Associated PC could have multiple devices (phones) associated to them -
    //  the "SEP" beginsWith parameter ensures that the first "hard-phone" is picked.
    //  This string may need to be modified depending upon your device naming conventions - a recommended
    //  practice would be to prefix the devicename with some string which indicates this is a primary phone
    //  for a user. This ensures proper behavior of this script.

    String[] excludedUsers = { pushUserId };
    User[] users = thePC.getAssociatedUsers(lp, null);

    if (users.length == 0) {
      out.println("ERROR: Associated PC with IP address " + thePC.getId() + " could not be found in LDAP directory.");
    }
    else {
      User theUser = users[0];

      // get the name of the Phone the User is currently logged in to
      Phone thePhone = theUser.getCurrentExtMobPhone(emp);

      // if the User is not logged-in via Extension Mobility, then search LDAP for controlled phones
      if (thePhone == null) {
        // find first associated device which begins with 'SEP' - this should be 'hard' phone
        Phone[] phones = theUser.getControlledPhones(lp, "SEP");
        // if a Controlled Phone was found, then use that - otherwise thePhone stays null
        if (phones.length != 0) {
          thePhone = phones[0];
        }
      }

      // if a valid Phone was not found, then return an error message
      if (thePhone == null) {
        out.println("ERROR: No associated device beginning with 'SEP' found for user " + theUser.getUserId() + ".");
      }
      else {
          //  Now that we know the name of the phone to dial from, get the current active IP address
          //  from the devicelistx.asp API running on CallManager - via the IPAddressProvider

          String phoneIP = ipp.getIPAddress(thePhone);

          if (phoneIP == null) {
            out.println("ERROR: Unable to find current IP address for device " + phoneIP + ".");
          }
          else {
            //  Now that we have the IP address of the phone, PUSH a Dial URI command to it

            String result = Phone.push( "Dial:"+number, phoneIP, pushUserId, pushPassword, false);

            //  And report that it was done.
            //  Refresh header in the beginning will redirect back to directory list in 3 seconds

            out.print("<br><b>Dial command sent to " + thePhone.getDeviceName() + " at " + phoneIP + ".");
          }
        }
      }
    }
  %>
  </html>


<%
//  THIS SAMPLE APPLICATION AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND BY CISCO, 
//  EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY
//  FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, SATISFACTORY QUALITY OR ARISING FROM A COURSE
//  OF DEALING, LAW, USAGE, OR TRADE PRACTICE.  CISCO TAKES NO RESPONSIBILITY REGARDING ITS USAGE IN AN
//  APPLICATION., THE APPLICATION IS PROVIDED AS AN EXAMPLE ONLY, THEREFORE CISCO DOES NOT MAKE ANY
//  REPRESENTATIONS REGARDING ITS RELIABILITY, SERVICEABILITY, OR FUNCTION.  IN NO EVENT DOES CISCO
//  WARRANT THAT THE SOFTWARE IS ERROR FREE OR THAT CUSTOMER WILL BE ABLE TO OPERATE THE SOFTWARE WITHOUT
//  PROBLEMS OR INTERRUPTIONS.  NOR DOES CISCO WARRANT THAT THE SOFTWARE OR ANY EQUIPMENT ON WHICH THE
//  SOFTWARE IS USED WILL BE FREE OF VULNERABILITY TO INTRUSION OR ATTACK.  THIS SAMPLE APPLICATION IS
//  NOT SUPPORTED BY CISCO IN ANY MANNER. CISCO DOES NOT ASSUME ANY LIABILITY ARISING FROM THE USE OF THE
//  APPLICATION. FURTHERMORE, IN NO EVENT SHALL CISCO OR ITS SUPPLIERS BE LIABLE FOR ANY INCIDENTAL OR
//  CONSEQUENTIAL DAMAGES, LOST PROFITS, OR LOST DATA, OR ANY OTHER INDIRECT DAMAGES EVEN IF CISCO OR ITS
//  SUPPLIERS HAVE BEEN INFORMED OF THE POSSIBILITY THEREOF.
%>