Merge pull request #52 from jtanx/master
authorJeremy Tan <[email protected]>
Fri, 4 Oct 2013 04:00:22 +0000 (21:00 -0700)
committerJeremy Tan <[email protected]>
Fri, 4 Oct 2013 04:00:22 +0000 (21:00 -0700)
Add working login to gui, add install scripts for server-configs

server-configs/gen_ssl_cert.sh
server-configs/install.sh [new file with mode: 0644]
server/fastcgi.c
server/fastcgi.h
server/login.c
server/run.sh
testing/MCTXWeb/public_html/Login-Rowan [new file with mode: 0644]
testing/MCTXWeb/public_html/index.html
testing/MCTXWeb/public_html/login.html [new file with mode: 0644]
testing/MCTXWeb/public_html/static/mctx.gui.js
testing/MCTXWeb/public_html/static/style.css

index 1342562..51abb2d 100644 (file)
@@ -12,20 +12,25 @@ if [ "$(whoami)" != "root" ]; then
         exit 1
 fi
 
-echo 'Making the conf dir /usr/share/nginx/conf...'
-mkdir -p /usr/share/nginx/conf
+# Check for nginx dir
+nginx=/usr/share/nginx
+if [ ! -d "$nginx" ]; then
+       (echo "nginx folder not found at $nginx.") 1>&2
+       exit 1
+fi;
+
+echo 'Making the conf dir $nginx/conf...'
+mkdir -p $nginx/conf
 
 echo Generating the server private key...
-openssl genrsa -out /usr/share/nginx/conf/server.key 2048
+openssl genrsa -out $nginx/conf/server.key 2048
 
 echo Generating the CSR...
-openssl req -new -key /usr/share/nginx/conf/server.key \
--out /usr/share/nginx/conf/server.csr \
+openssl req -new -key $nginx/conf/server.key -out $nginx/conf/server.csr \
  -subj "/C=AU/ST=WA/L=Perth/O=UWA/OU=Mechatronics/CN=$1"
 
 echo Signing the certificate...
