gt:       if (fs != null) {
context: package jpbx.core;
  
  /**
   * WebConfig service for jPBXlite
   *
   * @author pquiring
   *
   * Created : Sept 14, 2013
   */
  
  import java.io.*;
  import java.net.*;
  import java.util.*;
  
  import javaforce.*;
  import javaforce.service.*;
  
  public class WebConfig implements WebHandler {
    public static int http_port, https_port, sip_port;
    public static boolean hideAdmin, disableWebRTC;
    private static String dbVersion = "0.9";
    private WebRTC webrtc;
    private Web http, https;
  
    public boolean start() {
      JFLog.log("Starting Web Server on port " + http_port);
      http = new Web();
      http.start(this, http_port, false);
      if (new File(Paths.etc + "jpbx.key").exists()) {
        System.setProperty("javax.net.ssl.keyStore", Paths.etc + "jpbx.key");
        System.setProperty("javax.net.ssl.keyStorePassword", "password");
        JFLog.log("Starting Web Server on port " + https_port + " (secure)");
        https = new Web();
        https.start(this, https_port, true);
        if (!disableWebRTC) startWebRTC();
      }
      return true;
    }
  
    private void startWebRTC() {
      webrtc = new WebRTC();
      webrtc.init();
      http.setWebSocketHandler(webrtc);
      if (https != null) https.setWebSocketHandler(webrtc);
    }
  
    private String decode(String in) {
      try {return URLDecoder.decode(in, "UTF-8");} catch (Exception e) {}
      return "";
    }
  
    public void doPost(WebRequest req, WebResponse res) {
      try {doRequest(req, res);} catch (Exception e) {JFLog.log(e);}
    }
  
    public void doGet(WebRequest req, WebResponse res) {
      try {doRequest(req, res);} catch (Exception e) {JFLog.log(e);}
    }
  
    private HashMap<String, byte[]> cache = new HashMap<String, byte[]>();
  
    public void doStatic(WebRequest req, WebResponse res) throws Exception {
      String url = req.getURL();
      int idx = url.lastIndexOf('/');
      String file = url.substring(idx+1);
      byte data[];
      if (file.endsWith(".class")) {
        res.setStatus(404, "Not Found");
        return;
      }
      if (file.endsWith(".css")) {
        res.setContentType("text/css");
      }
      else if (file.endsWith(".js")) {
        res.setContentType("text/javascript");
      }
      else if (file.endsWith(".png")) {
        res.setContentType("image/png");
      }
      else if (file.endsWith(".txt")) {
        res.setContentType("text/plain");
      }
      data = (byte[])cache.get(file);
      if (data != null) {
        res.getOutputStream().write(data);
        return;
      }
      data = JF.readAll(getClass().getClassLoader().getResourceAsStream(file));
      if (data == null || data.length == 0) {
        res.setStatus(404, "Not Found");
        return;
      }
      cache.put(file, data);
      res.getOutputStream().write(data);
    }
  
    private void writeHeaders(OutputStream os) throws Exception {
      StringBuilder html = new StringBuilder();
      html.append("<html>");
      html.append("<head>");
      html.append("  <title>jPBXlite</title>");
      html.append("  <link rel=stylesheet href='/static/style.css' type='text/css'>");
      html.append("  <script type='text/javascript' src='/static/style.js'></script>");
      html.append("</head>");
      html.append("<body leftmargin=1 rightmargin=1 topmargin=1 bottommargin=1 vlink=ffffff link=ffffff alink=ffffff width=100% height=100%>");
      os.write(html.toString().getBytes());
    }
  
    public void doIndex(WebRequest req, WebResponse res) throws Exception {
      writeHeaders(res.getOutputStream());
      StringBuilder html = new StringBuilder();
      if (!hideAdmin) {
        html.append("<div style='float:right;'><a href='/login'>Admin</a></div>");
      }
      html.append("<center>");
      html.append("<img src='/static/logo.png'><br>");
      html.append("<br>");
      if (!disableWebRTC) {
        html.append("<t3>WebRTC Conferencing (experimental)</t3><br>");
        html.append("<form action=webrtc1 method=get>");
        html.append("Room Number:<input name=room size=10>");
        html.append("<input type=submit value='Enter Room'> (2 per room)<br><br><br>");
        html.append("</form>");
        html.append("<t3>WebRTC Phone (experimental)</t3><br>");
        html.append("<form action='https://" + req.getHost() + ":" + https_port + "/webrtc2' method=post>");
        html.append("Your Number:<input name=user size=10><br>");
        html.append("Password:<input type=password name=pass size=10><br>");
        html.append("Dial Number:<input name=dial size=10><br>");
        html.append("<input type=submit value='Make Call'>");
        html.append("</form>");
      }
      res.getOutputStream().write(html.toString().getBytes());
    }
  
    public String ValidUser(String user, String passmd5) {
      SQL sql = new SQL();
      if (!sql.init()) return "Unable to connect to database";
      String res = sql.select1value("SELECT passmd5 FROM users WHERE userid='" + user + "'");
      sql.uninit();
      if (res == null) return "Database not setup";
      return (res.equalsIgnoreCase(passmd5) ? "ok" : "Invalid Login");
    }
  
    public void doLogin(WebRequest req, WebResponse res) throws Exception {
      writeHeaders(res.getOutputStream());
      String query = req.getQueryString();
      String args[];
      if (query != null) args = query.split("&"); else args = new String[0];
      String user = "", pass = "";
      for(int a=0;a<args.length;a++) {
        if (args[a].startsWith("user=")) user = args[a].substring(5);
        if (args[a].startsWith("pass=")) pass = args[a].substring(5);
      }
      String msg = "";
      if ((user.length() > 0 && pass.length() > 0)) {
        //attempt login
        MD5 md5 = new MD5();
        md5.init();
        md5.add(pass.getBytes(),0,pass.length());
        String passmd5 = new String(md5.byte2char(md5.done()));
        //check database
        msg = ValidUser(user, passmd5);
        if (msg.equals("ok")) {
          req.session.setAttribute("id", user);
          res.sendRedirect("/admin");
          return;
        }
      }
      StringBuilder html = new StringBuilder();
  
      html.append("<center>");
      html.append("<div style='background-color: #dddddd; width: 300px;'>");
      if (msg.length() > 0) {
        html.append("<font color=#ff0000>");
        html.append(msg);
        html.append("<br></font>");
      }
      html.append("<form action='/login'>");
      html.append("Username : <input name=user><br>");
      html.append("Password : <input type=password name=pass><br>");
      html.append("<input type=submit value='Login'>");
      html.append("</form>");
      html.append("</div>");
  
      html.append("<br><br>");
  
      res.getOutputStream().write(html.toString().getBytes());
    }
  
    public void doLogout(WebRequest req, WebResponse res) throws Exception {
      req.session.setAttribute("id", null);
      res.sendRedirect("/");
    }
  
    private String doAdminPage(SQL sql, String args[]) {
      String verb = "", current = "", p1 = "", p2 = "";
      for(int a=0;a<args.length;a++) {
        if (args[a].startsWith("verb=")) verb = args[a].substring(5);
        if (args[a].startsWith("current=")) current = args[a].substring(8);
        if (args[a].startsWith("p1=")) p1 = args[a].substring(3);
        if (args[a].startsWith("p2=")) p2 = args[a].substring(3);
      }
      String msg = "";
      if (verb.equals("changepass")) {
        if (!p1.equals(p2)) {
          msg = "Passwords do not match!";
        } else if (p1.length() < 5) {
          msg = "Password too short (min = 5)";
        } else {
          MD5 md5 = new MD5();
          md5.init();
          md5.add(current.getBytes(),0,current.length());
          String currentmd5 = new String(md5.byte2char(md5.done()));
          String storedmd5 = sql.select1value("SELECT passmd5 FROM users WHERE userid='admin'");
          if ((storedmd5 == null) || (!storedmd5.equals(currentmd5))) {
            msg = "Incorrect current password";
          } else {
            md5.init();
            md5.add(p1.getBytes(),0,p1.length());
            String passmd5 = new String(md5.byte2char(md5.done()));
            if (!sql.execute("UPDATE users SET passmd5=" + sql.quote(passmd5) + " WHERE userid='admin'")) {
              msg = "Unable to change password (DB error)";
            } else {
              msg = "Password changed";
            }
          }
        }
      }
      StringBuilder html = new StringBuilder();
      html.append("<center>");
      html.append("Change Admin Password:<br>");
      html.append("<div style='background-color: #dddddd; width: 300px;'>");
      if (msg.length() > 0) {
        html.append("<font color=#ff0000>");
        html.append(msg + "<br>");
        html.append("</font>");
      }
      html.append(form("core", "admin"));
      html.append("<input type=hidden name=verb value='changepass'>");
      html.append("<table>");
      html.append("<tr><td>Current Password: </td><td> <input type=password name=current></td></tr>");
      html.append("<tr><td>New Password : </td><td> <input type=password name=p1></td></tr>");
      html.append("<tr><td>Confirm Password : </td><td> <input type=password name=p2></td></tr>");
      html.append("</table>");
      html.append("<input type=submit value=Change>");
      html.append("</form>");
      html.append("</div>");
  
      return html.toString();
    }
  
    private String doBlankPage(SQL sql, String args[]) {
      return "Please select an option on the left.";
    }
  
    private String doConfPage(SQL sql, String args[]) {
      return "Conferences are special IVRs.  To create a conference, create a new IVR and load the preset \"Conference\".<br>\n" +
             "To create a video conference select the \"Video Conference\" preset.<br>\n" +
             "Then change the $adminpass and $userpass variables for the admin and user pass codes.<br>\n";
    }
  
    private String doExtsPage(SQL sql, String args[]) {
      String verb = "", ext = "", editext = "", cloneStart = "", cloneCount = "", display = "", cid = "", pass = "", msg = "", sure = "", vm = "", vmpass = "";
      for(int a=0;a<args.length;a++) {
        int x = args[a].indexOf("=") + 1;
        if (args[a].startsWith("verb=")) verb = args[a].substring(x);
        if (args[a].startsWith("ext=")) ext = args[a].substring(x);
        if (args[a].startsWith("editext=")) editext = args[a].substring(x);
        if (args[a].startsWith("cloneStart=")) cloneStart = args[a].substring(x);
        if (args[a].startsWith("cloneCount=")) cloneCount = args[a].substring(x);
        if (args[a].startsWith("display=")) display = args[a].substring(x);
        if (args[a].startsWith("cid=")) cid = args[a].substring(x);
        if (args[a].startsWith("pass=")) pass = args[a].substring(x);
        if (args[a].startsWith("sure=")) sure = args[a].substring(x);
        if (args[a].startsWith("vm=")) vm = args[a].substring(x);
        if (args[a].startsWith("vmpass=")) vmpass = args[a].substring(x);
      }
      ext = numbersOnly(ext);
      cid = numbersOnly(cid);
      display = convertString(display);
      vmpass = numbersOnly(vmpass);
      if (verb.equals("del")) {
        if (sure.equalsIgnoreCase("on")) {
          if (sql.execute("DELETE FROM exts WHERE ext=" + sql.quote(ext))) {
            if (!sql.execute("DELETE FROM extopts WHERE ext=" + sql.quote(ext))) {
              msg = "Failed to delete extension options";
            }
          } else {
            msg = "Failed to delete extension";
          }
          if (msg.length() == 0) {
            msg = "Extension deleted";
          }
        } else {
          msg = "Please confirm delete action";
        }
        ext = "";
      }
      if (verb.equals("view")) {
        editext = ext;
        String exts[] = sql.select1row("SELECT display,cid,pass FROM exts WHERE ext=" + sql.quote(ext));
        if (exts != null) {
          display = exts[0];
          cid = exts[1];
          pass = exts[2];
          vm = sql.select1value("SELECT value FROM extopts WHERE ext=" + sql.quote(ext) + " AND id='vm'");
          if (vm == null) vm = "false";
          vmpass = sql.select1value("SELECT value FROM extopts WHERE ext=" + sql.quote(ext) + " AND id='vmpass'");
          if (vmpass == null) vmpass = "";
        }
      }
      if (verb.equals("add") || verb.equals("edit")) {
        if (ext.length() == 0)  {
          msg = "Invalid extension number";
        } else {
          String isivr = sql.select1value("SELECT ext FROM ivrs WHERE ext=" + sql.quote(ext));
          if ((isivr != null) && (isivr.equals(ext))) {
            msg = "IVR already exists with that number";
          }
        }
        if (display.length() == 0)  {
          msg = "Invalid display name";
        }
        if (pass.length() == 0)  {
          msg = "Invalid password";
        }
        if (msg.length() > 0) {
          if (verb.equals("add")) verb = ""; else verb = "view";
        }
      }
      if (verb.equals("add")) {
        if (!sql.execute("INSERT INTO exts (ext, display, cid, pass) VALUES (" + sql.quote(ext) + "," + sql.quote(display) + "," + sql.quote(cid) + "," + sql.quote(pass) + ")")) {
          msg = "Failed to add : " + sql.lastException.toString();
        } else if (!sql.execute("INSERT INTO extopts (ext, id, value) VALUES (" + sql.quote(ext) + ",'vm'," + sql.quote( (vm.equalsIgnoreCase("on") ? "true" : "false") ) + ")")) {
          msg = "Unable to add voicemail option";
        } else if (!sql.execute("INSERT INTO extopts (ext, id, value) VALUES (" + sql.quote(ext) + ",'vmpass'," + sql.quote(vmpass) + ")")) {
          msg = "Unable to add voicemail password";
        }
        if (msg.length() == 0) {
          msg = "Extension added";
          ext = "";
          display = "";
          cid = "";
          pass = "";
          vm = "";
          vmpass = "";
        }
      }
      if (verb.equals("edit")) {
        if (!sql.execute("UPDATE exts SET ext=" + sql.quote(ext) + ",display=" + sql.quote(display) + ",cid=" + sql.quote(cid) + ",pass=" + sql.quote(pass) + " WHERE ext=" + sql.quote(editext))) {
          msg = "Failed to edit : " + sql.lastException.toString();
        } else if (!sql.execute("UPDATE extopts SET ext=" + sql.quote(ext) + ",value=" + sql.quote( (vm.equalsIgnoreCase("on") ? "true" : "false") ) + " WHERE id='vm' AND ext=" + sql.quote(editext))) {
          msg = "Unable to edit voicemail option";
        } else if (!sql.execute("UPDATE extopts SET ext=" + sql.quote(ext) + ",value=" + sql.quote(vmpass) + " WHERE id='vmpass' AND ext=" + sql.quote(editext))) {
          msg = "Unable to edit voicemail password";
        }
        if (msg.length() == 0) {
          msg = "Extension edited";
          ext = "";
          display = "";
          cid = "";
          pass = "";
          vm = "";
          vmpass = "";
        } else {
          verb = "view";
        }
      }
      if (verb.equals("clone")) {
        verb = "";
        String exts[] = sql.select1row("SELECT cid,pass FROM exts WHERE ext=" + sql.quote(ext));
        cid = exts[0];
        pass = exts[1];
        vm = sql.select1value("SELECT value FROM extopts WHERE ext=" + sql.quote(ext) + " AND id='vm'");
        if (vm == null) vm = "false";
        vmpass = sql.select1value("SELECT value FROM extopts WHERE ext=" + sql.quote(ext) + " AND id='vmpass'");
        if (vmpass == null) vmpass = "";
        try {
          int start = Integer.valueOf(cloneStart);
          int count = Integer.valueOf(cloneCount);
          if (count <=0 || count > 1000) throw new Exception("max 1000 clones");
          int added = 0;
          for(int idx=start;count > 0;count--,idx++) {
            ext = "" + idx;
            display = "" + idx;
            msg = "";
            if (!sql.execute("INSERT INTO exts (ext, display, cid, pass) VALUES (" + sql.quote(ext) + "," + sql.quote(display) + "," + sql.quote(cid) + "," + sql.quote(pass) + ")")) {
              msg = "Failed to add";
            } else if (!sql.execute("INSERT INTO extopts (ext, id, value) VALUES (" + sql.quote(ext) + ",'vm'," + sql.quote(vm) + ")")) {
              msg = "Unable to add voicemail option";
            } else if (!sql.execute("INSERT INTO extopts (ext, id, value) VALUES (" + sql.quote(ext) + ",'vmpass'," + sql.quote(vmpass) + ")")) {
              msg = "Unable to add voicemail password";
            }
            if (msg.length() == 0) added++;
          }
          msg = "Cloning complete (" + added + " extensions added)";
        } catch (Exception e) {
          msg = "Cloning failed:" + e.toString();
        }
        ext = "";
        display = "";
        cid = "";
        pass = "";
        vm = "";
        vmpass = "";
      }
      StringBuilder html = new StringBuilder();
  
      html.append("<table height=100%>");
      html.append("<tr><td width=100%>");
      if (msg.length() > 0) html.append("<font color=#ff0000>" + msg + "</font><br>");
      if (verb.equals("view")) {
        html.append(form("core", "exts"));
        html.append("<input type=hidden name=verb value=del><input type=hidden name=ext value=" + sql.quote(ext) + ">");
        html.append("<input type=submit value='Delete Extension'> <input type=checkbox name=sure>I'm Sure</form>");
      }
      html.append(form("core", "exts"));
      if (verb.equals("cloneForm")) {
        html.append("<input type=hidden name=verb value=clone>");
        html.append("<table>");
        html.append("<tr><td>Extension #:</td><td><input name=ext value=" + sql.quote(ext) + " readonly></td></tr>");
        html.append("<tr><td>Start:</td><td><input name=cloneStart></td></tr>");
        html.append("<tr><td>Count:</td><td><input name=cloneCount></td></tr>");
        html.append("<tr><td colspan=2>NOTE : Existing extension in clone range may be altered!</td></tr>");
        html.append("</table>");
        html.append("<input type=submit value=Clone>");
      } else {
        html.append("<input type=hidden name=verb value=" + (verb.equals("view") ? "edit" : "add") + ">");
        if (verb.equals("view")) html.append("<input type=hidden name=editext value=" + sql.quote(editext) + ">");
        html.append("<table>");
        html.append("<tr><td>Extension #:</td><td><input name=ext value=" + sql.quote(ext) + "></td><td>");
        if (verb.equals("view")) {
          html.append(link("core", "exts", "verb=cloneForm&ext=" + editext, "Clone"));
        }
        html.append("</td></tr>");
        html.append("<tr><td>Display Name:</td><td><input name=display value=" + sql.quote(display) + "></td><td>(usually same as extension #)</td></tr>");
        html.append("<tr><td>Outbound Caller ID #:</td><td><input name=cid value=" + sql.quote(cid) + "></td><td>(optional) (used if call sent to trunk)</td></tr>");
        html.append("<tr><td>SIP Password:</td><td><input name=pass value=" + sql.quote(pass) + "></td></tr>");
        html.append("<tr><td>VoiceMail:</td><td><input type=checkbox name=vm " + (vm.equals("true") ? "checked" : "") + "></td></tr>");
        html.append("<tr><td>VM Password:</td><td><input name=vmpass value=" + sql.quote(vmpass) + "></td></tr>");
        html.append("</table>");
        html.append("<input type=submit value=" + (verb.equals("view") ? "Edit" : "Add") + ">");
      }
      html.append("</form>");
      html.append("</td><td width=2 bgcolor=#000000></td><td width=180>");
      html.append("<div class=menuitem>" + link("core", "exts", "", "Add New") + "</div>");
      String exts[][] = sql.select("SELECT ext,display FROM exts ORDER BY ext");
      if (exts != null) {
        for(int a=0;a<exts.length;a++) {
          html.append("<div class=menuitem>" + link("core", "exts", "verb=view&ext=" + exts[a][0], exts[a][1] + "&lt;" + exts[a][0] + "&gt;") + "</div>");
        }
      }
      html.append("</td></tr>");
      html.append("</table>");
  
      return html.toString();
    }
  
    private String doInRoutesPage(SQL sql, String args[]) {
      StringBuilder html = new StringBuilder();
  
      String verb = "", route = "", cid = "", did = "", dest = "", msg = "", editroute= "", sure = "";
      for(int a=0;a<args.length;a++) {
        int x = args[a].indexOf("=") + 1;
        if (args[a].startsWith("verb=")) verb = args[a].substring(x);
        if (args[a].startsWith("route=")) route = args[a].substring(x);
        if (args[a].startsWith("editroute=")) editroute = args[a].substring(x);
        if (args[a].startsWith("cid=")) cid = args[a].substring(x);
        if (args[a].startsWith("did=")) did = args[a].substring(x);
        if (args[a].startsWith("dest=")) dest = args[a].substring(x);
        if (args[a].startsWith("sure=")) sure = args[a].substring(x);
      }
      cid = numbersOnly(cid);
      did = numbersOnly(did);
      dest = numbersOnly(dest);
  
      if (route.length() == 0) verb = "";
      if (verb.equals("del")) {
        if (sure.equalsIgnoreCase("on")) {
          if (sql.execute("DELETE FROM inroutes WHERE route=" + sql.quote(route))) {
            msg = "Route deleted";
          } else {
            msg = "Failed to delete";
          }
        } else {
          msg = "Please confirm delete action";
        }
        route = "";
      }
      if (verb.equals("view")) {
        editroute = route;
        String routes[] = sql.select1row("SELECT cid,did,dest FROM inroutes WHERE route=" + sql.quote(route));
        if (routes != null) {
          cid = routes[0];
          did = routes[1];
          dest = routes[2];
        }
      }
      if (verb.equals("add") || verb.equals("edit")) {
        if ((cid.length() == 0) && (did.length() == 0)) {
          msg = "You must define DID and/or CID";
          if (verb.equals("add")) verb = ""; else verb = "view";
        }
        if (dest.length() == 0) {
          msg = "Destination must be defined";
          if (verb.equals("add")) verb = ""; else verb = "view";
        }
      }
      if (verb.equals("add")) {
        if (sql.execute("INSERT INTO inroutes (route, cid, did, dest) VALUES (" + sql.quote(route) + ","
        + sql.quote(cid) + "," + sql.quote(did) + "," + sql.quote(dest) + ")")) {
          msg = "Route added";
          route = "";
          cid = "";
          did = "";
          dest = "";
        } else {
          msg = "Failed to add:" + sql.lastException.toString();
        }
      }
      if (verb.equals("edit")) {
        if (sql.execute("UPDATE inroutes SET route=" + sql.quote(route) + ",cid=" + sql.quote(cid) + ",did=" + sql.quote(did)
        + ",dest=" + sql.quote(dest) + " WHERE route=" + sql.quote(editroute))) {
          msg = "Route edited";
          route = "";
          cid = "";
          did = "";
          dest = "";
        } else {
          msg = "Failed to edit:" + sql.lastException.toString();
          verb = "view";
        }
      }
  
      html.append("<table height=100%>");
      html.append("<tr><td width=100%>");
      if (msg.length() > 0) {
        html.append("<font color=#ff0000>" + msg + "</font><br>");
      }
      if (verb.equals("view")) {
        html.append(form("core", "inroutes"));
        html.append("<input type=hidden name=verb value=del><input type=hidden name=route value=" + sql.quote(route) + "><input type=submit value='Delete route'>");
        html.append("<input type=checkbox name=sure>I'm Sure</form>");
      }
      html.append(form("core", "inroutes"));
      html.append("<input type=hidden name=verb value=" + (verb.equals("view") ? "edit" : "add") + ">");
      if (verb.equals("view")) {
        html.append("<input type=hidden name=editroute value=" + sql.quote(editroute) + ">");
      }
      html.append("<table>");
      html.append("<tr><td> Route Name: </td><td> <input name=route value=" + sql.quote(route) + "</td></tr>");
      html.append("<tr><td> Dialed # (DID): </td><td> <input name=did value=" + sql.quote(did) + "></td><td>(optional)</td></tr>");
      html.append("<tr><td> Caller # (CID): </td><td> <input name=cid value=" + sql.quote(cid) + "></td><td>(optional)</td></tr>");
      html.append("<tr><td> Destination #: </td><td> <input name=dest value=" + sql.quote(dest) + "></td><td>(Extension, IVR, etc.)</tr>");
      html.append("<tr><td> <input type=submit value=" + (verb.equals("view") ? "Edit" : "Add") + "></td></tr>");
      html.append("</table>");
      html.append("</form>");
      html.append("</td><td width=2 bgcolor=#000000></td><td width=180>");
      html.append("<div class=menuitem>" + link("core", "inroutes", "", "Add New") + "</div>");
      String routes[][] = sql.select("SELECT route, did, cid FROM inroutes ORDER BY route");
      if (routes != null) {
        for(int a=0;a<routes.length;a++) {
          if (routes[a][1].length() == 0) routes[a][1] = "any DID";
          if (routes[a][2].length() == 0) routes[a][2] = "any CID";
          html.append("<div class=menuitem>" + link("core", "inroutes", "verb=view&route=" + routes[a][0],
            "&lt;" + routes[a][0] + "&gt;" + routes[a][1] + "/" + routes[a][2]) + "</div>");
        }
      }
      html.append("</td></tr></table>");
  
      return html.toString();
    }
  
    private String doIVRPage(SQL sql, String args[]) {
      String verb = "", ext = "", editext = "", display = "", script = "", sure = "", msg = "";
      for(int a=0;a<args.length;a++) {
        int x = args[a].indexOf("=") + 1;
        if (args[a].startsWith("verb=")) verb = args[a].substring(x);
        if (args[a].startsWith("ext=")) ext = args[a].substring(x);
        if (args[a].startsWith("display=")) display = args[a].substring(x);
        if (args[a].startsWith("script=")) script = args[a].substring(x);
        if (args[a].startsWith("editext=")) editext = args[a].substring(x);
        if (args[a].startsWith("sure=")) sure = args[a].substring(x);
      }
      ext = numbersOnly(ext);
      if (verb.equals("del")) {
        if (sure.equalsIgnoreCase("on")) {
          if (!sql.execute("DELETE FROM ivrs WHERE ext=" + sql.quote(ext))) {
            msg = "Failed to delete extension";
          }
          if (msg.length() == 0) {
            msg = "IVR deleted";
          }
        } else {
          msg = "Please confirm delete action";
        }
        ext = "";
      }
      if (verb.equals("view")) {
        editext = ext;
        display = sql.select1value("SELECT display FROM ivrs WHERE ext=" + sql.quote(ext));
        script = sql.select1value("SELECT script FROM ivrs WHERE ext=" + sql.quote(ext));
      }
      if (verb.equals("add") || verb.equals("edit")) {
        if (ext.length() == 0)  {
          msg = "Invalid IVR number";
        } else {
          String isext = sql.select1value("SELECT ext FROM exts WHERE ext=" + sql.quote(ext));
          if ((isext != null) && (isext.equals(ext))) {
            msg = "Extension already exists with that number";
          }
        }
        if (display.length() == 0)  {
          msg = "Invalid display name";
        }
        if (msg.length() > 0) {
          if (verb.equals("add")) verb = ""; else verb = "view";
        }
      }
      if (verb.equals("add")) {
        if (!sql.execute("INSERT INTO ivrs (ext, display, script) VALUES (" + sql.quote(ext) + "," + sql.quote(display) + "," + sql.quote(script) + ")")) {
          msg = "Failed to add";
        }
        if (msg.length() == 0) {
          msg = "IVR added";
          ext = "";
          display = "";
          script = "";
        }
      }
      if (verb.equals("edit")) {
        if (!sql.execute("UPDATE ivrs SET ext=" + sql.quote(ext) + ",display=" + sql.quote(display) + ",script=" + sql.quote(script) + " WHERE ext=" + sql.quote(editext))) {
          msg = "Failed to edit : " + sql.lastException.toString();
          verb = "view";
        }
        if (msg.length() == 0) {
          msg = "IVR edited";
          ext = "";
          display = "";
          script = "";
        }
      }
      StringBuilder html = new StringBuilder();
  
      html.append("<table height=100%>");
      html.append("<tr><td width=100%>");
      if (msg.length() > 0) {
        html.append("<font color=#ff0000>" + msg + "</font><br>");
      }
      if (verb.equals("view")) {
        html.append(form("core", "ivrs"));
        html.append("<input type=hidden name=verb value=del><input type=hidden name=ext value=" + sql.quote(ext));
        html.append("><input type=submit value='Delete IVR'>" + "<input type=checkbox name=sure>I'm Sure</form>");
      }
      html.append(form("core", "ivrs"));
      html.append("<input type=hidden name=verb value=" + (verb.equals("view") ? "edit" : "add") + ">");
      if (verb.equals("view")) {
        html.append("<input type=hidden name=editext value=" + sql.quote(editext) + ">");
      }
      html.append("<table>");
      html.append("<tr><td>IVR #:</td><td><input name=ext value=" + sql.quote(ext) + ">");
      html.append("</td></tr>");
      html.append("<tr><td>Name:</td><td><input name=display value=" + sql.quote(display) + "></td></tr>");
      html.append("<tr><td>Script:</td><td><textarea id=script name=script cols=40 rows=20>" + convertString(script) + "</textarea></td><td><a href=\"javascript:showHelp('ivr');\">Help</a><br>");
      html.append("Presets:<select id=preset><option>-none-</option><option>Conference</option><option>Video Conference</option></select> <a href=\"javascript:load_preset();\">Load</a></td></tr>");
      html.append("</table>");
      html.append("<input type=submit value=" + (verb.equals("view") ? "Edit" : "Add") + ">");
      html.append("</form>");
      html.append("</td><td width=2 bgcolor=#000000></td><td width=180>");
      html.append("<div class=menuitem>" + link("core", "ivrs", "", "Add New") + "</div>");
      String ivrs[][] = sql.select("SELECT ext,display FROM ivrs ORDER BY ext");
      if (ivrs != null) {
        for(int a=0;a<ivrs.length;a++) {
          html.append("<div class=menuitem>" + link("core", "ivrs", "verb=view&ext=" + ivrs[a][0], ivrs[a][1] + "&lt;" + ivrs[a][0] + "&gt;") + "</div>");
        }
      }
      html.append("</td></tr>");
      html.append("</table>");
  
      return html.toString();
    }
  
    private String doMsgsPage(SQL sql, String args[], WebRequest req) {
      String verb = "", file = "", oldfile = "", newfile = "", sure = "", msg= "";
      for(int a=0;a<args.length;a++) {
        int x = args[a].indexOf("=") + 1;
        if (args[a].startsWith("verb=")) verb = args[a].substring(x);
        if (args[a].startsWith("file=")) file = decode(args[a].substring(x));
        if (args[a].startsWith("oldfile=")) oldfile = decode(args[a].substring(x));
        if (args[a].startsWith("newfile=")) newfile = decode(args[a].substring(x));
        if (args[a].startsWith("sure=")) sure = args[a].substring(x);
      }
      if ( (file.indexOf("..") != -1) || (file.indexOf("/") != -1) || (file.indexOf("\\") != -1) ) {
        return "ERROR : invalid filename.";
      }
      if (verb.equals("del")) {
        if (sure.equalsIgnoreCase("on")) {
          if (isBuiltinSound(file)) {
            msg = "Can not delete built-in message";
          } else {
            try {
              File f = new File(Paths.sounds + Paths.lang + "/" + file + ".wav");
              f.delete();
              msg = "Deleted file : " + f.getName();
            } catch (Exception e1) {
              msg = "Unable to delete file";
            }
          }
        } else {
          msg = "Please confirm delete action";
        }
      }
      if (verb.equals("upload")) {
        try {
          if (WebUpload.isMultipartContent(req)) {
            WebUpload webUpload = new WebUpload();
            WebUpload.WebFile files[] = webUpload.parseRequest(req);
            JFLog.log("files=" + files.length);
            for(int a=0;a<files.length;a++) {
              String fileName = files[a].name;
              JFLog.log("filename=" + fileName);
              if (!fileName.toLowerCase().endsWith(".wav")) {
                msg += "Not a wav:" + fileName + "<br>";
                continue;
              }
              String dirName = Paths.sounds + Paths.lang + "/";
              try {
                File saveTo = new File(dirName + fileName);
                files[a].write(saveTo);
                msg += "Uploaded:" + fileName + "<br>";
              } catch (Exception e3) {
                msg += "Upload failed:" + fileName + ":" + e3 + "<br>";
              }
            }
          } else {
            msg += "No upload data found.";
          }
        } catch (Exception e) {
          msg += e;
        }
      }
      if (verb.equals("rename")) {
        verb = "";
        if (isBuiltinSound(oldfile) || isBuiltinSound(newfile)) {
          msg = "Can not rename build-in sound";
        } else {
          File oldFile = new File(Paths.sounds + Paths.lang + "/" + oldfile + ".wav");
          File newFile = new File(Paths.sounds + Paths.lang + "/" + newfile + ".wav");
          oldFile.renameTo(newFile);
          msg = "File renamed";
        }
      }
      StringBuilder html = new StringBuilder();
  
      html.append("<table height=100%>");
      html.append("<tr><td width=100%>");
      if (msg.length() > 0) html.append("<font color=#ff0000>" + msg + "</font><br>");
      if (verb.equals("view")) {
        html.append(form("core", "msgs"));
        html.append("<input type=hidden name=verb value=del><input type=hidden name=file value=" + sql.quote(file));
        html.append("><input type=submit value='Delete Message'>" + "<input type=checkbox name=sure>I'm Sure</form>");
      }
      if (verb.equals("renameForm")) {
        html.append(form("core", "msgs"));
        html.append("Not implemented Yet!<br>");
        html.append("<input type=hidden name=verb value=rename>");
        html.append("<table>");
        html.append("<tr><td>Old Filename:</td><td><input name=oldfile value=" + sql.quote(oldfile) + " readonly></td></tr>");
        html.append("<tr><td>New Filename:</td><td><input name=newfile></td></tr>");
        html.append("</table>");
        html.append("<input type=submit value=Rename>");
        html.append("</form>");
      } else if (verb.equals("view")) {
        html.append("<table>");
        html.append("<tr><td>Filename:</td><td>" + file);
        html.append(link("core", "msgs", "verb=renameForm&oldfile=" + file, "Rename"));
        html.append("</td></tr>");
        html.append("</table>");
      } else {
        html.append(formUpload("core", "msgs", "verb=upload"));
        html.append("<table>");
        html.append("<tr><td>Filename:</td><td><input type=file name=file></td><td><input type=submit value='Upload'></td></tr>");
        html.append("</table>");
        html.append("</form>");
        html.append("Wav files must be 8000Hz, mono, 16bit in PCM format.<br>");
      }
      html.append("</td><td width=2 bgcolor=#000000></td><td width=180>");
      html.append("<div class=menuitem>" + link("core", "msgs", "", "Add New") + "</div>");
      try {
        File f = new File(Paths.sounds + Paths.lang + "/");
        File fs[] = f.listFiles();
  