Skip to content

Standalone client work #41

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions TODO
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ General:

UI:
- replace qwebirc UI elements (logo, pages, etc)

README:
- Document dependencies (swftools added by flashsocket)
20 changes: 11 additions & 9 deletions bin/compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,21 +75,21 @@ def merge_files(output, files, root_path=lambda x: x):
f2.close()
f.close()

def mkdir(path):
try:
os.mkdir(path)
except:
pass

def main(outputdir=".", produce_debug=True):
ID = pagegen.getgitid()

pagegen.main(outputdir, produce_debug=produce_debug)

coutputdir = os.path.join(outputdir, "compiled")
try:
os.mkdir(coutputdir)
except:
pass

try:
os.mkdir(os.path.join(outputdir, "static", "css"))
except:
pass
mkdir(coutputdir)
mkdir(os.path.join(outputdir, "static", "css"))
mkdir(os.path.join(outputdir, "static", "swf"))

for uiname, value in pages.UIs.items():
csssrc = pagegen.csslist(uiname, True)
Expand All @@ -112,6 +112,8 @@ def main(outputdir=".", produce_debug=True):
alljs.append(os.path.join("js", "ui", "frontends", y + ".js"))
jmerge_files(outputdir, "js", uiname + "-" + ID, alljs, file_prefix="QWEBIRC_BUILD=\"" + ID + "\";\n")

subprocess.call(["as3compile", os.path.join(outputdir, "swf", "flashsocket.as"), "-o", os.path.join(outputdir, "static", "swf", "flashsocket.swf")])

os.rmdir(coutputdir)

f = open(".compiled", "w")
Expand Down
16 changes: 14 additions & 2 deletions bin/pagegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,18 @@ def producehtml(name, debug):
div = ui.get("div", "")
customcss = "\n".join(" <link rel=\"stylesheet\" href=\"%s%s\" type=\"text/css\"/>" % (config.frontend["static_base_url"], x) for x in ui.get("customcss", []))
customjs = "\n".join(" <script type=\"text/javascript\" src=\"%s%s\"></script>" % (config.frontend["static_base_url"], x) for x in ui.get("customjs", []))