-openssl x509 -req -days 3650 -in /usr/share/nginx/conf/server.csr \
--signkey /usr/share/nginx/conf/server.key \
--out /usr/share/nginx/conf/server.crt
+openssl x509 -req -days 3650 -in $nginx/conf/server.csr \
+ -signkey $nginx/conf/server.key -out $nginx/conf/server.crt
 
 
diff --git a/server-configs/install.sh b/server-configs/install.sh
new file mode 100644 (file)
index 0000000..d904801
--- /dev/null
@@ -0,0 +1,66 @@
+#!/bin/bash
+
+# Check running as root
+if [ "$(whoami)" != "root" ]; then
+        (echo "Run $0 as root.") 1>&2
+        exit 1
+fi
+
+nconf=/usr/share/nginx/conf
+# Generate the ssl cert, if necessary
+if [ ! -d "$nconf" ]; then
+       echo "ssl cert not found at $nconf, generating..."
+       ./gen_ssl_cert.sh $1
+       if [[ $? != 0 ]] ; then
+               (echo "gen_ssl_cert failed, exiting.") 1>&2
+               exit 1
+       fi;
+fi
+
+nginx=/etc/nginx
+# Check for nginx
+if [ ! -d "$nginx" ]; then
+       (echo "Install nginx.") 1>&2
+       exit 1
+fi
+
+# Copy nginx configs
+echo
+echo "Copying nginx configs..."
+cp -v nginx/fastcgi_params ./nginx/mime.types $nginx/
+rm -fv $nginx/sites-enabled/*
+cp -v nginx/sites-enabled/mctxconfig $nginx/sites-enabled
+
+echo
+echo "Restarting nginx..."
+/etc/init.d/nginx restart
+
+echo
+echo "Note: Check the document root for the nginx config is set correctly."
+
+# Copy syslog configs
+rsyslog=/etc/rsyslog.d
+lrotate=/etc/logrotate.d
+if [ -d "$rsyslog" ]; then
+       echo
+       echo "Copying rsyslog configs..."
+       cp -v rsyslog.d/30-mctxserv.conf $rsyslog/
+
+       echo
+       echo "Restarting rsyslog..."
+       /etc/init.d/rsyslog restart
+else
+       echo
+       (echo "Could not find rsyslog at $rsyslog. Skipping.") 1>&2
+fi
+
+if [ -d "$lrotate" ]; then
+       echo
+       echo "Copying logrotate configs..."
+       cp -v logrotate.d/mctxserv.conf $lrotate/
+else
+       echo
+       (echo "Could not find logrotate at $lrotate. Skipping.") 1>&2
+fi
+
+
index 08c413b..57c5b1b 100644 (file)
@@ -45,6 +45,8 @@ static void IdentifyHandler(FCGIContext *context, char *params) {
        FCGI_JSONPair("description", "MCTX3420 Server API (2013)");
        FCGI_JSONPair("build_date", __DATE__ " " __TIME__);
        FCGI_JSONLong("api_version", API_VERSION);
+       FCGI_JSONBool("logged_in", FCGI_HasControl(context, getenv("COOKIE_STRING")));
+       FCGI_JSONPair("friendly_name", "");
 
        //Sensor and actuator information
        if (ident_sensors) {
@@ -77,14 +79,14 @@ static void IdentifyHandler(FCGIContext *context, char *params) {
  * the system at any one time. The key can be forcibly generated, revoking
  * any previous control keys. To be used in conjunction with HTTP 
  * basic authentication.
- * This function will generate a JSON response that indicates success/failure.
  * @param context The context to work in
  * @param force Whether to force key generation or not.
- */ 
-void FCGI_LockControl(FCGIContext *context, bool force) {
+ * @return true on success, false otherwise (eg someone else already in control)
+ */
+bool FCGI_LockControl(FCGIContext *context, bool force) {
        time_t now = time(NULL);
        bool expired = now - context->control_timestamp > CONTROL_TIMEOUT;
-       
+
        if (force || !*(context->control_key) || expired) 
        {
                SHA_CTX sha1ctx;
@@ -100,7 +102,9 @@ void FCGI_LockControl(FCGIContext *context, bool force) {
                for (i = 0; i < 20; i++)
                        sprintf(context->control_key + i * 2, "%02x", sha1[i]);
                snprintf(context->control_ip, 16, "%s", getenv("REMOTE_ADDR"));
+               return true;
        }
+       return false;
 }
 
 /**
@@ -114,7 +118,8 @@ void FCGI_LockControl(FCGIContext *context, bool force) {
 bool FCGI_HasControl(FCGIContext *context, const char *key) {
        time_t now = time(NULL);
        int result = (now - context->control_timestamp) <= CONTROL_TIMEOUT &&
-                                key != NULL && !strcmp(context->control_key, key);
+                       key != NULL && context->control_key[0] != '\0' &&
+                       !strcmp(context->control_key, key);
        if (result) {
                context->control_timestamp = now; //Update the control_timestamp
        }
@@ -128,8 +133,6 @@ bool FCGI_HasControl(FCGIContext *context, const char *key) {
  */
 void FCGI_ReleaseControl(FCGIContext *context) {
        *(context->control_key) = 0;
-       FCGI_BeginJSON(context, STATUS_OK);
-       FCGI_EndJSON();
        return;
 }
 
@@ -287,6 +290,25 @@ void FCGI_BeginJSON(FCGIContext *context, StatusCodes status_code)
        FCGI_JSONPair("control_state", Control_GetModeName());
 }
 
+/**
+ * Generic accept response in JSON format.
+ * @param context The context to work in
+ * @param description A short description.
+ * @param cookie Optional. If given, the cookie field is set to that value.
+ */
+void FCGI_AcceptJSON(FCGIContext *context, const char *description, const char *cookie)
+{
+       printf("Content-type: application/json; charset=utf-8\r\n");
+       if (cookie) {
+               printf("Set-Cookie: %s\r\n", cookie);
+       }
+       printf("\r\n{\r\n");
+       printf("\t\"module\" : \"%s\"", context->current_module);
+       FCGI_JSONLong("status", STATUS_OK);
+       FCGI_JSONPair("description", description);
+       FCGI_EndJSON();
+}
+
 /**
  * Adds a key/value pair to a JSON response. The response must have already
  * been initiated by FCGI_BeginJSON. Special characters are not escaped.
@@ -441,17 +463,17 @@ void * FCGI_RequestLoop (void *data)
        while (FCGI_Accept() >= 0) {
                
                ModuleHandler module_handler = NULL;
-               char module[BUFSIZ], params[BUFSIZ], cookie[BUFSIZ];
+               char module[BUFSIZ], params[BUFSIZ];
+               //Don't need to copy if we're not modifying string contents
+               const char *cookie = getenv("COOKIE_STRING");
                
                //strncpy doesn't zero-truncate properly
                snprintf(module, BUFSIZ, "%s", getenv("DOCUMENT_URI_LOCAL"));
                snprintf(params, BUFSIZ, "%s", getenv("QUERY_STRING"));
-               snprintf(cookie, BUFSIZ, "%s", getenv("COOKIE_STRING"));
 
                Log(LOGDEBUG, "Got request #%d - Module %s, params %s", context.response_number, module, params);
                Log(LOGDEBUG, "Cookie: %s", cookie);
 
-
                
                //Remove trailing slashes (if present) from module query
                size_t lastchar = strlen(module) - 1;
@@ -486,11 +508,9 @@ void * FCGI_RequestLoop (void *data)
                context.current_module = module;
                context.response_number++;
                
-
-
                if (module_handler) 
                {
-                       if (module_handler != Login_Handler)
+                       if (module_handler != Login_Handler && module_handler != IdentifyHandler)
                        {
                                if (cookie[0] == '\0')
                                {
index 74468ae..365aa6c 100644 (file)
@@ -54,12 +54,13 @@ typedef struct
 
 typedef void (*ModuleHandler) (FCGIContext *context, char *params);
 
-extern void FCGI_LockControl(FCGIContext *context, bool force);
+extern bool FCGI_LockControl(FCGIContext *context, bool force);
 extern void FCGI_ReleaseControl(FCGIContext *context);
 extern bool FCGI_HasControl(FCGIContext *context, const char *key);
 extern char *FCGI_KeyPair(char *in, const char **key, const char **value);
 extern bool FCGI_ParseRequest(FCGIContext *context, char *params, FCGIValue values[], size_t count);
 extern void FCGI_BeginJSON(FCGIContext *context, StatusCodes status_code);
+extern void FCGI_AcceptJSON(FCGIContext *context, const char *description, const char *cookie);
 extern void FCGI_JSONPair(const char *key, const char *value);
 extern void FCGI_JSONLong(const char *key, long value);
 extern void FCGI_JSONDouble(const char *key, double value);
index a616af2..4ec89dd 100644 (file)
@@ -149,8 +149,9 @@ int Login_LDAP_Bind(const char * uri, const char * dn, const char * pass)
  * @param params - Parameter string, UNUSED
  */
 void Logout_Handler(FCGIContext * context, char * params)
-{              
+{
        FCGI_ReleaseControl(context);
+       FCGI_AcceptJSON(context, "Logged out", "0");
 }
 
 
@@ -161,16 +162,8 @@ void Logout_Handler(FCGIContext * context, char * params)
  */
 void Login_Handler(FCGIContext * context, char * params)
 {
-
-       if (context->control_key[0] != '\0')
-       {
-               FCGI_RejectJSON(context, "Already logged in.");
-               return;
-       }
-
-       char * user = ""; // The username supplied through CGI
-       char * pass = ""; // The password supplied through CGI
-                                               //TODO: Make sure these are passed through HTTPS, *not* HTTP .... otherwise people can eavesdrop on the passwords
+       char * user; // The username supplied through CGI
+       char * pass; // The password supplied through CGI
 
        FCGIValue values[] = {
                {"user", &user, FCGI_REQUIRED(FCGI_STRING_T)},
@@ -191,17 +184,14 @@ void Login_Handler(FCGIContext * context, char * params)
                return;
        }
 
-
-       // Trim leading whitespace (the BUFSIZ check is to make sure incorrectly terminated strings don't cause an infinite loop)
+       // Trim leading whitespace
        int i = 0;
-       for (i = 0; i < BUFSIZ && isspace(user[0]) && user[0] != '\0'; ++i,++user);
+       for (i = 0; isspace(user[0]) && user[0] != '\0'; ++i, ++user);
 
        // Truncate string at first non alphanumeric character
-       for (i = 0; i < BUFSIZ && isalnum(user[i]) && user[i] != '\0'; ++i);
+       for (i = 0; isalnum(user[i]) && user[i] != '\0'; ++i);
        user[i] = '\0';
 
-
-
        
        bool authenticated = true;
        
@@ -251,14 +241,18 @@ void Login_Handler(FCGIContext * context, char * params)
        
        if (!authenticated)
        {
-               FCGI_RejectJSON(context, "Authentication failure.");
-               return;
+               FCGI_RejectJSONEx(context, STATUS_UNAUTHORIZED, "Authentication failure.");
+       }
+       else
+       {
+               if (FCGI_LockControl(context, false))
+               {
+                       // Give the user a cookie
+                       FCGI_AcceptJSON(context, "Logged in", context->control_key);
+               }
+               else
+               {
+                       FCGI_RejectJSON(context, "Someone else is already logged in");
+               }
        }
-
-       FCGI_LockControl(context, false);
-       
-       // Give the user a cookie
-       FCGI_PrintRaw("Content-type: text\r\n");
-       FCGI_PrintRaw("Set-Cookie: %s\r\n\r\n", context->control_key);
-       
 }
index c2bd504..d4e6510 100755 (executable)
@@ -1,15 +1,18 @@
 #!/bin/bash
-# Use this to quickly test run the server in valgrind
-#spawn-fcgi -p9005 -n ./valgrind.sh
-# Use this to run the server normally
-#./stream &
+
+# Check existence of program
+if [ ! -e "server" ]; then
+        (echo "Rebuild server.") 1>&2;
+        exit 1
+fi
+
 
 if [[ "$(uname -m)" != *arm*  ]]; then
         echo Not running on the BBB
         # Use this to quickly test run the server in valgrind
         spawn-fcgi -p9005 -n ./valgrind.sh
         # Use this to run the server normally
-        #./stream &
+        #spawn-fcgi -p9005 -n ./server
         exit 0
 fi
 
@@ -19,12 +22,6 @@ if [ "$(whoami)" != "root" ]; then
         exit 1
 fi
 
-# Check existence of program
-if [ ! -e "server" ]; then
-        (echo "Rebuild server.") 1>&2;
-        exit 1
-fi
-
 # Rotate the logs
 echo Rotating the system logs
 logrotate -f /etc/logrotate.d/mctxserv.conf
diff --git a/testing/MCTXWeb/public_html/Login-Rowan b/testing/MCTXWeb/public_html/Login-Rowan
new file mode 100644 (file)
index 0000000..f93c0ab
--- /dev/null
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+
+<html>
+       <head>
+               <Title> MCTX 3420 login</Title>
+               </head>
+
+       <body style>
+               <div id="window">
+                       <div id= "container">
+                               <h1><a href="http://www.uwa.edu.au/" target="_blank">
+       <img alt = "The University of Western Australia"
+       src="http://www.lms.uwa.edu.au/theme/image.php/uwa/theme/1378302164/uwacrest">
+      </a> </h1>
+                       <div id="login-container">
+                               <div id="login-section">
+                                       <form name="login-form" method="post" action="index.html" class="aui-login-form-container">
+                                               <h2> "log in"</h2>
+                                               <div id="action-messages">
+                                               </div>
+                                               <fieldset class="compact-form-feilds">
+                                                       <div class="feild-group">
+                                                               <label id="os_username"> Username </label>
+                                                               <input type="text" name="os_username" id="os_username" class="text" data-focus="0">
+
+                                                       </div>
+                                                       <div class="feild-group"><label id="os-password-label" for="os_password"> "Password" </label>
+                                                               <input type="password" name="os_password" id="os_password" class="password">
+                                                       </div>
+                                                       <div class="feild-group-form-buttons compact-form-buttons">
+                                                               <input id="loginbutton" class="aui-button aui-style aui-button-primary" name="login" type="submit" value="Log In">
+
+                                                       </div>
+                                               </fieldset>
+                                       </form>
+
+                               </div>
+                       </div>
+
+                       </div>
+
+
+               </div>
+
+       </body>
+
+</html>
index a9269f8..7a62aa2 100644 (file)
@@ -14,6 +14,7 @@
     <link rel="stylesheet" type="text/css" href="static/style.css">
     <link rel="stylesheet" type="text/css" href="static/nav-menu.css">
     <script type="text/javascript">
+      runBeforeLoad();
       $(document).ready(function () {
         //$("#menu-container").populateNavbar();
         $("#login").submit(function () {
           return false;
         });
         
+        $("#logout").click(function () {
+          $("#logout").logout();
+        });
+        
         $("#main_controls").submit(function () {
           //Validate!
           return false;
         });
-        //$("#cam1").setCamera();
-        //$("#strain-graphs").setStrainGraphs();
+        
         $("#errorlog").setErrorLog();
       });
     </script>
@@ -53,7 +57,7 @@
         <div id="logout-container">
           <form action="#">
             <div>
-              <input type="button" name="logout" value="Logout">
+              <input type="button" id="logout" value="Logout">
             </div>
           </form>
         </div>
             <input type="submit" value="Submit">
           </form>
         </div>
-        
-        <!--Todo get rid of this and make a separate login page-->
-        <div class="widget">
-          <div class="title">Login</div>
-          <div class="item">
-            <form id="login" action="#">
-              <table class="centre">
-                <tr><td>Username</td><td><input name="username" type="text"></td></tr>
-                <tr><td>Password</td><td><input name="pass" type="password"></td></tr>
-                <tr>
-                  <td></td>
-                  <td>
-                    <input type="submit" value="Submit">
-                    <input type="checkbox" name="force"> Force
-                  </td>
-                </tr>
-              </table>
-            </form>
-          </div>
-        </div>
       </div>
       <!-- End sidebar -->
 
diff --git a/testing/MCTXWeb/public_html/login.html b/testing/MCTXWeb/public_html/login.html
new file mode 100644 (file)
index 0000000..caa6185
--- /dev/null
@@ -0,0 +1,74 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+  <head>
+    <title>MCTX3420 Web Interface</title>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <!--[if lte IE 8]>
+      <script language="javascript" type="text/javascript" src="static/excanvas.min.js"></script>
+    <![endif]-->
+    <script type="text/javascript" src="static/jquery-1.10.1.min.js"></script>
+    <script type="text/javascript" src="static/jquery.flot.min.js"></script>
+    <script type="text/javascript" src="static/base64.js"></script>
+    <script type="text/javascript" src="static/mctx.gui.js"></script>
+    
+    <link rel="stylesheet" type="text/css" href="static/style.css">
+    <link rel="stylesheet" type="text/css" href="static/nav-menu.css">
+    <script type="text/javascript">
+      runBeforeLoad(true);
+      
+      $(document).ready(function () {
+        $("#login").submit(function () {
+          $("#login").login();
+          return false;
+        });
+      });
+    </script>
+  </head>
+  
+  <body>
+    <div id="header">
+      <div id="leftnav">
+        <a href="http://www.uwa.edu.au/" target="_blank">
+          <img alt = "The University of Western Australia"
+          src="static/uwacrest-text.png">
+        </a>
+        <span id="title">Exploding Cans</span>
+      </div>
+      <div id="rightnav">
+        <span id="date">
+          <script type="text/javascript">getDate();</script>
+        </span>
+      </div>
+      <div class="clear"></div>
+    </div>
+    <!-- End header -->
+    
+    <div id="login-container">
+     <div class="widget">
+         <form id="login" action="#">
+           <p>
+             <label>
+               Username<br>
+               <input name="username" type="text">
+             </label>
+           </p>
+           <p>
+             <label>
+               Password<br>
+               <input name="pass" type="password">
+             </label>             
+           </p>
+           <p style="float:left; margin:0;">
+             <a href="#">Forgotten password?</a>
+           </p>
+           <p style="float:right; margin:0;">
+             <input type="submit" value="Log In">
+           </p>
+           <p id="result">
+             &nbsp;
+           </p>
+          </form>
+     </div>
+    </div>
+  </body>
+</html>
index 247484d..26f6f3c 100644 (file)
@@ -3,12 +3,20 @@
  */
 
 mctx = {};
-mctx.api = location.protocol + "//" +  location.host + "/api/";
+//Don't use this in the final version
+mctx.location = window.location.pathname;
+mctx.location = mctx.location.substring(0, mctx.location.lastIndexOf('/')) + "/";
+//mctx.location = location.protocol + "//" + location.host + "/";
+mctx.api = location.protocol + "//" + location.host + "/" + "api/";
 mctx.expected_api_version = 0;
-mctx.key = undefined;
 mctx.has_control = false;
+//mctx.debug = true;
 
-mctx.return_codes = {
+mctx.statusCodes = {
+  STATUS_OK : 1
+}
+
+mctx.statusCodesDescription = {
   "1" : "Ok",
   "-1" : "General error",
   "-2" : "Unauthorized",
@@ -37,6 +45,14 @@ mctx.strain_gauges = {};
 mctx.strain_gauges.ids = [0, 1, 2, 3];
 mctx.strain_gauges.time_limit = 20;
 
+function debugLog (msg) {
+  if (typeof console === "undefined" || typeof console.log === "undefined") {
+    alert(msg);
+  } else {
+    console.log(msg);
+  }
+}
+
 /**
  * Writes the current date to wherever it's called.
  */
@@ -44,6 +60,31 @@ function getDate(){
        document.write((new Date()).toDateString());
 }
 
+function runBeforeLoad(isLoginPage) {
+  $.ajax({
+    url : mctx.api + "identify"
+  }).done(function (data) {
+    if (mctx.debug) {
+      debugLog("Redirect disabled!");
+    } else if (data.logged_in && isLoginPage) {
+        window.location = mctx.location;
+    } else if (!data.logged_in && !isLoginPage) {
+      //Note: this only clears the nameless cookie
+      document.cookie = ""; 
+      window.location = mctx.location + "login.html";
+    } else {
+      mctx.friendlyName = data.friendly_name;
+      $("#content").css("display", "block");
+    }
+  }).fail(function (jqHXR) {
+    if (!isLoginPage) {
+      window.location = mctx.location + "login.html";
+    } else {
+      debugLog("Failed to ident server. Is API running?")
+    }
+  });
+}
+
 /**
  * Populates a submenu of the navigation bar
  * @param {string} header The header
@@ -154,33 +195,46 @@ $.fn.setStrainGraphs = function () {
 $.fn.login = function () {
   var username = this.find("input[name='username']").val();
   var password = this.find("input[name='pass']").val();
-  var force = this.find("input[name='force']").is(":checked");
-  var url = mctx.api + "control";
-  
-  var authFunc = function(xhr) {
-    xhr.setRequestHeader("Authorization",
-        "Basic " + base64.encode(username + ":" + password));
+  var out = this.find("#result");
+  var redirect = function () {
+    window.location.href = mctx.location;
   };
-
+  
+  out.removeAttr("class");
+  out.text("Logging in...");
+  
   $.ajax({
-    url : url,
-    data : {action : "lock", force : (force ? true : undefined)},
-    beforeSend : authFunc
+    url : mctx.api + "bind",
+    data : {user: username, pass : password}
   }).done(function (data) {
-    mctx.key = data.key;
     if (data.status < 0) {
-      alert("no - " + data.description);
+      mctx.has_control = false;
+      out.attr("class", "fail");
+      out.text("Login failed: " + data.description);
     } else {
+      //todo: error check
       mctx.has_control = true;
-      alert("yes - " + mctx.key);
+      out.attr("class", "pass");
+      out.text("Login ok!");
+      setTimeout(redirect, 800);      
     }
   }).fail(function (jqXHR) {
-    mctx.key = undefined;
     mctx.has_control = false;
-    alert("no");
+    out.attr("class", "fail");
+    out.text("Login request failed - connection issues.")
   });
 };
 
+$.fn.logout = function () {
+  $.ajax({
+    url : mctx.api + "unbind"
+  }).always(function () {
+    //Note: this only clears the nameless cookie
+    document.cookie = ""; 
+    window.location = mctx.location + "login.html";
+  });
+}
+
 $.fn.setErrorLog = function () {
   var url = mctx.api + "errorlog";
   var outdiv = this;
index 4f0df9b..0a21ecd 100644 (file)
@@ -29,6 +29,24 @@ form.controls {
   margin: 1em auto;
 }
 
+a {
+  color: #1188DD;
+}
+
+a:active {
+  color: #0066BB;
+}
+
+.pass {
+  color: #7AE309;
+  font-weight: bold;
+}
+
+.fail {
+  color: #E30909;
+  font-weight: bold
+}
+
 div.centre {
   text-align: center;
 }
@@ -79,9 +97,16 @@ input[type="button"]:active, input[type="submit"]:active {
   background-color: #E8E8E8;
 }
 
-/* IE8 width bugfix */
+
 input[type="text"], input[type="password"] {
-  width: 100%;
+  width: 100%; /* IE8 width bugfix */
+  border-radius: 3px;
+  border: 1px solid #CCCCCC;
+  font-size: 16px;
+}
+
+input[type="text"]:focus, input[type="password"]:focus {
+  box-shadow: 0 0 2px #BBBBBB;
 }
 
 #header {
@@ -114,15 +139,18 @@ input[type="text"], input[type="password"] {
   display: table;
 }
 
+#header #leftnav > div, #header #leftnav > span {
+  display: table-cell;
+  vertical-align: middle;
+  padding-left: 1em;  
+}
+
 #header #leftnav a {
   color: #2a2a2a;
 }
 
-#header #title {
+#title {
   font-size: 30px;
-  display: table-cell;
-  vertical-align: middle;
-  padding-left: 1em;
 }
 
 #header #rightnav {
@@ -148,10 +176,11 @@ input[type="text"], input[type="password"] {
 
 #content  {
   padding: 1em 2em;
+  /* display: none;  Enable in non-debug mode*/
 }
 
 #sidebar{
-  float: right;
+  float: left;
   max-width: 26%;
 }
 
