diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 99f5a0707625885a3d97b6f0dd90120ef6a84f87..9a4d5f7e20c417455960ed328f608f34245cd550 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -6,6 +6,24 @@ stages: - build - deploy +build-master: + stage: build + script: + - apt-get clean + - apt-get update + - apt-get install -y curl gnupg + - curl -sL https://deb.nodesource.com/setup_8.x | bash - + - apt-get install -y nodejs + - npm install + - npm run build + - cp -rv php dist/ + artifacts: + paths: + - dist/ + only: + - master + environment: master + build-release: stage: build script: @@ -16,13 +34,18 @@ build-release: - apt-get install -y nodejs - npm install - npm run build + - cp -rv php dist/ + artifacts: + paths: + - dist/ only: - release environment: release - + deploy-release: stage: deploy script: + - echo "Hello World!" only: - release environment: release diff --git a/README.md b/README.md index 3c8488bb11300677e298c22b5d1d5903f2228e07..9ec615c4b2c46813f2f9c33d77eb6e528931e14f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # webmw -> A Vue.js project +> Web client for Milliways. ## Build Setup diff --git a/php/.htaccess b/php/.htaccess new file mode 100644 index 0000000000000000000000000000000000000000..f289550940da314e0c59b1995c38ed2b670f8f78 --- /dev/null +++ b/php/.htaccess @@ -0,0 +1 @@ +Header set Access-Control-Allow-Origin "*" diff --git a/php/README b/php/README new file mode 100644 index 0000000000000000000000000000000000000000..2e394a531084a52b070e63f7c178f43bbc446a8b --- /dev/null +++ b/php/README @@ -0,0 +1,24 @@ +This is the web client for milliways talker. + +It works by running a background task that connects to the server +and appears as a standard client, buffering up messages until it is +polled by the web interface. + + +once you have the username & password (or existing sucssite token) +you launch mwpoll, this authenticates the user, puts itself into +the background, and gives you a socket address to communicate with it. + +the html code can then make ajax calls to poll.php, which will talk +to mwpoll through the socket, retrieving any new messages that are +waiting, if there are none it will pause for upto 60 seconds in a +"long poll". + +all messages (or an empty list) are returned in json format + +when the user has a command or message to send, an ajax call is placed +to send.php any immediate response from the command will be returned +as a json message + +in the current implimentation most of the decoding of the json messages +is handled by say.js in the browser diff --git a/php/config.php b/php/config.php new file mode 100644 index 0000000000000000000000000000000000000000..5e7980b5a861f872f24d5dd44e06471e748bf364 --- /dev/null +++ b/php/config.php @@ -0,0 +1,11 @@ + diff --git a/php/index.php b/php/index.php new file mode 100644 index 0000000000000000000000000000000000000000..cb0aab8ac7fa180db852a7c03bb0f9e8ab9e1e7c --- /dev/null +++ b/php/index.php @@ -0,0 +1,32 @@ + diff --git a/php/poll.php b/php/poll.php new file mode 100644 index 0000000000000000000000000000000000000000..a9d7783c6df53ee8a51a2e0d5e20c4ad3cffec34 --- /dev/null +++ b/php/poll.php @@ -0,0 +1,53 @@ + diff --git a/php/send.php b/php/send.php new file mode 100644 index 0000000000000000000000000000000000000000..19e112e807157e51bc5c6bd54a25415636ff61cd --- /dev/null +++ b/php/send.php @@ -0,0 +1,84 @@ + diff --git a/php/startup.php b/php/startup.php new file mode 100644 index 0000000000000000000000000000000000000000..0e9781a74274256b980cb6aa0a640cba9fb077df --- /dev/null +++ b/php/startup.php @@ -0,0 +1,146 @@ + 'username and password must both be not empty']); + exit; + } + + if (ctype_alnum($username) === FALSE) { + echo json_encode((object) ['error' => 'alphanumeric usernames only']); + exit; + } +} + +// Have we been asked to create a new Milliways account? +// If so, make sure that the password supplied is suitable. +if ($action=="create") { + $pass1 = @$_REQUEST['password1']; + $pass2 = @$_REQUEST['password2']; + + if ($pass1 != $pass2) { + echo json_encode((object) ['error' => 'Passwords don\'t match - please try again']); + exit; + } + + if (empty($pass1)) { + echo json_encode((object) ['error' => 'Password cannot be blank']); + exit; + } + + if (ctype_alnum($pass1) === FALSE) { + echo json_encode((object) ['error' => 'Milliways passwords can only contain alphanumeric characters']); + exit; + } + + if (strlen($pass1) < 6) { + echo json_encode((object) ['error' => 'Milliways passwords must be 6 characters or more']); + exit; + } +} + +$desc = array( + 0 => array("pipe", "r"), + 1 => array("pipe", "w"), +); +$pipes = array(); + +// Have we got SUCSsite cookies? +// And no login attempt? +// If so, fetch the user details and try to log in without a password. +if (@$_REQUEST['sucssite_loggedin']=="true" && empty($username)) { + + // Do we appear to be in the SUCSsite environment? + // There's only any point trying if we do. + if (file_exists("../settings.php")) { + // Do the bare minimum of SUCSsite init to retrieve the username for the session we've been passed + include("../settings.php"); + // Initialise the database + require("/usr/share/php/adodb/adodb.inc.php"); + $DB = NewADOConnection('postgres9'); + $DB->Connect('dbname='.$dbname.' user=apache'); + $DB->SetFetchMode(ADODB_FETCH_ASSOC); + + // Include the session library + require($base."lib/session.php"); + $session = new Session; + // $smarty->assign("session", $session); // This line might be useful? + $realuser = $session->username; + + if ($action == "create") { + $p = proc_open($mwpoll." -q -a -u ".$realuser." -s", $desc, $pipes); + } else { + $p = proc_open($mwpoll." -q -u ".$realuser." -s", $desc, $pipes); + } + $mode = "sucssite"; + } +} else { + // If not, try logging in with a password. + $p = proc_open($mwpoll." -q -u $username", $desc, $pipes); + $mode = "password"; +} + +if (empty($p)) { + echo "