flash = """
<div style="width:0px;height:0px;overflow:hidden;">
<div id="FlashSocket"></div>
<script type="text/javascript">
FlashSocket = swfobject.createSWF({
data: "%sswf/flashsocket.swf",
width: "10",
height: "10",
allowscriptaccess: "always"
}, {}, "FlashSocket");
</script>
</div>""" % (config.frontend["static_base_url"])
return """%s
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
Expand All @@ -84,9 +95,10 @@ def producehtml(name, debug):
<div id="noscript">Javascript is required to use IRC.</div>
</noscript>%s
</div>
%s
</body>
</html>
""" % (ui["doctype"], config.frontend["app_title"], config.frontend["static_base_url"], config.frontend["extra_html"], csshtml, customcss, jshtml, customjs, config.js_config(), ui["class"], div)
""" % (ui["doctype"], config.frontend["app_title"], config.frontend["static_base_url"], config.frontend["extra_html"], csshtml, customcss, jshtml, customjs, config.js_config(), ui["class"], div, flash)

def main(outputdir=".", produce_debug=True):
p = os.path.join(outputdir, "static")
Expand Down
28 changes: 28 additions & 0 deletions iris.conf.example
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,30 @@ webirc_mode:
webirc_password: fish


# FRONTEND IRC CONNECTION OPTIONS
# These options provide the needed information for the frontend to connect to
# the IRC server via tcp using a flash plugin.
# They require a backend restart and a rerun of compile.py to update.
[flash]

# SERVER: Hostname (or IP address) of IRC server to connect to.
server: irc.myserver.com

# PORT: Port of IRC server to connect to.
port: 6667

# XMLPORT: Port of IRC servers flash policy daemon
xmlport: 8430

# FRONTEND IRC CONNECTION OPTIONS
# These options provide the needed information for the frontend to connect to
# the IRC server via websocket
# They require a backend restart and a rerun of compile.py to update.
[websocket]

# URL: URL of IRC server to connect to.
url: ws://irc.myserver.com/


# ATHEME ENGINE OPTIONS
# These options control communication with Atheme by the Atheme engine backend,
Expand Down Expand Up @@ -221,6 +245,10 @@ static_base_url: /
# Iris instances on the same host.
dynamic_base_url: /

# CONNECTIONS: What order to attempt methods of connection in
# space seperated list of methods
# valid values: ajax flash websocket
connections: ajax


# ATHEME INTEGRATION OPTIONS
Expand Down
62 changes: 62 additions & 0 deletions js/base64.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/* Base64 polyfill from https://github.com/davidchambers/Base64.js/ */

;(function () {

var object = typeof exports != 'undefined' ? exports : this; // #8: web workers
var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';

function InvalidCharacterError(message) {
this.message = message;
}
InvalidCharacterError.prototype = new Error;
InvalidCharacterError.prototype.name = 'InvalidCharacterError';

// encoder
// [https://gist.github.com/999166] by [https://github.com/nignag]
object.btoa || (
object.btoa = function (input) {
for (
// initialize result and counter
var block, charCode, idx = 0, map = chars, output = '';
// if the next input index does not exist:
// change the mapping table to "="
// check if d has no fractional digits
input.charAt(idx | 0) || (map = '=', idx % 1);
// "8 - idx % 1 * 8" generates the sequence 2, 4, 6, 8
output += map.charAt(63 & block >> 8 - idx % 1 * 8)
) {
charCode = input.charCodeAt(idx += 3/4);
if (charCode > 0xFF) {
throw new InvalidCharacterError("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.");
}
block = block << 8 | charCode;
}
return output;
});

// decoder
// [https://gist.github.com/1020396] by [https://github.com/atk]
object.atob || (
object.atob = function (input) {
input = input.replace(/=+$/, '')
if (input.length % 4 == 1) {
throw new InvalidCharacterError("'atob' failed: The string to be decoded is not correctly encoded.");
}
for (
// initialize result and counters
var bc = 0, bs, buffer, idx = 0, output = '';
// get next character
buffer = input.charAt(idx++);
// character found in table? initialize bit storage and add its ascii value;
~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer,
// and if not first of each 4 characters,
// convert the first 8 bits to one ascii character
bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0
) {
// try to find character in table (0-63, not found => -1)
buffer = chars.indexOf(buffer);
}
return output;
});

}());
144 changes: 118 additions & 26 deletions js/irc/baseircclient.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,40 +22,88 @@ qwebirc.irc.BaseIRCClient = new Class({
this.toIRCLower = qwebirc.irc.RFC1459toIRCLower;

this.nickname = connOptions.nickname;
this.authUser = connOptions.authUser;
this.authSecret = connOptions.authSecret;
this.lowerNickname = this.toIRCLower(this.nickname);

this.__signedOn = false;
this.__connected = false;
this.caps = {};
this.sasl_timeout = false;
this.pmodes = {b: qwebirc.irc.PMODE_LIST, l: qwebirc.irc.PMODE_SET_ONLY, k: qwebirc.irc.PMODE_SET_UNSET, o: qwebirc.irc.PMODE_SET_UNSET, v: qwebirc.irc.PMODE_SET_UNSET};
this.channels = {}
this.nextctcp = 0;

connOptions.initialNickname = this.nickname;
connOptions.onRecv = this.dispatch.bind(this);
this.connection = new qwebirc.irc.IRCConnection(session, connOptions);

this.send = this.connection.send.bind(this.connection);
this.connect = this.connection.connect.bind(this.connection);
this.disconnect = this.connection.disconnect.bind(this.connection);
this.connections = [];
for(var x = 0; x < conf.frontend.connections.length; x++) {
switch(conf.frontend.connections[x]) {
case "ajax": this.connections.unshift(qwebirc.irc.IRCConnection); break;
case "flash": this.connections.unshift(qwebirc.irc.FlashConnection); break;
case "websocket": this.connections.unshift(qwebirc.irc.WSConnection); break;
}
}

this.setupGenericErrors();
},
send: function(data) {
return this.connection.send(data);
},
connect: function() {
this.tryConnect();
},
disconnect: function() {
this.connection.disconnect();
},
tryConnect: function() {
var options = {};
var Connection = this.connections.pop();
if(Connection) {
options.initialNickname = this.nickname;
options.onRecv = this.dispatch.bind(this);
this.connection = new Connection(this.session, options);
this.connection.connect();
} else {
this.disconnected("Unable to connect")
}
},
dispatch: function(data) {
var message = data[0];
if(message == "connect") {
this.__connected = true;
this.connected();
} else if(message == "disconnect") {
if(data.length == 0) {
this.disconnected("No error!");
} else {
this.disconnected(data[1]);
}
this.disconnect();
if(this.__connected) {
this.disconnect();
} else {
this.tryConnect();
}
} else if(message == "c") {
var command = data[1].toUpperCase();
var line = data[1];
var command = "";
var prefix = "";
var params = [];
var trailing = "";

if (line[0] == ":") {
var index = line.indexOf(" ");
prefix = line.substring(1, index);
line = line.substring(index + 1);
}
if (line.indexOf(" :") != -1) {
var index = line.indexOf(" :");
trailing = line.substring(index + 2);
params = line.substring(0, index).split(" ");
params.push(trailing);
} else {
params = line.split(" ");
}
command = params.splice(0, 1)[0].toUpperCase();

var prefix = data[2];
var sl = data[3];
var n = qwebirc.irc.Numerics[command];

var x = n;
Expand All @@ -65,11 +113,11 @@ qwebirc.irc.BaseIRCClient = new Class({
var o = this["irc_" + n];

if(o) {
var r = o.run([prefix, sl], this);
var r = o.run([prefix, params], this);
if(!r)
this.rawNumeric(command, prefix, sl);
this.rawNumeric(command, prefix, params);
} else {
this.rawNumeric(command, prefix, sl);
this.rawNumeric(command, prefix, params);
}
}
},
Expand Down Expand Up @@ -102,22 +150,63 @@ qwebirc.irc.BaseIRCClient = new Class({
}
},
irc_AUTHENTICATE: function(prefix, params) {
/* Silently hide. */
this.send("AUTHENTICATE "+btoa([this.authUser, this.authUser, this.authSecret].join('\0')));
return true;
},
irc_saslFinished: function(prefix, params) {
this.send("CAP END");
$clear(this.sasl_timeout);
return false;
},
__saslTimeout: function() {
this.send("CAP END");
},
irc_CAP: function(prefix, params) {
if(params[1] == "ACK") {
var capslist = [];
if (params[2] == "*")
capslist = params[3].split(" ");
else
capslist = params[2].split(" ");
var caplist;
switch(params[1]) {
case "ACK":
if (params[2] == "*") {
caplist = params[3].split(" ");
} else {
caplist = params[2].split(" ");
}

var i;
for (i = 0; i < capslist.length; i++) {
this.caps[capslist[i]] = true;
if (capslist[i] == "sasl")
this.rawNumeric("AUTHENTICATE", prefix, ["*", "Attempting SASL authentication..."]);
for (i = 0; i < caplist.length; i++)
this.caps[caplist[i]] = true

if (params[2] != "*") {
if(this.caps.sasl && this.authUser) {
this.send("AUTHENTICATE "+conf.atheme.sasl_type);
this.sasl_timeout = this.__saslTimeout.delay(15000, this);
} else {
this.send("CAP END");
}
}
break;
case "NAK":
this.send("CAP END");
break;
case "LS":
if (params[2] == "*") {
caplist = params[3].split(" ");
} else {
caplist = params[2].split(" ");
}

for (i = 0; i < caplist.length; i++) {
if (caplist[i] == "sasl")
this.caps[caplist[i]] = false;
if (caplist[i] == "multi-prefix")
this.caps[caplist[i]] = false;
}

if (params[2] != "*") {
caplist = Object.keys(this.caps);
if(caplist.length) {
this.send("CAP REQ :"+caplist.join(" "));
} else {
this.send("CAP END");
}
}
}

Expand Down Expand Up @@ -514,6 +603,9 @@ qwebirc.irc.BaseIRCClient = new Class({
this.irc_ERR_CHANOPPRIVSNEEDED = this.irc_ERR_CANNOTSENDTOCHAN = this.irc_genericError;
this.irc_ERR_NOSUCHNICK = this.irc_genericQueryError;
this.irc_ERR_NICKNAMEINUSE = this.irc_ERR_UNAVAILRESOURCE = this.irc_genericNickInUse;
this.irc_RPL_LOGGEDIN = this.irc_ERR_NICKLOCKED = this.irc_saslFinished;
this.irc_ERR_SASLFAIL = this.irc_ERR_SASLTOOLONG = this.irc_saslFinished;
this.irc_ERR_SASLABORTED = this.irc_ERR_SASLALREADY = this.irc_saslFinished;
return true;
},
irc_RPL_AWAY: function(prefix, params) {
Expand Down
Loading