@@ -167,8 +196,7 @@ input[type="text"], input[type="password"] {
 
 #main {
   overflow: auto;
-  margin-right: 25%;
-  padding-right: 2em;
+  padding-left: 2em;
   min-width: 400px;
 }
 
@@ -194,6 +222,7 @@ input[type="text"], input[type="password"] {
   margin: 0.25em 0.25em 1.5em;
   padding: 1em 1.25em;
   box-shadow: 0 0 3px #CCCCCC;
+  border: 1px solid #CCCCCC;
   transition: all 0.2s ease 0s;
   overflow: auto;
 }
@@ -212,4 +241,34 @@ input[type="text"], input[type="password"] {
   max-width: 100%;
   width: 100%;
   height: 6em;
+}
+
+/** For login.html **/
+#login-container {
+  margin: 0 auto;
+  width: 400px;
+  padding: 1.8em 0 0;
+  color: #606060;
+  line-height: 1.8em;
+}
+
+#login-container form {
+  padding: 1em 2em;
+}
+
+#login-container input {
+  font-size: 24px;
+}
+
+#login-container label {
+  font-size: 14px;
+}
+
+#login-container input[type=submit] {
+  font-size: inherit;
+  padding: 0.4em 0.8em;
+}
+
+#login-container #result {
+  clear: both;
 }
\ No newline at end of file

UCC git Repository :: git.ucc.asn.au