No

"; + exit; +} + +if ($p === FALSE) { + echo json_encode((object) ['error' => 'failed to exec mwpoll']); + exit; +} + +if ($mode == "sucssite" && @$_REQUEST['sucssite_loggedin']=="true" && empty($username)) { + // If we have a sucssite session cookie, use that + fwrite($pipes[0], trim($_REQUEST['sucssite_session'])."\n"); + if ($action="create") { + fwrite($pipes[0], $pass1."\n"); + } +} else { + // Try logging on using username and password + if (fwrite($pipes[0], $password."\n") === FALSE) { + echo "Error writing to mwpoll\n"; + } +} + +$pid = fgets($pipes[1]); +if ($pid === FALSE) { + echo "error reading pid.\n"; +} + +$pid = trim($pid); +if (!is_numeric($pid)) { + if (substr($pid, -10) =="not found.") { + // User doesn't exist - ask for a Milliways password so we can create them! + echo json_encode((object) ['error' => $pid]); + exit; + } + + echo json_encode((object) ['error' => 'Bad response: pid=$pid']); + exit; +} + +$auth = fgets($pipes[1]); + +if ($auth === FALSE) { + echo "Error reading auth string\n"; +} + +$sess = array ( + "pid" => $pid, + "auth" => $auth, + "username" => $username +); + +$mwsess = serialize($sess); +echo "success:" . $mwsess; +?> diff --git a/src/App.vue b/src/App.vue index bc4699ef43958aa67d72a644b97d4cbd974d3ec9..10eba107554e064b5a380d414741a9cee4c9b12c 100644 --- a/src/App.vue +++ b/src/App.vue @@ -25,8 +25,17 @@ body { -moz-osx-font-smoothing: grayscale; } +/* Moz has no support for custom scrollbars. Default to plain */ .scroll { - overflow-y: hidden; + overflow-y: scroll; +} + +/* Chrome 29+ (Only) */ +@supports (-webkit-appearance:none) and (not (overflow:-webkit-marquee)) +and (not (-ms-ime-align:auto)) and (not (-moz-appearance:none)) { + .scroll { + overflow-y: hidden; + } } @media only screen and (max-width: 800px) { diff --git a/src/components/chat.vue b/src/components/chat.vue index 5a8acebcfada342a275e9a75d3ab5937d7177685..6769f38233b0cf9c70012c874d5647c0e9c839ba 100644 --- a/src/components/chat.vue +++ b/src/components/chat.vue @@ -25,6 +25,7 @@ import getImage from '../mixins/get-image.js' const urlRegex = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#/%?=~_|!:,.;]*[-A-Z0-9+&@#/%=~_|])/ig let room = 0 let msgCount = 0 +let previousOnline = true export default { name: 'chat', @@ -53,7 +54,16 @@ export default { msgCount -= msgNum }) - var ctx = this + window.bus.$on('online', (online) => { + if (!previousOnline && online) { + let timestamp = (this.$ls.get('lastmsg-' + room) + 1) + this.sendCmd('replay since ' + timestamp) + } + + previousOnline = online + }) + + let ctx = this setInterval(function () { ctx.sync(ctx, ctx.$ls.get('token'), ctx.onMsg) }, 1000) } }, @@ -79,7 +89,7 @@ export default { this.$ls.set('lastmsg-' + room, timestamp) } - var timestamp = (this.$ls.get('lastmsg-' + room) + 1) + let timestamp = (this.$ls.get('lastmsg-' + room) + 1) this.sendCmd('replay since ' + timestamp) }, onMsg: function (msg) { diff --git a/src/components/login.vue b/src/components/login.vue index 0a53c2e6a421c9a98e60d9bf7747a086df8f7cb9..0c34342b7b9a59e384ef30c6adfae62e90232798 100644 --- a/src/components/login.vue +++ b/src/components/login.vue @@ -34,7 +34,7 @@ export default { var user = document.getElementById('login-user').value var pass = document.getElementById('login-pass').value - axios.post('https://chat.sucs.org/php', { username: user, password: pass, action: 'login' }) + axios.post('https://chat.sucs.org/php/', { username: user, password: pass, action: 'login' }) .then(function (response) { if (typeof response.data === 'string') { if (response.data.startsWith('success:')) { diff --git a/src/components/topbar.vue b/src/components/topbar.vue index 88b2ee4822101d3a787b661c338fa00883981b2c..96ddf62f42c18340d9f127999fb73ab7479525e8 100644 --- a/src/components/topbar.vue +++ b/src/components/topbar.vue @@ -28,7 +28,7 @@ export default { }, methods: { logout: function (event) { - this.$ls.clear() + this.$ls.remove('token') this.$clearStorage() location.reload() }, diff --git a/src/mixins/mw-sync.js b/src/mixins/mw-sync.js index 83678c1b5be4eb410cdf558960c1f6a569daaa93..78ef03b7a7a313cb6db985fc65e3c2d8dd686a0d 100644 --- a/src/mixins/mw-sync.js +++ b/src/mixins/mw-sync.js @@ -1,7 +1,7 @@ import axios from 'axios' import getImage from '../mixins/get-image.js' -var profileImages = {} +let profileImages = {} export default { mixins: [getImage], @@ -9,7 +9,13 @@ export default { sync: function (ctx, token, callback) { axios.get('https://chat.sucs.org/php/poll.php', { params: { mwsess: token }, timeout: 1000 }) .then((msg) => { ctx.response(msg, callback) }).catch(function (error) { - error // Yea this counts as handling the error + if (error.message.indexOf('Network Error') !== -1) { + window.bus.$emit('online', false) + } else if (error.message.indexOf('timeout of') !== -1) { + window.bus.$emit('online', true) + } else { + error // 'Handle' the error + } }) }, response: async function (msg, callback) { @@ -145,7 +151,7 @@ export default { default: if (typeof msg[one].text === 'undefined') { // We've been timed out :( - this.$ls.clear() + this.$ls.remove('token') this.$clearStorage() location.reload() }