Alpha commit. Time to tidy up..
authorDarren <darren@darrenwhitlen.com>
Tue, 12 Jul 2011 21:01:42 +0000 (22:01 +0100)
committerDarren <darren@darrenwhitlen.com>
Tue, 12 Jul 2011 21:01:42 +0000 (22:01 +0100)
40 files changed:
class_irc.php [new file with mode: 0644]
class_ircconnection.php [new file with mode: 0644]
class_session.php [new file with mode: 0644]
common.php [new file with mode: 0644]
config.php [new file with mode: 0644]
css/border-radius.htc [new file with mode: 0644]
css/default.css [new file with mode: 0644]
css/default_1.css [new file with mode: 0644]
css/touchscreen_tweaks.css [new file with mode: 0644]
css/ui.css [new file with mode: 0644]
dev_processClientMessage.php [new file with mode: 0644]
embed.php [new file with mode: 0644]
img/logo.png [new file with mode: 0644]
img/more.png [new file with mode: 0644]
img/redcross.png [new file with mode: 0644]
img/trans_back.png [new file with mode: 0644]
index.html [new file with mode: 0644]
index.lighttpd.html [new file with mode: 0644]
index.php [new file with mode: 0644]
index_old.php [new file with mode: 0644]
inf.php [new file with mode: 0644]
irc_session.php [new file with mode: 0644]
ircd_profiles/hybrid.php [new file with mode: 0644]
ircd_profiles/rfc_1459.php [new file with mode: 0644]
ircd_profiles/rfc_2812.php [new file with mode: 0644]
js/front.js [new file with mode: 0644]
js/front_backup.js [new file with mode: 0644]
js/gateway.js [new file with mode: 0644]
js/jquery.json-2.2.min.js [new file with mode: 0644]
js/touchscreen_tweaks.js [new file with mode: 0644]
js/util.js [new file with mode: 0644]
layout.html [new file with mode: 0644]
plugins.js [new file with mode: 0644]
plugins/console.php [new file with mode: 0644]
plugins/dev.php [new file with mode: 0644]
plugins/plugin.php [new file with mode: 0644]
poll.php [new file with mode: 0644]
test.html [new file with mode: 0644]
ws.html [new file with mode: 0644]
ws.php [new file with mode: 0644]

diff --git a/class_irc.php b/class_irc.php
new file mode 100644 (file)
index 0000000..9903031
--- /dev/null
@@ -0,0 +1,415 @@
+<?php\r
+\r
+class SocketStream {\r
+    var $stream;\r
+    function __construct($host = null, $port = null, $blocking = true) {\r
+        if(!$host && !$port)\r
+            return;\r
+        $scheme = 'tcp';\r
+        if($host{0}=='!')\r
+        {\r
+                $scheme = 'ssl';\r
+                $port = 6697;\r
+                $host = substr($host, 1);\r
+        }    \r
+        $this->stream = @stream_socket_client("{$scheme}://$host:$port", $errno, $errstr, 4);\r
+        if(!$this->stream)\r
+        {\r
+            debug("Error creating socket: $host $errstr\n");\r
+            return;\r
+        }\r
+//        socket_set_timeout($this->stream, 30);\r
+        stream_set_blocking($this->stream, $blocking);\r
+    }\r
+    function __destruct()\r
+    {\r
+        if($this->stream)\r
+        {\r
+            fclose($this->stream);\r
+            $this->stream = null;\r
+        }\r
+    }\r
+    function SetBlocking($blocking = true)\r
+    {\r
+        if(!$this->stream)\r
+            return false;\r
+        stream_set_blocking($this->stream, $blocking);\r
+    }\r
+    function SetTimeout($timeout, $ms=0)\r
+    {\r
+        if(!$this->stream)\r
+            return false;\r
+        stream_set_timeout($this->stream, $timeout, $ms);\r
+    }\r
+    function Eof()\r
+    {\r
+        if(!$this->stream)\r
+            return true;\r
+        $x = stream_get_meta_data($this->stream);\r
+        if($x['eof'] && $x['unread_bytes']==0 && feof($this->stream))\r
+            return true;\r
+        return false;\r
+    }\r
+    function Read($length = 512, $type = PHP_BINARY_READ)\r
+    {\r
+        if(!$this->stream) return false;\r
+        if($type == PHP_NORMAL_READ){\r
+            $ret = fgets($this->stream, $length);\r
+        } elseif($type == PHP_BINARY_READ) {\r
+            $ret = fread($this->stream, $length);\r
+        }\r
+               //file_put_contents('/home/wok/public_html/kiwi/rawlog', '> '.$ret, FILE_APPEND);\r
+               return $ret;\r
+    }\r
+    function Write($data)\r
+    {\r
+        global $totalup;\r
+        if(!$this->stream) return false;\r
+               \r
+               //file_put_contents('/home/wok/public_html/kiwi/rawlog', '< '.$data, FILE_APPEND);\r
+        $x = @fwrite($this->stream, $data);\r
+        return $x;\r
+    }\r
+    function GetInfo()\r
+    {\r
+        if(!$this->stream)\r
+            return false;\r
+        return array('local'=>stream_socket_get_name($this->stream,false),'remote'=>stream_socket_get_name($this->stream, true));\r
+    }\r
+}\r
+class SocketStreamSSL extends SocketStream\r
+{\r
+    function __construct($host = null, $port = null, $blocking = true)\r
+    {\r
+        if(!$host && !$port)\r
+            return;\r
+        $this->stream = @stream_socket_client("ssl://$host:$port", $errno, $errstr, 4);\r
+        if(!$this->stream)\r
+        {\r
+            debug("Error creating socket: $host $errstr\n");\r
+            return;\r
+        }\r
+//        socket_set_timeout($this->stream, 30);\r
+        stream_set_blocking($this->stream, $blocking);\r
+    }\r
+}\r
+class IRC\r
+{\r
+    var $stream;\r
+    var $url, $urlinfo;\r
+    var $nick;\r
+    protected $msgqueue = array();\r
+    protected $dccs = array();\r
+    private $reconnect=0;\r
+    private $nextping=0;\r
+    public $modes;\r
+    public $chanlist;\r
+       public $connected;\r
+    function __construct($url, $kiwi=null, $opts=array()){             \r
+        $this->url = $url;\r
+        $urlinfo = parse_url($url);\r
+        $this->urlinfo = $urlinfo;\r
+        if(!ereg("^ircs?$", $urlinfo['scheme']))\r
+            return false;\r
+        $ssl = ($urlinfo['scheme']=='ircs');\r
+        $host = $urlinfo['host'];\r
+        $port = isset($urlinfo['port'])?$urlinfo['port']:6667;\r
+        $this->nick = $nick = isset($urlinfo['user'])?$urlinfo['user']:'kiwi_user|'.(string)rand(0,9999);\r
+        //$ident = isset($urlinfo['pass'])?$urlinfo['pass']:$nick;\r
+               $ident = (isset($opts['ident'])) ? "{$opts['ident']}_kiwi" : "{$nick}_kiwi";\r
+        $chans = false;\r
+        if(isset($urlinfo['path'])){\r
+            $path = trim($urlinfo['path'], "/");\r
+            $chans = explode(",", $path); //"#".str_replace(array(":", ","), array(" ", ",#"), $path);\r
+        }\r
+               $this->connected = false;\r
+        if($ssl)\r
+            $this->stream = new SocketStreamSSL($host, $port, false);\r
+        else\r
+            $this->stream = new SocketStream($host, $port, false);\r
+        if(!$this->stream || !$this->stream->stream){\r
+            return false;\r
+        }\r
+               \r
+               // Send the login data\r
+               if($kiwi != null) $this->stream->Write($kiwi."\r\n");\r
+        if(isset($urlinfo['fragment'])) $this->stream->Write("PASS {$urlinfo['fragment']}\r\n");\r
+               \r
+        $this->stream->Write("NICK $nick\r\n");\r
+        $this->stream->Write("USER $ident 0 0 :$nick\r\n");\r
+        //if($chans){\r
+        //    foreach($chans as $chan)\r
+        //        $this->stream->Write("JOIN ".str_replace(":", " ", $chan)."\r\n");\r
+        //} else {\r
+                       $chans = array();\r
+               //}\r
+        $this->chans = $chans;\r
+               $this->connected = true;\r
+        return true;\r
+    }\r
+    function __destruct(){\r
+        if($this->stream){\r
+            $this->stream->Write("QUIT :kiwi\r\n");    \r
+        }\r
+    }\r
+    function Process(){\r
+        if((!$this->stream || !$this->stream->stream)){\r
+            if(time() > $this->reconnect) {\r
+            $this->__construct($this->url);\r
+            $this->reconnect = time() + 10;\r
+            }\r
+            usleep(50000);\r
+            return;\r
+        }\r
+        if($this->stream->Eof()){\r
+            $this->stream = null;\r
+            return;\r
+        }\r
+        $r=array($this->stream->stream);\r
+               $w = $e = array();\r
+        if(($num=@stream_select($r, $w,$e,0,50000))===false){\r
+            \r
+        } elseif($num>0){\r
+            $data=$this->stream->Read(512, PHP_NORMAL_READ);\r
+               //deb($data);\r
+            if(preg_match("/^(?::(?:([a-z0-9\x5B-\x60\x7B-\x7D\.\-]+)|([a-z0-9\x5B-\x60\x7B-\x7D\.\-]+)!([a-z0-9~\.\-_|]+)@([a-z0-9\.\-:]+)) )?([a-z0-9]+)(?:(?: ([^:]+))?(?: :(.+))?)$/i", $data, $regs))\r
+            {\r
+                    unset($prefix, $nick, $ident, $hostname, $command, $params, $trailing, $flarp);\r
+                    $prefix = $regs[1];\r
+                    $nick = $regs[2];\r
+                    $ident = $regs[3];\r
+                    $hostname = $regs[4];\r
+                    $command = $regs[5];\r
+                    $params = isset($regs[6]) ? $regs[6] : '';\r
+                    $trailing = trim(isset($regs[7]) ? $regs[7] : '');\r
+                    $flarp = compact('prefix', 'nick', 'ident', 'hostname', 'command', 'params', 'trailing');\r
+                    $this->ProcessMessage($flarp);\r
+            }\r
+        }\r
+        else\r
+        {\r
+            if(sizeof($this->msgqueue) && (microtime(true)>($this->lastqueuewrite+0.1)) && ($msg=array_shift($this->msgqueue)))\r
+            {    $this->lastqueuewrite = microtime(true); $this->stream->Write($msg); }\r
+            foreach($this->dccs as $k=>&$dcc)\r
+            {\r
+                if(!$dcc->Process())\r
+                {\r
+                    if($dcc->errorinfo)\r
+                        $this->SendNotice($dcc->errorinfo['nick'], $dcc->errorinfo['text']);\r
+                    unset($this->dccs[$k]);\r
+                }\r
+            }\r
+            if(($now=microtime(1))>$this->nextping)\r
+            {\r
+                $this->stream->Write("PONG x\r\n");\r
+                $this->nextping = $now + 150;\r
+            }\r
+        }\r
+    }\r
+    function ProcessMessage($message)\r
+    {\r
+        //echo $message['command']."\n";\r
+        switch($message['command'])\r
+        {\r
+            case "PING":\r
+                $this->stream->Write("PONG {$message['trailing']}\r\n");\r
+            break;\r
+            case "PRIVMSG":\r
+                $this->ProcessPrivMsg($message);\r
+            break;\r
+            case "001":\r
+            if($this->chans)\r
+                foreach($this->chans as $chan)\r
+                    $this->stream->Write("JOIN ".str_replace(":", " ", $chan)."\r\n");\r
+            break;\r
+            case "443":\r
+                $newnick = 'kiwi_user|'.rand(0,99999);\r
+                $this->SendCmd("NICK ".$newnick);\r
+                $this->nick = $newnick;\r
+            break;\r
+            case "MODE":\r
+                $this->OnMODE($message);\r
+            break;\r
+            case "353":\r
+                $this->On353($message);\r
+                               break;\r
+                       case "QUIT":\r
+                               $chan = $message['params'];\r
+                               foreach($this->chanlist as &$chan){\r
+                                       if(isset($chan['userlist'][$message['nick']]))\r
+                                               unset($chan['userlist'][$message['nick']]);\r
+                               }\r
+                               break;\r
+                       case "PART":\r
+                               $chan = $message['params'];\r
+                               //debug("Parting {$message['nick']} from $chan");\r
+                               if(isset($this->chanlist[ltrim($chan, "#")])){\r
+                                       unset($this->chanlist[ltrim($chan, "#")]['userlist'][$message['nick']]);\r
+                               }\r
+                               break;\r
+                       case "JOIN":\r
+                               $chan = $message['trailing'];\r
+                               $nick = array($message['nick'] => '');\r
+                               $nicklist = is_array($this->chanlist[ltrim($chan, "#")]['userlist']) ? $this->chanlist[ltrim($chan, "#")]['userlist'] :\r
+                                                                                                                                                                               array();\r
+                               \r
+                               $this->chanlist[ltrim($chan, "#")]['userlist'] = array_merge($nicklist, $nick);\r
+                               break;\r
+                       case "NICK":\r
+                               if($message['nick'] == $this->nick){\r
+                                       $this->nick = $message['trailing'];\r
+                               }\r
+                               foreach($this->chanlist as $chan_name => $chan){\r
+                                       foreach($chan['userlist'] as $nick => $status){\r
+                                               if($nick == $message['nick']){\r
+                                                       $this->chanlist[$chan_name]['userlist'][$message['trailing']] = $this->chanlist[$chan_name]['userlist'][$message['nick']];\r
+                                                       unset($this->chanlist[$chan_name]['userlist'][$message['nick']]);\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+                               }\r
+                               break;\r
+        }\r
+    }\r
+    function OnMODE($message){\r
+               if($message['params'] == $this->nick) {\r
+                       $modes = $message['trailing'];\r
+                       $size = strlen($modes);\r
+                       $add = 1;\r
+                       for($i=0;$i<$size;$i++){\r
+                               if($modes{$i} == '+'){\r
+                                       $add = 1;\r
+                                       continue;\r
+                               } elseif($modes{$i} == '-'){\r
+                                       $add = 0;\r
+                                       continue;\r
+                               }\r
+                               if($add && strpos($this->modes, $modes{$i}) === false)\r
+                                       $this->modes.=$modes{$i};\r
+                               if(!$add && strpos($this->modes, $modes{$i}))\r
+                                       $this->modes = str_replace($modes{$i}, "", $this->modes);\r
+                       }\r
+               }\r
+               $params = $message['params'];\r
+               $chan = trim(strtok($params, ' '));\r
+               if(in_array(trim($chan,'#'), $this->chans)){\r
+                       $modes = strtok(' ');\r
+                       $therest = explode(" ", trim(strtok('')));\r
+                       $size = strlen($modes);\r
+                       $add = 1;\r
+                       $offset = 0;\r
+                       for($i=0;$i<$size;$i++){\r
+                               if($modes{$i} == '+'){\r
+                                       $add = 1;\r
+                                       $offset--;\r
+                                       continue;\r
+                               } elseif($modes{$i} == '-'){\r
+                                       $add = 0;\r
+                                       $offset--;\r
+                                       continue;\r
+                               }\r
+                                       if($modes[$i]=='v')\r
+                                       {\r
+                                               $user = $therest[$i+$offset];\r
+                                               if($add)\r
+                                               {\r
+                                                       if(stripos($this->chanlist[trim($chan,'#')]['userlist'][$user], '+')===false)\r
+                                                               $this->chanlist[trim($chan,'#')]['userlist'][$user] .= '+';\r
+                                               }\r
+                                               else\r
+                                                       $this->chanlist[trim($chan,'#')]['userlist'][$user] = str_replace('+','',$this->chanlist[trim($chan,'#')]['userlist'][$user]);\r
+                                               continue;\r
+                                       }\r
+                                       if($modes[$i]=='o')\r
+                                       {\r
+                                               $user = $therest[$i+$offset];\r
+                                               if($add)\r
+                                               {\r
+                                                       if(stripos($this->chanlist[trim($chan,'#')]['userlist'][$user], '@')===false)\r
+                                                               $this->chanlist[trim($chan,'#')]['userlist'][$user] .= '@';\r
+                                               }\r
+                                               else\r
+                                                       $this->chanlist[trim($chan,'#')]['userlist'][$user] = str_replace('@','',$this->chanlist[trim($chan,'#')]['userlist'][$user]);\r
+                                               continue;\r
+                                       }\r
+                                       if($modes[$i]=='h')\r
+                                       {\r
+                                               $user = $therest[$i+$offset];\r
+                                               if($add)\r
+                                               {\r
+                                                       if(stripos($this->chanlist[trim($chan,'#')]['userlist'][$user], '%')===false)\r
+                                                               $this->chanlist[trim($chan,'#')]['userlist'][$user] .= '%';\r
+                                               }\r
+                                               else\r
+                                                       $this->chanlist[trim($chan,'#')]['userlist'][$user] = str_replace('%','',$this->chanlist[trim($chan,'#')]['userlist'][$user]);\r
+                                               continue;\r
+                                       }\r
+                                       if($modes[$i]=='q')\r
+                                       {\r
+                                               $user = $therest[$i+$offset];\r
+                                               if($add)\r
+                                               {\r
+                                                       if(stripos($this->chanlist[trim($chan,'#')]['userlist'][$user], '~')===false)\r
+                                                               $this->chanlist[trim($chan,'#')]['userlist'][$user] .= '~';\r
+                                               }\r
+                                               else\r
+                                                       $this->chanlist[trim($chan,'#')]['userlist'][$user] = str_replace('~','',$this->chanlist[trim($chan,'#')]['userlist'][$user]);\r
+                                               continue;\r
+                                       }\r
+                       }\r
+               }\r
+               \r
+       }\r
+       function On353($message){\r
+               // Response to NAMES command\r
+               list($nick,,$chan) = explode(" ", $message['params']);\r
+               $nicks = explode(" ", $message['trailing']);\r
+               $prefixes = array('~', '&', '@','%','+');\r
+               $nicklist = array();\r
+               foreach($nicks as $nick){\r
+                       if(in_array($nick{0}, $prefixes)){\r
+                               $prefix = $nick{0};\r
+                               $nick = substr($nick,1);\r
+                       }\r
+                       else\r
+                               $prefix = '';\r
+                       $nicklist[$nick] = $prefix;\r
+               }\r
+               if(sizeof($nicklist)){\r
+                       // If we havn't got a list of nicks for this channel yet, create it\r
+                       if(!isset($this->chanlist[ltrim($chan, "#")]['userlist'])){\r
+                               $this->chanlist[ltrim($chan, "#")]['userlist'] = array();\r
+                       }\r
+                       $this->chanlist[ltrim($chan, "#")]['userlist'] = array_merge($this->chanlist[ltrim($chan, "#")]['userlist'], $nicklist);\r
+               }\r
+       }\r
+    function ProcessPrivMsg($message){\r
+                //$cmd = strtok($message['trailing'], ' ');\r
+                //switch($cmd){\r
+                //}\r
+    }\r
+    function SendMessage($dest, $text){\r
+        if(!$this->stream)\r
+            return false;\r
+        $this->stream->Write("PRIVMSG $dest :$text\r\n");\r
+    }\r
+       function Join($chan){\r
+               $this->stream->Write("JOIN ".str_replace(":", " ", $chan)."\r\n");\r
+       }\r
+    function SendNotice($dest, $text)\r
+    {\r
+        $this->stream->Write("NOTICE $dest :$text\r\n");\r
+    }\r
+    function QueueMessage($dest, $text)\r
+    {\r
+        $this->msgqueue[] = "PRIVMSG $dest :$text\r\n";\r
+    }\r
+    function QueueNotice($dest, $text)\r
+    {\r
+        $this->msgqueue[] = "NOTICE $dest :$text\r\n";        \r
+    }\r
+    function GetMainChan()\r
+    {\r
+        return '#'.$this->chans[0];    \r
+    }\r
+}
\ No newline at end of file
diff --git a/class_ircconnection.php b/class_ircconnection.php
new file mode 100644 (file)
index 0000000..62b8b2a
--- /dev/null
@@ -0,0 +1,394 @@
+<?php\r
+\r
+       class IRCConnection extends IRC {\r
+               \r
+               var $scrollback = array();\r
+               var $server_options = array();\r
+               \r
+               function ProcessMessage($message){\r
+                       global $config;\r
+                       global $buffer;\r
+                       global $send_debug;\r
+                       \r
+                       //debug("Message: ".print_r($message, 1));\r
+                       deb(print_r($message, 1));\r
+                       \r
+                       if(strlen($message['trailing']) && ord($message['trailing'][0]) == 1){\r
+                               $message['trailing'] = trim($message['trailing'], chr(1));\r
+                               if(strtoupper(substr($message['trailing'], 1, 6)) == 'ACTION'){\r
+                                       $message['command'] = 'ACTION';\r
+                               } else {\r
+                                       $message['command'] = 'CTCP';\r
+                               }\r
+                       }\r
+                       \r
+                       switch(strtoupper($message['command'])){\r
+                               case '001':\r
+                                       $buffer[] = json_encode(array(\r
+                                               'event'=>'connect',\r
+                                               'connected'=>true,\r
+                                               'host'=>$args['server']\r
+                                       ));\r
+                                       break;\r
+                                       \r
+                               case '005':\r
+                                       $opts = explode(' ', $message['params']);\r
+                                       $to_client = array();\r
+                                       foreach($opts as $pair){\r
+                                               $opt = explode('=', $pair, 2);\r
+                                               $name = strtoupper($opt[0]);\r
+                                               $val = isset($opt[1]) ? $opt[1] : true;\r
+                                               $this->server_options[$name] = $val;\r
+                                               \r
+                                               if(in_array($name, array('NETWORK', 'PREFIX', 'CHANTYPES'))){\r
+                                                       // Put the user prefixes (~&@ etc etc) into a more usable format\r
+                                                       if($name == 'PREFIX'){\r
+                                                               $matches = null;\r
+                                                               preg_match('/\(([^)]*)\)(.*)/', $val, $matches);\r
+                                                               \r
+                                                               $val = array();\r
+                                                               if(count($matches) == 3){\r
+                                                                       for($i=0; $i<strlen($matches[1]); $i++){\r
+                                                                               $val[$matches[2][$i]] = $matches[1][$i];\r
+                                                                       }\r
+                                                               }\r
+                                                       }\r
+                                                       $to_client[$name] = $val;\r
+                                               }\r
+                                       }\r
+                                       \r
+                                       $data = array(\r
+                                               'event' => 'options',\r
+                                               'server' => '',\r
+                                               'options' => $to_client\r
+                                       );\r
+                                       $buffer[] = json_encode($data);\r
+                                       break;\r
+                                       \r
+                               case RPL_WHOISUSER:\r
+                               case RPL_WHOISSERVER:\r
+                               case RPL_WHOISOPERATOR:\r
+                               case RPL_WHOISIDLE:\r
+                               case RPL_ENDOFWHOIS:\r
+                               case RPL_WHOISCHANNELS:\r
+                               case RPL_WHOISMODES:\r
+                                       $tmp = explode(' ', $message['params']);\r
+                                       $tmp = $tmp[1];\r
+                                       $data = array(\r
+                                               'event' => 'whois',\r
+                                               'server' => '',\r
+                                               'nick' => $tmp,\r
+                                               'msg' => $message['trailing']\r
+                                       );\r
+                                       $buffer[] = json_encode($data);\r
+                                       break;\r
+                               \r
+                               case RPL_MOTD:\r
+                                       $data = array(\r
+                                               'event' => 'motd',\r
+                                               'server' => '',\r
+                                               'msg' => $message['trailing']\r
+                                       );\r
+                                       $buffer[] = json_encode($data);\r
+                                       break;\r
+                                       \r
+                               case "353":\r
+                                       // NAMES command reply\r
+                                       list($nick,,$chan) = explode(" ", $message['params']);\r
+                                       $nicks = explode(" ", $message['trailing']);\r
+                                       \r
+                                       $data = array(\r
+                                               'event' => 'userlist',\r
+                                               'server' => '',\r
+                                               'users' => array(),\r
+                                               'channel' => $chan\r
+                                       );\r
+                                       \r
+                                       $prefixes = array('~', '&', '@','%','+');\r
+                                       $nicklist = array();\r
+                                       $i = 0;\r
+                                       foreach($nicks as $nick){\r
+                                               if(in_array($nick{0}, $prefixes)){\r
+                                                       $prefix = $nick{0};\r
+                                                       $nick = substr($nick,1);\r
+                                               } else {\r
+                                                       $prefix = '';\r
+                                               }\r
+                                               $nicklist[$nick] = $prefix;\r
+                                               \r
+                                               if($i==50){\r
+                                                       $tmp = $data;\r
+                                                       $tmp['users'] = $nicklist;\r
+                                                       $buffer[] = json_encode($tmp);\r
+                                                       unset($tmp);\r
+                                                       $nicklist = array();\r
+                                               }\r
+                                               \r
+                                               $i++;\r
+                                       }\r
+                                       \r
+                                       if(count($nicklist)){\r
+                                               $tmp = $data;\r
+                                               $tmp['users'] = $nicklist;\r
+                                               $buffer[] = json_encode($tmp);\r
+                                       }\r
+                                       //deb(print_r($data, 1));\r
+                                       break;\r
+                                       \r
+                               case '366':\r
+                                       list(,$chan) = explode(' ', $message['params']);\r
+                                       $data = array(\r
+                                               'event' => 'userlist_end',\r
+                                               'server' => '',\r
+                                               'channel' => $chan\r
+                                       );\r
+                                       $buffer[] = json_encode($data);\r
+                                       break;\r
+                                       \r
+                               case ERR_LINKCHANNEL:\r
+                                       list($nick, $source_chan, $dest_chan) = explode(' ', $message['params']);\r
+                                       $data = array(\r
+                                               'event' => 'channel_redirect',\r
+                                               'from' => $source_chan,\r
+                                               'to' => $dest_chan\r
+                                       );\r
+                                       $buffer[] = json_encode($data);\r
+                                       break;\r
+                                       \r
+                               case ERR_NOSUCHNICK:\r
+                                       //TODO: shit\r
+                                       break;\r
+                                       \r
+                               case "JOIN":\r
+                                       $data = array(\r
+                                               'event' => 'join',\r
+                                               'nick' => $message['nick'],\r
+                                               'ident' => $message['ident'],\r
+                                               'hostname' => $message['hostname'],\r
+                                               'channel' => $message['trailing'],\r
+                                       );\r
+                                       $buffer[] = json_encode($data);\r
+                                       \r
+                                       deb("JOIN: {$message['nick']} / {$this->nick}");\r
+                                       if($message['nick'] == $this->nick){\r
+                                               $this->stream->Write("NAMES {$message['trailing']}\r\n");\r
+                                       }\r
+                                       \r
+                                       break;\r
+                                       \r
+                               case "PART":\r
+                                       $data = array(\r
+                                               'event' => 'part',\r
+                                               'nick' => $message['nick'],\r
+                                               'ident' => $message['ident'],\r
+                                               'hostname' => $message['hostname'],\r
+                                               'channel' => trim($message['params']),\r
+                                               'message' => $message['trailing'],\r
+                                       );\r
+                                       $buffer[] = json_encode($data);\r
+                                       break;\r
+                                       \r
+                               case "KICK":\r
+                                       $tmp = explode(' ', $message['params']);\r
+                                       \r
+                                       $data = array(\r
+                                               'event' => 'kick',\r
+                                               'kicked' => $tmp[1],\r
+                                               'nick' => $message['nick'],\r
+                                               'ident' => $message['ident'],\r
+                                               'hostname' => $message['hostname'],\r
+                                               'channel' => trim($tmp[0]),\r
+                                               'message' => $message['trailing'],\r
+                                       );\r
+                                       $buffer[] = json_encode($data);\r
+                                       break;\r
+                                       \r
+                               case "QUIT":\r
+                                       $data = array(\r
+                                               'event' => 'quit',\r
+                                               'nick' => $message['nick'],\r
+                                               'ident' => $message['ident'],\r
+                                               'hostname' => $message['hostname'],\r
+                                               'message' => $message['trailing'],\r
+                                       );\r
+                                       $buffer[] = json_encode($data);\r
+                                       break;\r
+                                       \r
+                               case "NOTICE":\r
+                                       $data = array(\r
+                                               'event' => 'notice',\r
+                                               'nick' => $message['nick'],\r
+                                               'ident' => $message['ident'],\r
+                                               'hostname' => $message['hostname'],\r
+                                               'msg' => $message['trailing'],\r
+                                       );\r
+                                       $buffer[] = json_encode($data);\r
+                                       break;\r
+                                       \r
+                               case "NICK":\r
+                                       $data = array(\r
+                                               'event' => 'nick',\r
+                                               'nick' => $message['nick'],\r
+                                               'ident' => $message['ident'],\r
+                                               'hostname' => $message['hostname'],\r
+                                               'newnick' => $message['trailing'],\r
+                                       );\r
+                                       $buffer[] = json_encode($data);\r
+                                       break;\r
+                                       \r
+                               case 'TOPIC':\r
+                                       $data = array(\r
+                                               'event' => 'topic',\r
+                                               'nick' => $message['nick'],\r
+                                               'channel' => $message['params'],\r
+                                               'topic' => $message['trailing']\r
+                                       );\r
+                                       $buffer[] = json_encode($data);\r
+                                       break;\r
+                                       \r
+                               case '332':\r
+                                       $tmp = explode(' ', $message['params']);\r
+                                       $data = array(\r
+                                               'event' => 'topic',\r
+                                               'nick' => '',\r
+                                               'channel' => $tmp[1],\r
+                                               'topic' => $message['trailing']\r
+                                       );\r
+                                       $buffer[] = json_encode($data);\r
+                                       break;\r
+                                       \r
+                               case 'ACTION':\r
+                                       $data = array(\r
+                                               'event' => 'action',\r
+                                               'nick' => $message['nick'],\r
+                                               'msg' => trim(substr($message['trailing'], 8), chr(1)),\r
+                                               'channel' => $message['params'],\r
+                                               'time' => time()\r
+                                       );\r
+                                       $buffer[] = json_encode($data);\r
+                                       break;\r
+                                       \r
+                               case 'CTCP':\r
+                                       $data = array(\r
+                                               'event' => 'ctcp',\r
+                                               'nick' => $message['nick'],\r
+                                               'msg' => trim(substr($message['trailing'], 8), chr(1)),\r
+                                               'channel' => $message['params'],\r
+                                               'time' => time()\r
+                                       );\r
+                                       $buffer[] = json_encode($data);\r
+                                       break;\r
+                                       \r
+                               case 'MODE':\r
+                                       $opts = explode(' ', $message['params']);\r
+                                       if(count($opts) == 1){\r
+                                               $effected_nick = $opts[0];\r
+                                               $mode = $message['trailing'];\r
+                                       } elseif(count($opts) == 2){\r
+                                               $channel = $opts[0];\r
+                                               $mode = $opts[1];\r
+                                       } else {\r
+                                               $channel = $opts[0];\r
+                                               $mode = $opts[1];\r
+                                               $effected_nick = $opts[2];\r
+                                       }\r
+                                       \r
+                                       $data = array(\r
+                                               'event' => 'mode',\r
+                                               'nick' => $message['nick'],\r
+                                               'mode' => $mode\r
+                                       );\r
+                                       \r
+                                       if(isset($effected_nick)) $data['effected_nick'] = $effected_nick;\r
+                                       if(isset($channel)) $data['channel'] = $channel;\r
+                                       $buffer[] = json_encode($data);\r
+                                       \r
+                                       break;\r
+                       }\r
+                       \r
+                       if($send_debug){\r
+                               $ret = str_replace('\n', ' ', print_r($message, 1));\r
+                               $data = array(\r
+                                       'event' => 'debug',\r
+                                       'msg' => $ret,\r
+                                       'time' => time()\r
+                               );\r
+                               $buffer[] = json_encode($data);\r
+                       }\r
+                       \r
+                       parent::ProcessMessage($message);\r
+               }\r
+               \r
+               \r
+               \r
+               \r
+               function ProcessPrivMsg($message){\r
+                       global $buffer;\r
+                       global $timeout;\r
+                       global $config;\r
+                       \r
+                       $msg = $message['trailing'];\r
+                       $cmd = strtok($msg, ' ');\r
+                       //debug(print_r($message));\r
+                       //deb(print_r($message, 1));\r
+                       $data = array(\r
+                               'event' => 'msg',\r
+                               'nick' => $message['nick'],\r
+                               'msg' => $message['trailing'],\r
+                               'channel' => $message['params'],\r
+                               'time' => time()\r
+                       );\r
+                       \r
+                       // Save it into the scrollback\r
+                       if($config['scrollback_size']){\r
+                               deb('Size: '.count($this->scrollback));\r
+                               if(count($this->scrollback) >= $config['scrollback_size']){\r
+                                       \r
+                                       for($i=1; $i<count($this->scrollback); $i++){\r
+                                               $this->scrollback[$i-1] = $this->scrollback[$i];\r
+                                       }\r
+                                       /*\r
+                                       for($j=$i; $j<count($this->scrollback); $j++){\r
+                                               unset($this->scrollback[$i]);\r
+                                       }\r
+                                       //unset($this->scrollback[$i]);\r
+                                       */\r
+                                       $pos = count($this->scrollback)-1;\r
+                                       deb('Popped');\r
+                               } else {\r
+                                       $pos = count($this->scrollback);\r
+                               }\r
+                               \r
+                               $this->scrollback[$pos] = $data;\r
+                       }\r
+                       \r
+                       $buffer[] = json_encode($data);\r
+                       parent::ProcessPrivMsg($message);\r
+                       \r
+                       return;\r
+               }\r
+               \r
+               \r
+               private function isChannel($name){\r
+                       return ($name[0] == "#");\r
+               }\r
+               \r
+               \r
+               private function formatMsgToHtml($inp){\r
+                       $tmp = $inp;\r
+                       \r
+                       // Bold\r
+                       if(strpos($tmp, chr(2))){\r
+                               $next = '<b>';\r
+                               while(strpos($tmp, chr(2)) !== false){\r
+                                       $pos = strpos($tmp, chr(2));\r
+                                       $tmp = str_replace($tmp, chr(2), $next);\r
+                                       $next = ($next=='<b>') ? '</b>' : '<b>';\r
+                               }\r
+                               if($next == '</b>') $tmp = $tmp . '</b>';\r
+                       }\r
+                       \r
+                       return $tmp;\r
+               }\r
+               \r
+       }
\ No newline at end of file
diff --git a/class_session.php b/class_session.php
new file mode 100644 (file)
index 0000000..2a31d5d
--- /dev/null
@@ -0,0 +1,115 @@
+<?php\r
+\r
+       class SESSIONS {\r
+               static function exists($session_id){\r
+                       global $config;\r
+                       \r
+                       if($config['memcache_use']){\r
+                               $ret = GLOB::$mc->get('sid_'.$session_id) ? true : false;\r
+                               return $ret;\r
+                       } else {\r
+                               $session_sok = $config['sok_dir'].$config['sok_prefix'].$session_id;\r
+                               return file_exists($session_sok);\r
+                       }\r
+               }\r
+               \r
+               \r
+               \r
+               static function create($client_key){\r
+                       global $config;\r
+                       \r
+                       $temp_id = md5(microtime().rand());\r
+                       \r
+                       if($config['memcache_use']){\r
+                               $host_key = 'hostcount_'.$client_key;\r
+                               $count = (int)GLOB::$mc->get($host_key);\r
+                               if($count > $config['connections_per_host']) return false;\r
+                               \r
+                               // Save the sid into memcached for the ircbot to pick up\r
+                               GLOB::$mc->add('sid_'.$temp_id, $temp_id);\r
+                       }\r
+                       \r
+                       $session_cmd = "{$config['php_path']} {$config['session_script_path']} $temp_id $host_key -h$client_key";\r
+                       //die($session_cmd);\r
+                       $session_status  = `$session_cmd`;\r
+                       \r
+                       if($session_status != 'ok'){\r
+                               debug("Failed creating session socket with: $session_status");\r
+                               GLOB::$mc->delete('sid_'.$temp_id);\r
+                               return false;\r
+                       }\r
+                       \r
+                       if($config['memcache_use']){\r
+                               GLOB::$mc->add($host_key, 0);\r
+                               GLOB::$mc->increment($host_key);\r
+                       }\r
+                       \r
+                       return $temp_id;\r
+               }\r
+               \r
+               \r
+               \r
+               static function open($session_id){\r
+                       global $config;\r
+                       \r
+                       if($config['memcache_use']){\r
+                               $session_sok = GLOB::$mc->get('sid_'.$session_id);\r
+                               if(!$session_sok) return false;\r
+                       } else {\r
+                               $session_sok = $session_id;\r
+                       }\r
+                       $session_sok = $config['sok_dir'].$config['sok_prefix'].$session_sok;\r
+                       \r
+                       $sok = @stream_socket_client('unix://'.$session_sok, $errno, $errstr);\r
+                       \r
+                       if(!$sok) return false;\r
+                       return $sok;\r
+               }\r
+               \r
+               \r
+               \r
+               static function close($session){\r
+                       fclose($session);\r
+               }\r
+               \r
+               \r
+               static function read($session, $block=true){\r
+                       fwrite($session, json_encode(array('method'=>'read')));\r
+                       \r
+                       if(!$block){\r
+                               stream_set_timeout($session, 0, 100000);\r
+                       } else {\r
+                               stream_set_timeout($session, 120, 0);\r
+                       }\r
+                       $data = fgets($session);\r
+                       \r
+                       return $data;\r
+               }\r
+               \r
+               \r
+               \r
+               \r
+               \r
+               static function clStart($session_id){\r
+                       global $config;\r
+                       \r
+                       if($config['memcache_use']){\r
+                               $session_sok = GLOB::$mc->get('sid_'.$session_id);\r
+                               if(!$session_sok) return false;\r
+                       } else {\r
+                               $session_sok = $session_id;\r
+                       }\r
+                       $session_sok = $config['sok_dir'].$config['sok_prefix'].$session_sok;\r
+                       \r
+                       $sok = stream_socket_server('unix://'.$session_sok, $errno, $errstr);\r
+                       if(!$sok) return false;\r
+                       \r
+                       return $sok;\r
+                        \r
+               }\r
+               \r
+               \r
+               static function clStop($session){\r
+                       fclose($session);\r
+               }\r
+       }\r
diff --git a/common.php b/common.php
new file mode 100644 (file)
index 0000000..7085d76
--- /dev/null
@@ -0,0 +1,24 @@
+<?php\r
+\r
+       // Class for storing global vars\r
+       class GLOB {\r
+               // Memcache connection\r
+               static $mc;\r
+       }\r
+       \r
+       \r
+       if($config['memcache_use']){\r
+               GLOB::$mc = new Memcache();\r
+               GLOB::$mc->addServer('localhost', 11211, true, 1);\r
+       }\r
+       \r
+       \r
+       \r
+       function deb($what){\r
+               //if(DEBUG){\r
+               //      echo "$what\n";\r
+               //} else {\r
+                       file_put_contents('/tmp/kiwi_err.log', "$what\n", FILE_APPEND);\r
+               //}\r
+       }\r
+       function debug($what){ deb($what); }
\ No newline at end of file
diff --git a/config.php b/config.php
new file mode 100644 (file)
index 0000000..4783f0a
--- /dev/null
@@ -0,0 +1,25 @@
+<?php\r
+       \r
+       $config = array(\r
+               'sok_dir' => '/tmp/',\r
+               'sok_prefix' => 'kiwi_',\r
+               \r
+               'memcache_use' => true,\r
+               'memcache_addr' => 'localhost',\r
+               'memcache_port' => 11211,\r
+               \r
+               'php_path' => '/usr/bin/php5',\r
+               'session_script_path' => dirname(__FILE__).'/irc_session.php',\r
+               //Unused 'cons_per_host' => 5,\r
+               \r
+               'messages_per_poll' => 30,\r
+               'max_time_per_poll' => 30, //60*2,\r
+               'timeout' => 5, // minimum time in seconds in which the webclient needs to poll the server\r
+               'scrollback_size' => 15,\r
+               'connections_per_host' => 20,\r
+               \r
+               'websocket' => array(\r
+                       'bind_addr'=>'0.0.0.0',\r
+                       'bind_port'=>7777\r
+               )\r
+       );
\ No newline at end of file
diff --git a/css/border-radius.htc b/css/border-radius.htc
new file mode 100644 (file)
index 0000000..c3ae104
--- /dev/null
@@ -0,0 +1,143 @@
+--Do not remove this if you are using--\r
+Original Author: Remiz Rahnas\r
+Original Author URL: http://www.htmlremix.com\r
+Published date: 2008/09/24\r
+\r
+Changes by Nick Fetchak:\r
+- IE8 standards mode compatibility\r
+- VML elements now positioned behind original box rather than inside of it - should be less prone to breakage\r
+Published date : 2009/11/18\r
+\r
+\r
+<public:attach event="oncontentready" onevent="oncontentready('v08vnSVo78t4JfjH')" />\r
+<script type="text/javascript">\r
+\r
+// findPos() borrowed from http://www.quirksmode.org/js/findpos.html\r
+function findPos(obj) {\r
+       var curleft = curtop = 0;\r
+\r
+       if (obj.offsetParent) {\r
+               do {\r
+                       curleft += obj.offsetLeft;\r
+                       curtop += obj.offsetTop;\r
+               } while (obj = obj.offsetParent);\r
+       }\r
+\r
+       return({\r
+               'x': curleft,\r
+               'y': curtop\r
+       });\r
+}\r
+\r
+function oncontentready(classID) {\r
+  if (this.className.match(classID)) { return(false); }\r
+\r
+       if (!document.namespaces.v) { document.namespaces.add("v", "urn:schemas-microsoft-com:vml"); }\r
+\r
+       this.className = this.className.concat(' ', classID);\r
+       var arcSize = Math.min(parseInt(this.currentStyle['-moz-border-radius'] ||\r
+                                       this.currentStyle['-webkit-border-radius'] ||\r
+                                       this.currentStyle['border-radius'] ||\r
+                                       this.currentStyle['-khtml-border-radius']) /\r
+                              Math.min(this.offsetWidth, this.offsetHeight), 1);\r
+       var fillColor = this.currentStyle.backgroundColor;\r
+       var fillSrc = this.currentStyle.backgroundImage.replace(/^url\("(.+)"\)$/, '$1');\r
+       var strokeColor = this.currentStyle.borderColor;\r
+       var strokeWeight = parseInt(this.currentStyle.borderWidth);\r
+       var stroked = 'true';\r
+       if (isNaN(strokeWeight)) {\r
+               strokeWeight = 0;\r
+               strokeColor = fillColor;\r
+               stroked = 'false';\r
+       }\r
+\r
+       this.style.background = 'transparent';\r
+       this.style.borderColor = 'transparent';\r
+\r
+       // Find which element provides position:relative for the target element (default to BODY)\r
+       var el = this;\r
+       var limit = 100, i = 0;\r
+       while ((typeof(el) != 'unknown') && (el.currentStyle.position != 'relative') && (el.tagName != 'BODY')) {\r
+               el = el.parentElement;\r
+               i++;\r
+               if (i >= limit) { return(false); }\r
+       }\r
+       var el_zindex = parseInt(el.currentStyle.zIndex);\r
+       if (isNaN(el_zindex)) { el_zindex = 0; }\r
+       //alert('got tag '+ el.tagName +' with pos '+ el.currentStyle.position);\r
+\r
+       var rect_size = {\r
+               'width': this.offsetWidth - strokeWeight,\r
+               'height': this.offsetHeight - strokeWeight\r
+       };\r
+       var el_pos = findPos(el);\r
+       var this_pos = findPos(this);\r
+       this_pos.y = this_pos.y + (0.5 * strokeWeight) - el_pos.y;\r
+       this_pos.x = this_pos.x + (0.5 * strokeWeight) - el_pos.x;\r
+\r
+       var rect = document.createElement('v:roundrect');\r
+       rect.arcsize = arcSize +'px';\r
+       rect.strokecolor = strokeColor;\r
+       rect.strokeWeight = strokeWeight +'px';\r
+       rect.stroked = stroked;\r
+       rect.style.display = 'block';\r
+       rect.style.position = 'absolute';\r
+       rect.style.top = this_pos.y +'px';\r
+       rect.style.left = this_pos.x +'px';\r
+       rect.style.width = rect_size.width +'px';\r
+       rect.style.height = rect_size.height +'px';\r
+       rect.style.antialias = true;\r
+       rect.style.zIndex = el_zindex - 1;\r
+\r
+       var fill = document.createElement('v:fill');\r
+       fill.color = fillColor;\r
+       fill.src = fillSrc;\r
+       fill.type = 'tile';\r
+\r
+       rect.appendChild(fill);\r
+       el.appendChild(rect);\r
+\r
+       var css = el.document.createStyleSheet();\r
+       css.addRule("v\\:roundrect", "behavior: url(#default#VML)");\r
+       css.addRule("v\\:fill", "behavior: url(#default#VML)");\r
+\r
+       isIE6 = /msie|MSIE 6/.test(navigator.userAgent);\r
+       // IE6 doesn't support transparent borders, use padding to offset original element\r
+       if (isIE6 && (strokeWeight > 0)) {\r
+               this.style.borderStyle = 'none';\r
+               this.style.paddingTop = parseInt(this.currentStyle.paddingTop || 0) + strokeWeight;\r
+               this.style.paddingBottom = parseInt(this.currentStyle.paddingBottom || 0) + strokeWeight;\r
+       }\r
+\r
+       if (typeof(window.rounded_elements) == 'undefined') {\r
+               window.rounded_elements = new Array();\r
+\r
+               if (typeof(window.onresize) == 'function') { window.previous_onresize = window.onresize; }\r
+               window.onresize = window_resize;\r
+       }\r
+       this.element.vml = rect;\r
+       window.rounded_elements.push(this.element);\r
+}\r
+\r
+function window_resize() {\r
+       if (typeof(window.rounded_elements) == 'undefined') { return(false); }\r
+\r
+       for (var i in window.rounded_elements) {\r
+               var el = window.rounded_elements[i];\r
+\r
+               var strokeWeight = parseInt(el.currentStyle.borderWidth);\r
+               if (isNaN(strokeWeight)) { strokeWeight = 0; }\r
+\r
+               var parent_pos = findPos(el.vml.parentNode);\r
+               var pos = findPos(el);\r
+               pos.y = pos.y + (0.5 * strokeWeight) - parent_pos.y;\r
+               pos.x = pos.x + (0.5 * strokeWeight) - parent_pos.x;\r
+\r
+               el.vml.style.top = pos.y +'px';\r
+               el.vml.style.left = pos.x +'px';\r
+       }\r
+\r
+       if (typeof(window.previous_onresize) == 'function') { window.previous_onresize(); }\r
+}\r
+</script>\r
+\r
diff --git a/css/default.css b/css/default.css
new file mode 100644 (file)
index 0000000..c3a4839
--- /dev/null
@@ -0,0 +1,174 @@
+body, html {\r
+       height: 100%; margin:0px;\r
+       font-size:14px;\r
+}\r
+\r
+\r
+\r
+\r
+\r
+/* Style resets */\r
+#kiwi * {\r
+       padding:0px; margin:0px;\r
+}\r
+#kiwi ul { list-style:none; }\r
+\r
+#kiwi #login a {\r
+       color: #8D9713;\r
+       text-decoration:none;\r
+}\r
+\r
+\r
+/* The main app container */\r
+#kiwi {\r
+       overflow:hidden;\r
+       position:relative; \r
+       height:100%;\r
+       font-family:monospace; font-size:1em;\r
+       background:#EBEBEB;\r
+}\r
+#kiwi.small_kiwi .userlist { right:-100px; }\r
+#kiwi.small_kiwi .messages { right:10px; }\r
+\r
+\r
+#kiwi .windowlist {\r
+       /*height: 65px;*/\r
+       background-color:#1B1B1B;\r
+       font-size:0.9em;\r
+}\r
+#kiwi .windowlist ul {\r
+       overflow: hidden;\r
+       margin-right: 200px;\r
+       white-space: nowrap;\r
+       display:block;\r
+       /*height: 35px;*/\r
+}\r
+#kiwi .windowlist ul li {\r
+       float: left;\r
+       list-style: inline;\r
+       padding:5px;\r
+       margin:3px;\r
+       border: 1px solid #333;\r
+       background-color: #eee;\r
+       display: inline;\r
+       line-height: 1.4em;\r
+       vertical-align: middle;\r
+       \r
+       position:relative;\r
+       \r
+       border-radius:5px;\r
+       -moz-border-radius:5px;\r
+       -webkit-border-radius:5px;\r
+       -khtml-border-radius:5px;\r
+       behavior: url(border-radius.htc);\r
+}\r
+#kiwi .windowlist .active { padding-right:23px; }\r
+#kiwi .windowlist .highlight {\r
+       background-color: #990000;\r
+       font-weight: bold;\r
+}\r
+#kiwi .windowlist .activity { font-weight: bold; background-color: #009900; }\r
+\r
+#kiwi .windowlist ul li img { width:1em; height:1em; top:7px; right:5px; position:absolute; }\r
+/*#kiwi .windowlist ul li a {\r
+       margin:3px;\r
+       border: 1px solid #333;\r
+       background-color: #eee;\r
+       display: inline;\r
+}*/\r
+\r
+#kiwi .poweredby { float:right; margin:8px 15px; cursor:pointer; color:#EBEBEB; font-family: arial;}\r
+\r
+\r
+#kiwi .userlist {\r
+       position: absolute;\r
+       width: 100px;\r
+       top:92px; right:0px;\r
+       bottom: 55px;\r
+       margin: 5px;\r
+       overflow-y:auto;\r
+}\r
+#kiwi .userlist ul { display: none; }\r
+#kiwi .userlist ul.active { display:block; }\r
+#kiwi .userlist ul li { padding:1px 2px; cursor:pointer; }\r
+#kiwi .userlist ul li:hover { background-color:#FAF7D3; }\r
+#kiwi .userlist ul li a.nick { display:block; }\r
+\r
+#kiwi .userlist ul li .userbox { margin:0px 5px 5px 5px; font-size:.9em; }\r
+#kiwi .userlist ul li .userbox a { display:block; text-decoration:none; border-bottom: 1px dashed #aaa; }\r
+\r
+#kiwi .cur_topic {\r
+       font-family:Verdana, Geneva, sans-serif;\r
+       font-size:.85em;\r
+       line-height:18px; vertical-align:middle;\r
+       height:18px;\r
+       text-align:center;\r
+       white-space: nowrap; overflow: hidden;\r
+       padding: 3px 5px;\r
+       border-bottom:3px solid #1B1B1B;\r
+       \r
+}\r
+\r
+#kiwi .messages {\r
+       position: absolute;\r
+       top:92px; left:0px;\r
+       right: 110px; bottom:30px;\r
+       overflow-y:scroll;\r
+       overflow-x:wrap;\r
+       border:none;\r
+       display: none;\r
+}\r
+#kiwi .messages a {\r
+       text-decoration:none;\r
+}\r
+#kiwi .messages.active { display:block; }\r
+\r
+#kiwi .messages .msg { border-bottom: 1px solid #CCC; padding:1px; font-size:0.9em; }\r
+#kiwi .messages .msg .time { width:6em; float:left; color:#777; }\r
+#kiwi .messages .msg .nick { width:7em; text-align:right; float:left; font-family:arial; font-size:12px; }\r
+#kiwi .messages .msg .text { margin-left:15em; white-space:pre-wrap; word-wrap:break-word;  }\r
+\r
+#kiwi .messages .msg.action .nick { display:none; }\r
+#kiwi .messages .msg.action .text { margin-left:9em; }\r
+#kiwi .messages .msg.status .nick { display:none; }\r
+#kiwi .messages .msg.status .text { color:#990000; margin-left:9em; font-weight:bold; }\r
+#kiwi .messages .msg.topic .nick { display:none; }\r
+#kiwi .messages .msg.topic .text { color:#009900; margin-left:9em; font-style: italic; }\r
+/*#kiwi .messages .msg.motd .nick { display:none; }*/\r
+#kiwi .messages .msg.motd { border:none; }\r
+#kiwi .messages .msg.motd .text { color:#666; }\r
+#kiwi .messages .msg.whois .nick { font-weight:normal; }\r
+#kiwi .messages .msg.whois .text { margin-left:18em; padding-left:1em; border-left:1px dashed #999; }\r
+\r
+\r
+\r
+#kiwi .control {\r
+       position: absolute;\r
+       bottom:0px; left: 0px;\r
+       /*height:55px;*/\r
+       /*padding: 3px;*/\r
+       width: 100%;\r
+       background-color:#1B1B1B;\r
+}\r
+#kiwi .control .msginput {\r
+       background-color:#fff; margin:3px;\r
+       position:relative;\r
+       \r
+       border-radius:5px;\r
+       -moz-border-radius:5px;\r
+       -webkit-border-radius:5px;\r
+       -khtml-border-radius:5px;\r
+       behavior: url(border-radius.htc);\r
+}\r
+#kiwi .control .nick { text-align: right; width:11em; float:left; padding:2px; }\r
+#kiwi .control .nick a { text-decoration:none; }\r
+#kiwi .control input {\r
+       border:none;\r
+       width:70%; display:block;\r
+       margin-left:12em;\r
+       padding: 2px 0px;\r
+}\r
+#kiwi .control .plugins { margin:3px; margin-top:7px; }\r
+#kiwi .control .plugins ul li {\r
+       display:inline; font-size:0.8em; margin-left:1em;\r
+}
\ No newline at end of file
diff --git a/css/default_1.css b/css/default_1.css
new file mode 100644 (file)
index 0000000..007ff7e
--- /dev/null
@@ -0,0 +1,30 @@
+body, html { height: 100%; margin:0px; }\r
+\r
+\r
+\r
+#kiwi { overflow:hidden; font-family:monospace; font-size:1em; height:100%; }\r
+#kiwi * {\r
+       padding:0px; margin:0px;\r
+}\r
+#kiwi ul { list-style:none; }\r
+\r
+#kiwi .userlist {\r
+       float:right;\r
+       width: 100px;\r
+       margin: 5px;\r
+       overflow-y:auto;\r
+}\r
+\r
+#kiwi .messages {      overflow-y:scroll; height:90%; border:1px solid #999; }\r
+\r
+#kiwi .messages .msg { border-bottom: 1px solid #CCC; }\r
+#kiwi .messages .msg .time { width:6em; float:left; }\r
+#kiwi .messages .msg .nick { width:7em; text-align:right; float:left; }\r
+#kiwi .messages .msg .text { margin-left:15em; }\r
+\r
+#kiwi .control { margin: 3px;}\r
+#kiwi .control .msginput { padding:3px; padding-left: 7em; background-color:#F2F9FD; }\r
+#kiwi .control .msginput .nick { float:left; width:6em; text-align:right; }\r
+#kiwi .control .msginput .nick a { text-decoration:none; }\r
+#kiwi .control .msginput input { margin-left: 1em; border:none; background-color:inherit; padding:3px; display:block;}\r
+#kiwi .control .plugins ul li { display:inline; font-size:0.9em; margin-left:1em; }
\ No newline at end of file
diff --git a/css/touchscreen_tweaks.css b/css/touchscreen_tweaks.css
new file mode 100644 (file)
index 0000000..5e74a5d
--- /dev/null
@@ -0,0 +1,11 @@
+#kiwi.touchscreen.portrait .userlist { right:-100px; }\r
+\r
+#kiwi.touchscreen.portrait .messages { right:10px; }\r
+\r
+#kiwi.touchscreen .windowlist ul li { padding:10px; }\r
+\r
+#kiwi .windowlist ul {\r
+       /* border: 1px solid blue;*/\r
+       margin-right: 0px; /* i* safaris seem to behave differently here :/ */\r
+       height: 50px;\r
+}
\ No newline at end of file
diff --git a/css/ui.css b/css/ui.css
new file mode 100644 (file)
index 0000000..838cc40
--- /dev/null
@@ -0,0 +1,114 @@
+/*\r
+       ####################\r
+               Common\r
+       ####################\r
+*/\r
+#kiwi .link { cursor:pointer; color:blue; }\r
+#kiwi .newnick {\r
+       bottom:40px;\r
+       left:20px;\r
+}\r
+\r
+\r
+#kiwi .plugins a { cursor:pointer; color:#EBEBEB; font-family:verdana; }\r
+#kiwi .plugin_file { left:20px; }\r
+\r
+#kiwi .plugin_file .list select { width:150px; height:200px; }\r
+#kiwi .plugin_file .list button { float:right; }\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+#kiwi .connectwindow {\r
+       background:#1b1b1b;\r
+       color: #D4D4D4;\r
+       z-index:50;\r
+       position:absolute;\r
+       top:0px; bottom:0px;\r
+       width:100%; height:100%;\r
+}\r
+#kiwi h1.logo { display:block; width:264px; height:90px; text-indent:-1000px; margin:70px auto 100px; background:url(../img/logo.png) no-repeat; }\r
+#kiwi #login { width:665px; margin:0px auto; }\r
+#kiwi #login ul { list-style:none; }\r
+#kiwi #login ul li { overflow:hidden; margin-bottom:10px; }\r
+#kiwi #login ul li label { float:left; text-align:right; padding-right:.5em; }\r
+#kiwi #login input { border:none; }\r
+#kiwi #login .more { border-top: 1px dotted #888888; }\r
+#kiwi #login .more_link {\r
+       font-size:12px;\r
+       position:relative;\r
+       top:-16px;\r
+       background: url(../img/more.png) no-repeat right 5px;\r
+       padding-right: 12px;\r
+       color:#888;\r
+}\r
+\r
+#kiwi #login .content.top {\r
+       width:470px;\r
+       margin:30px auto 60px;\r
+}\r
+#kiwi #login .content.top ul { font-size:20px; }\r
+#kiwi #login .content.top label { width:40%; }\r
+#kiwi #login .content.top input {\r
+       width:240px; padding:.2em .5em;\r
+       font-size:.8em; text-align:center;\r
+       background:#D4D4D4;\r
+       border-radius:8px; -moz-border-radius:8px;\r
+}\r
+#kiwi #login .content.top a { display:block; text-align:right; font-size:22px; margin-top:30px; }\r
+\r
+#kiwi #login .content.bottom { width:350px; margin:30px auto; display:none; }\r
+#kiwi #login .content.bottom ul { font-size:18px; }\r
+#kiwi #login .content.bottom label { width:25%; }\r
+#kiwi #login .content.bottom input { width:216px; font-size:.8em; no-repeat; padding:.2em .5em; background:#D4D4D4; border-radius:8px; -moz-border-radius:8px; }\r
+#kiwi #login .content.bottom a { display:block; text-align:right; font-size:16px; margin-top:30px; }\r
+\r
+\r
+\r
+\r
+#kiwi .about {\r
+       top: 100px;\r
+       left: 200px;\r
+       width: 400px;\r
+       z-index: 1;\r
+       display: none;\r
+}\r
+#kiwi .about button { float:right; }\r
+\r
+#kiwi .box {\r
+       min-width:150px;\r
+       background: url(../img/trans_back.png) repeat;\r
+       border-radius:5px;\r
+       -moz-border-radius:5px;\r
+       -webkit-border-radius:5px;\r
+       -khtml-border-radius:5px;\r
+       padding:7px;\r
+       \r
+       /*background-color: #819463;\r
+       padding: 10px;*/\r
+       position:absolute;\r
+       \r
+       /*\r
+       box-shadow: 0px 0px 20px #555;\r
+       -webkit-box-shadow: 0px 0px 20px #555;\r
+       -moz-box-shadow: 0px 0px 20px #555;\r
+       filter: progid:DXImageTransform.Microsoft.dropShadow(color=#555, offX=0, offY=0, positive=true);\r
+       \r
+       border-radius: 4px;\r
+       -moz-border-radius: 4px;\r
+       -webkit-border-radius: 4px;\r
+       */\r
+}\r
+\r
+.box .boxarea {\r
+       padding:15px !important;\r
+       background-color:#D4D4D4;\r
+       border-radius:3px;\r
+       -moz-border-radius:3px;\r
+       -webkit-border-radius:3px;\r
+       -khtml-border-radius:3px;\r
+       overflow:hidden;\r
+}
\ No newline at end of file
diff --git a/dev_processClientMessage.php b/dev_processClientMessage.php
new file mode 100644 (file)
index 0000000..fffc041
--- /dev/null
@@ -0,0 +1,142 @@
+<?php\r
+\r
+\r
+               //deb("FROM IPC CLIENT:\n$data\n-----");\r
+               $d = json_decode($data, 1);\r
+               //deb('Processed data: '.print_r($d, 1));\r
+               switch($d['method']){\r
+                       case 'addbuffer':\r
+                               $buffer[] = json_encode($d['args']);\r
+                               break;\r
+                               \r
+                       case 'read':\r
+                               // The 'read' command adds this web client to the read buffer queue\r
+                               if(!in_array($cl, $ipc_read)) $ipc_read[] = $cl;\r
+                               break;\r
+                               \r
+                       case 'quit':\r
+                               deb('Quitting');\r
+                               $bot = null;\r
+                               break;\r
+                               \r
+                       case 'connect':\r
+                               $args = $d['args'];\r
+                               deb('Connecting with: '.print_r($args, 1));\r
+                               if(!isset($args['server'], $args['port'], $args['ssl'], $args['nick'])){\r
+                                       $buffer[] = json_encode(array('error'=>'invalid_args'));\r
+                                       break;\r
+                               } else {\r
+                                       $kiwi = null;\r
+                                       /*if($config['memcache_use']){\r
+                                               $conf_key = 'kiwi_conf_'.$args['server'];\r
+                                               $conf = (int)GLOB::$mc->get($conf_key);\r
+                                               if($conf){\r
+                                                       $c = @unserialize($conf);\r
+                                                       if($c){\r
+                                                               //$hostname = GET HOSTNAME HERE\r
+                                                               //$ip = GET IP HERE\r
+                                                               $kiwi = "WEBIRC {$c['password']} {$c['user']} $hostname $ip";\r
+                                                       }\r
+                                               }\r
+                                       }*/\r
+                                       deb("ARGSLOL: ".print_r($app_args, 1));\r
+                                       $opts = array();\r
+                                       if(isset($app_args['remote_host'])) $opts['ident'] = md5($app_args['remote_host']);\r
+                                       \r
+                                       $bot = new IRCConnection("irc://{$args['nick']}@{$args['server']}:{$args['port']}", $kiwi, $opts);\r
+                                       if(isset($args['channels'])) $bot->chans = explode(',', $args['channels']);\r
+                                       if($bot->connected){\r
+                                               // We're.. connected!\r
+                                       } else {\r
+                                               $buffer[] = json_encode(array('event'=>'server_connect', 'connected'=>false, 'host'=>$args['server']));\r
+                                               unset($bot);\r
+                                       }\r
+                               }\r
+                               break;\r
+                               \r
+                       case 'join':\r
+                               $args = $d['args'];\r
+                               if(!isset($args['channel'])){\r
+                                       $buffer[] = json_encode(array('error'=>'invalid_args'));\r
+                                       break;\r
+                               } else {\r
+                                       $chans = explode(',', $args['channel']);\r
+                                       foreach($chans as $c)\r
+                                               $bot->Join($c);\r
+                               }\r
+                               break;\r
+                               \r
+                       case 'msg':\r
+                               $args = $d['args'];\r
+                               deb('msg with: '.print_r($args, 1));\r
+                               if(!isset($args['target'],$args['msg'])){\r
+                                       $buffer[] = json_encode(array('error'=>'invalid_args'));\r
+                                       break;\r
+                               } else {\r
+                                       $bot->SendMessage($args['target'], $args['msg']);\r
+                                       $buffer[] = json_encode($data);\r
+                               }\r
+                               break;\r
+                               \r
+                       case 'action':\r
+                               $args = $d['args'];\r
+                               deb('action with: '.print_r($args, 1));\r
+                               if(!isset($args['target'],$args['msg'])){\r
+                                       $buffer[] = json_encode(array('error'=>'invalid_args'));\r
+                                       break;\r
+                               } else {\r
+                                       $bot->SendMessage($args['target'], chr(1)."ACTION {$args['msg']}".chr(1));\r
+                                       $buffer[] = json_encode($data);\r
+                               }\r
+                               break;\r
+                               \r
+                       case 'raw':\r
+                               $args = $d['args'];\r
+                               if(!isset($args['data'])){\r
+                                       $buffer[] = json_encode(array('error'=>'invalid_args'));\r
+                                       break;\r
+                               } else {\r
+                                       $bot->stream->Write("{$args['data']}\r\n");\r
+                                       //$bot->SendMessage($args['target'], $args['msg']);\r
+                               }\r
+                               break;\r
+                               \r
+                               \r
+                       case 'debug':\r
+                               $send_debug = !$send_debug;\r
+                               $data = array(\r
+                                       'event' => 'debug',\r
+                                       'msg' => 'Debugging '.($send_debug)?'on':'off',\r
+                                       'time' => time()\r
+                               );\r
+                               $buffer[] = json_encode($data);\r
+                               break;\r
+                               \r
+                               \r
+                       case 'sync':\r
+                               // Clear the current buffer as we're gonna send only scrollback\r
+                               $buffer = array();\r
+                               \r
+                               // Send the settings and channels over\r
+                               if($bot){\r
+                                       $data = array('event'=>'sync');\r
+                                       $data['nick'] = $bot->nick;\r
+                                       $data['tabviews'] = array();\r
+                                       if($bot->chanlist){\r
+                                               foreach($bot->chanlist as $chan_name => $chan){\r
+                                                       $data['tabviews'][] = array('name'=>$chan_name, 'userlist'=>$chan['userlist']);\r
+                                               }\r
+                                               $buffer[] = json_encode($data);\r
+                                       }\r
+                                       \r
+                                       // Send the message scrollback\r
+                                       foreach($bot->scrollback as $line) $buffer[] = json_encode($line);\r
+                                       //$bot->scrollback = array();\r
+                               } else {\r
+                                       $data = array('error'=>'no_data');\r
+                               }\r
+                               \r
+                               $buffer[] = json_encode($data);\r
+                               \r
+                               break;\r
+               }
\ No newline at end of file
diff --git a/embed.php b/embed.php
new file mode 100644 (file)
index 0000000..3e286ae
--- /dev/null
+++ b/embed.php
@@ -0,0 +1,97 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml">\r
+<head>\r
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />\r
+<title>kiwi</title>\r
+<link rel="stylesheet" type="text/css" href="css/default.css">\r
+<link rel="stylesheet" type="text/css" href="css/ui.css">\r
+\r
+<?php\r
+\r
+       switch(true){\r
+               case stripos($_SERVER['HTTP_USER_AGENT'], 'android') > 0:\r
+               case stripos($_SERVER['HTTP_USER_AGENT'], 'iphone') > 0:\r
+               case stripos($_SERVER['HTTP_USER_AGENT'], 'ipod') > 0:\r
+?>\r
+<meta name="viewport" content="width=device-width,user-scalable=no" />\r
+\r
+<?php\r
+                       break;\r
+                       \r
+       }\r
+?>\r
+\r
+<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js"></script>\r
+<script type="text/javascript" src="js/jquery.json-2.2.min.js"></script>\r
+<script type="text/javascript" src="js/util.js"></script>\r
+<script type="text/javascript" src="js/gateway.js"></script>\r
+<script type="text/javascript" src="js/front.js"></script>\r
+<script type="text/javascript">\r
+\r
+       var init_data = {};\r
+\r
+       $(document).ready(function(){\r
+               \r
+               //#nick@irc.anonnet.org:6679+/#channel,##channel,&channel\r
+               var chans = document.location.hash.match(/[#&]+[^ ,\007]+/g);\r
+               if(chans != null && chans.length > 0) { init_data.channel = chans.join(','); }\r
+       \r
+               front.init();\r
+       });\r
+</script>\r
+\r
+</head>\r
+\r
+<body>\r
+\r
+<div id="kiwi">\r
+       <div class="connectwindow">\r
+               <form class="formconnectwindow">\r
+                       <label for="nick">Nick</label>\r
+                       <input type="text" class="nick" value="<?php if(isset($_GET['nick'])) echo htmlentities($_GET['nick']); ?>" /><br />\r
+                       \r
+                       <span class="networkselection">\r
+<?php if(isset($_GET['server'])){ ?>\r
+                               <input type="hidden" class="network" value="<?php if(isset($_GET['server'])) echo htmlentities($_GET['server']); ?>" />\r
+<?php } else { ?>\r
+                               <label for="network">Network</label>\r
+                               <select class="network">\r
+                                       <option value="irc.anonnet.org">AnonNet</option>\r
+                                       <option value="irc.freenode.net">Freenode</option>\r
+                                       <option value="irc.dal.net">DALnet</option>\r
+                                       \r
+                               </select>\r
+<?php } ?>\r
+                       </span>\r
+                       <button type="submit">Connect</button>\r
+               </form>\r
+       </div>\r
+       \r
+       \r
+       \r
+       \r
+               <div class="windowlist">\r
+                       <ul></ul>\r
+                       <div class="poweredby">Powered by kiwi</div>\r
+               </div>\r
+               \r
+       <div class="userlist">\r
+               <ul></ul>\r
+        </div>\r
+       \r
+               \r
+        <div class="control">\r
+               <div class="msginput">\r
+               <div class="nick"><a href="#"></a>:</div>\r
+                       <input type="text" name="kiwi_msginput" id="kiwi_msginput" />\r
+            </div>\r
+            <div class="plugins">\r
+  <ul>\r
+                    <li><a class="load_plugin_file">Load plugin file</a></li>\r
+                </ul>\r
+                       </div>\r
+               </div>\r
+</div>\r
+\r
+</body>\r
+</html>\r
diff --git a/img/logo.png b/img/logo.png
new file mode 100644 (file)
index 0000000..59d5aa4
Binary files /dev/null and b/img/logo.png differ
diff --git a/img/more.png b/img/more.png
new file mode 100644 (file)
index 0000000..a0d4dd7
Binary files /dev/null and b/img/more.png differ
diff --git a/img/redcross.png b/img/redcross.png
new file mode 100644 (file)
index 0000000..e5f5fe0
Binary files /dev/null and b/img/redcross.png differ
diff --git a/img/trans_back.png b/img/trans_back.png
new file mode 100644 (file)
index 0000000..f17dc9d
Binary files /dev/null and b/img/trans_back.png differ
diff --git a/index.html b/index.html
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/index.lighttpd.html b/index.lighttpd.html
new file mode 100644 (file)
index 0000000..e639955
--- /dev/null
@@ -0,0 +1,57 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml">\r
+<head>\r
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />\r
+<title>Welcome page</title>\r
+<style type="text/css" media="screen">\r
+body { background: #e7e7e7; font-family: Verdana, sans-serif; font-size: 11pt; }\r
+#page { background: #ffffff; margin: 50px; border: 2px solid #c0c0c0; padding: 10px; }\r
+#header { background: #4b6983; border: 2px solid #7590ae; text-align: center; padding: 10px; color: #ffffff; }\r
+#header h1 { color: #ffffff; }\r
+#body { padding: 10px; }\r
+span.tt { font-family: monospace; }\r
+span.bold { font-weight: bold; }\r
+a:link { text-decoration: none; font-weight: bold; color: #C00; background: #ffc; }\r
+a:visited { text-decoration: none; font-weight: bold; color: #999; background: #ffc; }\r
+a:active { text-decoration: none; font-weight: bold; color: #F00; background: #FC0; }\r
+a:hover { text-decoration: none; color: #C00; background: #FC0; }\r
+</style>\r
+</head>\r
+<body>\r
+<div id="page">\r
+ <div id="header">\r
+ <h1> Placeholder page </h1>\r
+  The owner of this web site has not put up any web pages yet. Please come back later.\r
+ </div>\r
+ <div id="body">\r
+  <h2>You should replace this page with your own web pages as soon as possible.</h2>\r
+  Unless you changed its configuration, your new server is configured as follows:\r
+  <ul>\r
+   <li>Configuration files can be found in <span class="tt">/etc/lighttpd</span>. Please read  <span class="tt">/etc/lighttpd/conf-available/README</span> file.</li>\r
+   <li>The DocumentRoot, which is the directory under which all your HTML files should exist, is set to <span class="tt">/var/www</span>.</li>\r
+   <li>CGI scripts are looked for in <span class="tt">/usr/lib/cgi-bin</span>, which is where Ubuntu packages will place their scripts. You can enable cgi module by using command <span class="bold tt">&quot;lighty-enable-mod cgi&quot;</span>.</li>\r
+   <li>Log files are placed in <span class="tt">/var/log/lighttpd</span>, and will be rotated weekly. The frequency of rotation can be easily changed by editing <span class="tt">/etc/logrotate.d/lighttpd</span>.</li>\r
+   <li>The default directory index is <span class="tt">index.html</span>, meaning that requests for a directory <span class="tt">/foo/bar/</span> will give the contents of the file /var/www/foo/bar/index.html if it exists (assuming that <span class="tt">/var/www</span> is your DocumentRoot).</li>\r
+   <li>You can enable user directories by using command <span class="bold tt">&quot;lighty-enable-mod userdir&quot;</span></li>\r
+  </ul>\r
+  <h2>About this page</h2>\r
+  <p>\r
+   This is a placeholder page installed by the Ubuntu release of the <a href="http://packages.ubuntu.com/lighttpd">Lighttpd server package.</a>\r
+  </p>\r
+  <p>\r
+   This computer has installed the Ubuntu operating system, but it has nothing to do with the Ubuntu Project. Please do not contact the Ubuntu Project about it.\r
+  </p>\r
+  <p>\r
+   If you find a bug in this Lighttpd package, or in Lighttpd itself, please file a bug report on it. Instructions on doing this, and the list of known bugs of this package, can be found in the \r
+   <a href="https://bugs.launchpad.net/ubuntu/+source/lighttpd/">Ubuntu Bug Tracking System.</a>\r
+  </p>\r
+  <p>\r
+    <a href="http://validator.w3.org/check?uri=referer"><img\r
+        src="http://www.w3.org/Icons/valid-xhtml10"\r
+        alt="Valid XHTML 1.0 Transitional" height="31" width="88" border="0" /></a>\r
+  </p>  \r
+ </div>\r
+</div>\r
+<!-- s:853e9a42efca88ae0dd1a83aeb215047 -->\r
+</body>\r
+</html>\r
diff --git a/index.php b/index.php
new file mode 100644 (file)
index 0000000..d1b1f45
--- /dev/null
+++ b/index.php
@@ -0,0 +1,155 @@
+<?php\r
+\r
+       switch(true){\r
+               case stripos($_SERVER['HTTP_USER_AGENT'], 'android') > 0:\r
+                       $agent = "android"; $touchscreen = true;\r
+                       break;\r
+                       \r
+               case stripos($_SERVER['HTTP_USER_AGENT'], 'iphone') > 0:\r
+                       $agent = "iphone"; $touchscreen = true;\r
+                       break;\r
+                       \r
+               case stripos($_SERVER['HTTP_USER_AGENT'], 'ipod') > 0:\r
+                       $agent = "ipod"; $touchscreen = true;\r
+                       break;\r
+                       \r
+               case stripos($_SERVER['HTTP_USER_AGENT'], 'ipad') > 0:\r
+                       $agent = "ipad"; $touchscreen = true;\r
+                       break;\r
+                       \r
+               default:\r
+                       $agent = "normal";\r
+                       $touchscreen = false;\r
+       }\r
+       \r
+       define("SERVER_SET", isset($_GET['server']));\r
+       $server = isset($_GET['server']) ? $_GET['server'] : "irc.anonnet.org";\r
+       $nick = isset($_GET['nick']) ? $_GET['nick'] : "";\r
+       // Channel is set via javascript using location.hash\r
+       \r
+?>\r
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml">\r
+<head>\r
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />\r
+\r
+<?php if(in_array($agent, array("android", "ipad", "iphone", "ipad"))){ ?>\r
+<meta name="viewport" content="width=device-width,user-scalable=no" />\r
+<?php } ?>\r
+\r
+<title>Kiwi IRC</title>\r
+<link rel="stylesheet" type="text/css" href="css/default.css">\r
+<link rel="stylesheet" type="text/css" href="css/ui.css">\r
+\r
+<?php if($touchscreen){ ?>\r
+<link rel="stylesheet" type="text/css" href="css/touchscreen_tweaks.css">\r
+<?php } ?>\r
+\r
+\r
+<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js"></script>\r
+<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.min.js"></script>\r
+<script type="text/javascript" src="js/jquery.json-2.2.min.js"></script>\r
+<script type="text/javascript" src="js/util.js"></script>\r
+<script type="text/javascript" src="js/gateway.js"></script>\r
+<script type="text/javascript" src="js/front.js"></script>\r
+\r
+<?php if(in_array($agent, array("android", "ipad", "iphone", "ipad"))){ ?>\r
+<script type="text/javascript" src="js/touchscreen_tweaks.js"></script>\r
+<?php } ?>\r
+\r
+<script type="text/javascript">\r
+       var agent = '<?= $agent ?>';\r
+       var touchscreen = <?= ($touchscreen) ? 'true' : 'false' ?>;\r
+       var init_data = {};\r
+       \r
+       $(document).ready(function(){\r
+               if(touchscreen) $('#kiwi').addClass('touchscreen');\r
+               \r
+               //#nick@irc.anonnet.org:6679+/#channel,##channel,&channel\r
+               var chans = document.location.hash.match(/[#&]+[^ ,\007]+/g);\r
+               if(chans != null && chans.length > 0) {\r
+                       init_data.channel = chans.join(',');\r
+                       $('#channel').val(init_data.channel);\r
+               }\r
+               \r
+               front.init();\r
+               \r
+               addEvents();\r
+               $('.nick').focus();\r
+       });\r
+       \r
+       \r
+       function addEvents(){\r
+               $('.more_link').click(function(){ $('.content.bottom').slideDown('fast'); $('.network').focus(); return false; });\r
+               $('.formconnectwindow').submit(function(){\r
+                       init_data.channel = $('#channel').val();\r
+                       return false;\r
+               });\r
+               $('a.connect').click(function(){ $('.formconnectwindow').submit(); return false; });\r
+       }\r
+</script>\r
+\r
+</head>\r
+\r
+<body>\r
+\r
+<div id="kiwi">\r
+       <div class="connectwindow">\r
+               <h1 class="logo">Kiwi IRC</h1>\r
+               <div id="login">\r
+                       <form class="formconnectwindow">\r
+                               <div class="content top">\r
+                                       <ul>\r
+                                               <li><label for="nick">Your nickname:</label>\r
+                                                       <input type="text" id="nick" name="nick" class="nick" value="<?php echo htmlentities($nick); ?>" /></li>\r
+                                       </ul>\r
+                                       <a class="connect" href="">Connect..</a>\r
+                               </div>\r
+                               \r
+                               <div class="more" style="<?php if(SERVER_SET) echo "display:none;"; ?>">\r
+                                       <a href="" class="more_link">More</a>\r
+                                       <div class="content bottom">\r
+                                               <ul>\r
+                                                       <li><label for="network">Server:</label>\r
+                                                               <input type="text" id="network" name="network" class="network" value="<?php echo htmlentities($server); ?>" /></li>\r
+                                                       <li><label for="channel">Channel:</label>\r
+                                                               <input type="text" id="channel" name="channel" class="channel" value="#kiwiirc" /></li>\r
+                                               </ul>\r
+                                               <a class="connect" href="">Connect..</a>\r
+                                       </div>\r
+                               </div>\r
+                       </form>\r
+               </div>\r
+       </div>\r
+       \r
+       \r
+       \r
+       <div class="windowlist">\r
+               <div class="poweredby">Powered by Kiwi IRC</div>\r
+               <ul></ul>\r
+       </div>\r
+       \r
+       <div class="cur_topic"></div>\r
+       \r
+       <div class="userlist">\r
+               <ul></ul>\r
+       </div>\r
+       \r
+       <div class="control">\r
+               <div class="msginput">\r
+                       <div class="nick"><a href="#"></a>:</div>\r
+                       <input type="text" name="kiwi_msginput" id="kiwi_msginput" />\r
+               </div>\r
+               <div class="plugins">\r
+                       <ul>\r
+                               <!-- <li><a class="load_plugin_file">Plugins</a></li> -->\r
+                               <?php if(isset($_GET['debug'])){ ?>\r
+                               <li><a class="reload_css">Reload CSS</a></li>\r
+                               <?php } ?>\r
+                       </ul>\r
+               </div>\r
+       </div>\r
+</div>\r
+\r
+</body>\r
+</html>
\ No newline at end of file
diff --git a/index_old.php b/index_old.php
new file mode 100644 (file)
index 0000000..7f4d827
--- /dev/null
@@ -0,0 +1,130 @@
+<?php\r
+\r
+       switch(true){\r
+               case stripos($_SERVER['HTTP_USER_AGENT'], 'android') > 0:\r
+                       $agent = "android"; $touchscreen = true;\r
+                       break;\r
+                       \r
+               case stripos($_SERVER['HTTP_USER_AGENT'], 'iphone') > 0:\r
+                       $agent = "iphone"; $touchscreen = true;\r
+                       break;\r
+                       \r
+               case stripos($_SERVER['HTTP_USER_AGENT'], 'ipod') > 0:\r
+                       $agent = "ipod"; $touchscreen = true;\r
+                       break;\r
+                       \r
+               case stripos($_SERVER['HTTP_USER_AGENT'], 'ipad') > 0:\r
+                       $agent = "ipad"; $touchscreen = true;\r
+                       break;\r
+                       \r
+               default:\r
+                       $agent = "normal";\r
+                       $touchscreen = false;\r
+       }\r
+?>\r
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml">\r
+<head>\r
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />\r
+\r
+<?php if(in_array($agent, array("android", "ipad", "iphone", "ipad"))){ ?>\r
+<meta name="viewport" content="width=device-width,user-scalable=no" />\r
+<?php } ?>\r
+\r
+<title>kiwi</title>\r
+<link rel="stylesheet" type="text/css" href="css/default.css">\r
+<link rel="stylesheet" type="text/css" href="css/ui.css">\r
+\r
+<?php if($touchscreen){ ?>\r
+<link rel="stylesheet" type="text/css" href="css/touchscreen_tweaks.css">\r
+<?php } ?>\r
+\r
+\r
+<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js"></script>\r
+<script type="text/javascript" src="js/jquery.json-2.2.min.js"></script>\r
+<script type="text/javascript" src="js/util.js"></script>\r
+<script type="text/javascript" src="js/gateway.js"></script>\r
+<script type="text/javascript" src="js/front.js"></script>\r
+\r
+<?php if(in_array($agent, array("android", "ipad", "iphone", "ipad"))){ ?>\r
+<script type="text/javascript" src="js/touchscreen_tweaks.js"></script>\r
+<?php } ?>\r
+\r
+<script type="text/javascript">\r
+       var agent = '<?= $agent ?>';\r
+       var touchscreen = <?= ($touchscreen) ? 'true' : 'false' ?>;\r
+       var init_data = {};\r
+       \r
+       $(document).ready(function(){\r
+               if(touchscreen) $('#kiwi').addClass('touchscreen');\r
+               \r
+               //#nick@irc.anonnet.org:6679+/#channel,##channel,&channel\r
+               var chans = document.location.hash.match(/[#&]+[^ ,\007]+/g);\r
+               if(chans != null && chans.length > 0) { init_data.channel = chans.join(','); }\r
+       \r
+               front.init();\r
+       });\r
+</script>\r
+\r
+</head>\r
+\r
+<body>\r
+\r
+<div id="kiwi">\r
+       <div class="box about">\r
+               <h2>kiwi</h2>\r
+               <p>An alternative to downloading an irc client. This web app is the best thing you'll use in the next couple years.</p>\r
+               <button class="about_close">Close</button>\r
+       </div>\r
+       \r
+       <div class="connectwindow">\r
+               <form class="formconnectwindow">\r
+                       <label for="nick">Nick</label>\r
+                       <input type="text" class="nick" value="<?php if(isset($_GET['nick'])) echo htmlentities($_GET['nick']); ?>" /><br />\r
+                       \r
+                       <span class="networkselection">\r
+<?php if(isset($_GET['server'])){ ?>\r
+                               <input type="hidden" class="network" value="<?php if(isset($_GET['server'])) echo htmlentities($_GET['server']); ?>" />\r
+<?php } else { ?>\r
+                               <label for="network">Network</label>\r
+                               <select class="network">\r
+                                       <?php if(1==1){ echo '<option value="irc.anonnet.org">AnonNet</option>'; } ?>\r
+                                       <option value="irc.freenode.net">Freenode</option>\r
+                                       <option value="irc.dal.net">DALnet</option>\r
+                                       \r
+                               </select>\r
+<?php } ?>\r
+                       </span>\r
+                       <button type="submit">Connect</button>\r
+               </form>\r
+       </div>\r
+       \r
+       \r
+       \r
+       <div class="windowlist">\r
+               <div class="poweredby">Powered by kiwi</div>\r
+               <ul></ul>\r
+       </div>\r
+       \r
+       <div class="userlist">\r
+               <ul></ul>\r
+       </div>\r
+       \r
+       <div class="cur_topic"></div>\r
+       \r
+       <div class="control">\r
+               <div class="msginput">\r
+                       <div class="nick"><a href="#"></a>:</div>\r
+                       <input type="text" name="kiwi_msginput" id="kiwi_msginput" />\r
+               </div>\r
+               <div class="plugins">\r
+                       <ul>\r
+                               <li><a class="load_plugin_file">Load plugin file</a></li>\r
+                               <li><a class="reload_css">Reload CSS</a></li>\r
+                       </ul>\r
+               </div>\r
+       </div>\r
+</div>\r
+\r
+</body>\r
+</html>
\ No newline at end of file
diff --git a/inf.php b/inf.php
new file mode 100644 (file)
index 0000000..9c66bb6
--- /dev/null
+++ b/inf.php
@@ -0,0 +1,5 @@
+<?php\r
+       print_r($_SERVER);\r
+       exit;\r
+       echo `whoami`;\r
+phpinfo();\r
diff --git a/irc_session.php b/irc_session.php
new file mode 100644 (file)
index 0000000..393af4e
--- /dev/null
@@ -0,0 +1,238 @@
+<?php\r
+\r
+       /*\r
+        * CASE: Page requested to start a session.\r
+        *     1. Attempt connect to the server\r
+        *          Fail?\r
+        *            End session and send error to the client\r
+        *          Connected?\r
+        *            Create a listening UNIX socket and send OK to the client\r
+        *\r
+        *     2. Run the IRC client class as normal, while polling for connections\r
+        *        on the listening socket. Save any messages for the web client into\r
+        *        a buffer.\r
+        *        This is the normal running state.\r
+        *\r
+        *     3. Upon a connection on the UNIX socket, send the oldest message from\r
+        *        the buffer to the web client, FIFO style and close the connection.\r
+        */\r
+       \r
+       // If -d has been sent to start this session, start in debug mode\r
+       if(in_array('-d', $argv)){\r
+               define('DEBUG', true);\r
+       } else {\r
+               define('DEBUG', false);\r
+       }\r
+       \r
+       \r
+       require(dirname(__FILE__).'/config.php');\r
+       require(dirname(__FILE__).'/common.php');\r
+       require(dirname(__FILE__).'/class_session.php');\r
+       require(dirname(__FILE__).'/class_irc.php');\r
+       require(dirname(__FILE__).'/class_ircconnection.php');\r
+       require(dirname(__FILE__).'/ircd_profiles/hybrid.php');\r
+       \r
+       \r
+       // Make sure a session id has been specified\r
+       if(!isset($argv[1]) || empty($argv[1]))\r
+               die('no_session');\r
+       \r
+       $session_id = $argv[1];\r
+       $host_key = $argv[2];           // The remote host key in memcached\r
+       \r
+       // Process any arguments\r
+       $app_args = array();\r
+       foreach($argv as $a){\r
+               $value = substr($a, 2);\r
+               switch(substr($a, 0, 2)){\r
+                       case '-h':\r
+                               // Client host\r
+                               $app_args['remote_host'] = $value;\r
+                               break;\r
+               }\r
+       }\r
+       \r
+       \r
+       // Start the unix session, and if it fails quit here\r
+       $ipc_srv = SESSIONS::clStart($session_id);\r
+       if($ipc_srv === false) die("fail '$session_id' '$host_key'");\r
+       \r
+       // Debug mode runs in terminal to see output\r
+       if(!DEBUG){\r
+               // See if we can fork our process..\r
+               $pid = pcntl_fork();\r
+               if($pid === -1){\r
+                       die('Forking error');\r
+               }elseif($pid){\r
+                       // Parent process\r
+                       die('ok');\r
+               }else{\r
+                       // Child process\r
+                       fclose(STDOUT);\r
+               }\r
+       }\r
+       \r
+       //posix_setsid();\r
+\r
+       \r
+       \r
+       ############################################\r
+       ## Main process\r
+       ############################################\r
+       \r
+       $old_error_handler = set_error_handler("errorHandler");\r
+       \r
+       $bot = null; // The IRC connection\r
+       $ipc = array(); // The IPC connections to the web clients\r
+       $ipc_read = array(); // The IPC connections wanting to read the buffer\r
+       $buffer = array(); // Messages to be sent to the web client\r
+       $send_debug = false;\r
+       \r
+       deb("Starting $session_id");\r
+       \r
+       $timeout = time();\r
+       $timeout_timer = 0;\r
+       while(true){\r
+               // If there are no webclients connected, start the timer to disconnect\r
+               if(!count($ipc)){\r
+                       if($timeout == 0) $timeout = time();\r
+                       $timeout_timer = time() - $timeout;\r
+                       if($timeout_timer >= $config['timeout']) break;\r
+               } else {\r
+                       $timeout = 0;\r
+               }\r
+               \r
+               processClients();\r
+       \r
+               if($bot != null){\r
+                       // $bot handles the sleep time here\r
+                       $bot->Process();\r
+               } else {\r
+                       usleep(5000);\r
+               }\r
+       }\r
+       \r
+       \r
+       // We've quit, lower the counter for this host\r
+       if($config['memcache_use']){\r
+               @GLOB::$mc->decrement($host_key);\r
+       }\r
+       \r
+       deb('Exiting client');\r
+       \r
+       \r
+       ############################################\r
+       ## Functions / Classes\r
+       ############################################\r
+       \r
+       \r
+       \r
+       function processClients(){\r
+               global $ipc_srv;\r
+               global $ipc;\r
+               global $ipc_read;\r
+               global $buffer;\r
+               global $timeout;\r
+               \r
+               // Check for any new web client connections...\r
+               $read = array($ipc_srv);\r
+               $write = $excep = array();\r
+               $read_changed = stream_select($read, $write, $excep, 0);\r
+               for($i=0; $i<$read_changed; $i++) {\r
+                       if($read[$i] === $ipc_srv){\r
+                               $ipc[] = stream_socket_accept($ipc_srv);\r
+                               deb("Connection...");\r
+                       }\r
+               }\r
+               \r
+               \r
+               // Check for any changed web clients..\r
+               $read = $ipc;\r
+               $read_changed = (count($read)) ? stream_select($read, $write, $excep, 0) : array();\r
+               if($read_changed){\r
+                       foreach($read as $cl){\r
+                               $data = fread($cl, 1024);\r
+                               if(!$data){\r
+                                       // Web client has disconnected\r
+                                       deb("Removing closed socket..");\r
+                                       $key = array_search($cl, $ipc);\r
+                                       unset($ipc[$key]);\r
+                                       \r
+                                       $key = array_search($cl, $ipc_read);\r
+                                       if($key !== false) unset($ipc_read[$key]);\r
+                               } else {\r
+                                       //deb('Got data: '.$data);\r
+                                       processClientMessage($data, $cl);\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               \r
+               \r
+               // Send the buffer messages to any connected web clients..\r
+               // 1 message at a time...\r
+               if(count($buffer) && count($ipc_read)){\r
+                       //deb(print_r($ipc_read, 1));\r
+                       $msg = array_shift($buffer);\r
+                       //deb("Sending '$msg' to ".count($ipc_read)." clients..");\r
+                       foreach($ipc_read as $cl){\r
+                               if($cl) fwrite($cl, $msg."\n");\r
+                       }\r
+               }\r
+               \r
+               // The whole buffer at a time...\r
+               /*\r
+               while(count($buffer)){\r
+                       $msg = array_shift($buffer);\r
+                       foreach($ipc as $cl) frwite($cl, $msg);\r
+               }\r
+               */\r
+               \r
+       }\r
+       \r
+       \r
+       function processClientMessage($data, $cl){\r
+               global $config;\r
+               global $buffer;\r
+               global $bot, $ipc_read;\r
+               global $timeout;\r
+               global $send_debug;\r
+               global $app_args;\r
+               \r
+               require('dev_processClientMessage.php');\r
+               return true;\r
+       }\r
+               \r
+       \r
+       \r
+       \r
+       function errorHandler($errno, $errstr, $errfile, $errline){\r
+               $ret = '';\r
+               switch ($errno) {\r
+                       case E_USER_ERROR:\r
+                               $ret .= "USER [$errno] $errstr\n";\r
+                               $ret .= "  Fatal error on line $errline in file $errfile";\r
+                               $ret .= ", PHP " . PHP_VERSION . " (" . PHP_OS . ")\n";\r
+                               $ret .= "Aborting...<br />\n";\r
+                               exit(1);\r
+                               break;\r
+\r
+                       case E_USER_WARNING:\r
+                               $ret .= "WARNING on line $errline in file $errfile: [$errno] $errstr\n";\r
+                               break;\r
+\r
+                       case E_USER_NOTICE:\r
+                               $ret .= "NOTICE on line $errline in file $errfile: [$errno] $errstr\n";\r
+                               break;\r
+\r
+                       default:\r
+                               $ret .= "UNKOWN ERR on line $errline in file $errfile: [$errno] $errstr\n";\r
+                               break;\r
+               }\r
+               \r
+               if(!empty($ret)) deb($ret);                     \r
+               \r
+               \r
+               /* Don't execute PHP internal error handler */\r
+               return true;\r
+       }
\ No newline at end of file
diff --git a/ircd_profiles/hybrid.php b/ircd_profiles/hybrid.php
new file mode 100644 (file)
index 0000000..f5284b4
--- /dev/null
@@ -0,0 +1,27 @@
+<?php\r
+\r
+       /*\r
+        * Best of all worlds\r
+        * A collection of response commands from all RFCs.\r
+        * These selected appears to work best for the most part.\r
+        */\r
+       \r
+       define('RPL_WHOISUSER', 311);\r
+       define('RPL_WHOISSERVER', 312);\r
+       define('RPL_WHOISOPERATOR', 313);\r
+       define('RPL_WHOISIDLE', 317);\r
+       define('RPL_ENDOFWHOIS', 318);\r
+       define('RPL_WHOISCHANNELS', 319);\r
+       \r
+       define('RPL_MOTD', 372);\r
+       define('RPL_WHOISMODES', 379);                                  // Unreal. Conflicts.\r
+       \r
+       define('ERR_NOSUCHNICK', 401);\r
+       define('ERR_NOSUCHSERVER', 402);\r
+       define('ERR_NOSUCHCHANNEL', 403);\r
+       define('ERR_CANNOTSENDTOCHAN', 404);\r
+       define('ERR_TOOMANYCHANNELS', 405);\r
+       define('ERR_WASNOSUCHNICK', 406);\r
+       define('ERR_TOOMANYTARGETS', 407);\r
+               \r
+       define('ERR_LINKCHANNEL', 470);
\ No newline at end of file
diff --git a/ircd_profiles/rfc_1459.php b/ircd_profiles/rfc_1459.php
new file mode 100644 (file)
index 0000000..a4ff2a0
--- /dev/null
@@ -0,0 +1,11 @@
+<?php\r
+\r
+       $resp_code = array(\r
+               'ERR_NOSUCHNICK' => '401',\r
+               'ERR_NOSUCHSERVER' => '402',\r
+               'ERR_NOSUCHCHANNEL' => '403',\r
+               'ERR_CANNOTSENDTOCHAN' => '404',\r
+               'ERR_TOOMANYCHANNELS' => '405',\r
+               'ERR_WASNOSUCHNICK' => '406',\r
+               'ERR_TOOMANYTARGETS' => '407',\r
+       );
\ No newline at end of file
diff --git a/ircd_profiles/rfc_2812.php b/ircd_profiles/rfc_2812.php
new file mode 100644 (file)
index 0000000..4db4751
--- /dev/null
@@ -0,0 +1,6 @@
+<?php\r
+\r
+       $resp_code = array(\r
+               'RPL_WELCOME' => '001',\r
+               'RPL_YOURHOST' => '002',\r
+       );
\ No newline at end of file
diff --git a/js/front.js b/js/front.js
new file mode 100644 (file)
index 0000000..172749a
--- /dev/null
@@ -0,0 +1,953 @@
+var front = {
+       revision: 38,
+       
+       cur_channel: '',
+       windows: {},
+       tabviews: {},
+       boxes: {},
+       
+       buffer: [],
+       buffer_pos: 0,
+       
+       init: function(){       
+               gateway.nick = 'kiwi_'+Math.ceil(100*Math.random())+Math.ceil(100*Math.random());
+               gateway.session_id = null;
+               
+               $(gateway).bind("onmsg", front.onMsg);
+               $(gateway).bind("onnotice", front.onNotice);
+               $(gateway).bind("onaction", front.onAction);
+               $(gateway).bind("onmotd", front.onMOTD);
+               $(gateway).bind("onoptions", front.onOptions);
+               $(gateway).bind("onconnect", front.onConnect);
+               $(gateway).bind("onnick", front.onNick);
+               $(gateway).bind("onuserlist", front.onUserList);
+               $(gateway).bind("onuserlist_end", front.onUserListEnd);
+               $(gateway).bind("onjoin", front.onJoin);
+               $(gateway).bind("ontopic", front.onTopic);
+               $(gateway).bind("onpart", front.onPart);
+               $(gateway).bind("onkick", front.onKick);
+               $(gateway).bind("onquit", front.onQuit);
+               $(gateway).bind("onwhois", front.onWhois);
+               $(gateway).bind("onsync", front.onSync);
+               $(gateway).bind("onchannel_redirect", front.onChannelRedirect);
+               $(gateway).bind("ondebug", front.onDebug);
+               
+               this.buffer = new Array();
+               
+               // Build the about box
+               front.boxes.about = new box("about");
+               front.boxes.about.content.html('<h2>Kiwi IRC</h2>');
+               front.boxes.about.content.append("<p>An alternative to downloading an irc client. Kiwi IRC is the best web app you'll use for the next couple years.</p>");
+               front.boxes.about.content.append('<button class="about_close">Close</button>');
+               
+               var about_info = 'UI adapted for '+agent;
+               if(touchscreen) about_info += ' touchscreen ';
+               about_info += 'usage';
+               
+               front.boxes.about.content.append('<p class="info">'+about_info+'</p>');
+               front.boxes.about.content.append('<p class="revisions">Front: '+front.revision+'<br />Gateway: '+gateway.revision+'</p>');
+               
+               //$(window).bind("beforeunload", function(){ gateway.quit(); });
+               
+               front.registerKeys();
+               
+               
+               $('#kiwi .formconnectwindow').submit(function(){
+                       var netsel = $('#kiwi .formconnectwindow .network');
+                       var nick = $('#kiwi .formconnectwindow .nick');
+                       
+                       if(nick.val() == ''){
+                               nick.val('Nick please!');
+                               nick.focus();
+                               return false;
+                       }
+                       
+                       var tmp = nick.val().split(' ');
+                       gateway.nick = tmp[0];
+                       front.doLayout();
+                       try {
+                               front.run('/connect '+netsel.val());
+                       } catch(e){
+                               alert(e);
+                       }
+                       
+                       $('#kiwi .connectwindow').slideUp();
+                       return false;
+               });
+
+               var supportsOrientationChange = "onorientationchange" in window,
+                   orientationEvent = supportsOrientationChange ? "orientationchange" : "resize";
+               window.addEventListener(orientationEvent, front.doLayoutSize, false);
+               //$('#kiwi').bind("resize", front.doLayoutSize, false);
+
+               front.doLayout();
+               //front.windowAdd('server');
+               front.tabviewAdd('server');
+               
+               // Any pre-defined nick?
+               if(typeof init_data.nick == "string") $('#kiwi .formconnectwindow .nick').val(init_data.nick);
+               
+               //gateway.session_id = 'testses';
+               
+               gateway.start();
+               //front.sync();
+       },
+       
+       doLayoutSize: function() {
+           var kiwi = $('#kiwi');
+               if(kiwi.width() < 330 && !kiwi.hasClass('small_kiwi')){
+                       console.log("switching to small kiwi");
+                       kiwi.removeClass('large_kiwi');
+                       kiwi.addClass('small_kiwi');
+               } else if(kiwi.width() >= 330 && !kiwi.hasClass('large_kiwi')) {
+                       console.log("switching to large kiwi");
+                       kiwi.removeClass('small_kiwi');
+                       kiwi.addClass('large_kiwi');
+               }
+
+               var ct = $('#kiwi .cur_topic');
+               var ul = $('#kiwi .userlist');
+               var top = ul.css('margin-top').replace('px', '') + ct.css('border-bottom-width').replace('px', '');
+               //top = parseInt(ct.offset().top) + parseInt(ct.height()) + parseInt(top);
+               top = parseInt(ct.height()) + parseInt(top);
+
+               $('#kiwi .messages').css('top', top+"px");
+               $('#kiwi .userlist').css('top', top+"px");
+       },
+
+
+       doLayout: function(){
+               $('#kiwi .msginput .nick a').text(gateway.nick);
+               $('#kiwi_msginput').val(' ');
+               $('#kiwi_msginput').focus();
+       },
+       
+       
+       joinChannel: function(chan_name){
+               var chans = chan_name.split(',');
+               for(var i in chans){
+                       chan = chans[i];
+                       if(front.tabviews[chan.toLowerCase()] == undefined){
+                               gateway.join(chan);
+                               front.tabviewAdd(chan);
+                       } else {
+                               front.tabviews[chan.toLowerCase()].show();
+                       }
+               }
+       },
+       
+       
+       run: function(msg){
+               if(msg.substring(0,1) == '/'){
+                       var parts = msg.split(' ');
+                       switch(parts[0]){
+                               case '/j':
+                               case '/join':
+                                       front.joinChannel(parts[1]);
+                                       break;
+                                       
+                               case '/connect':
+                               case '/server':
+                                       if(parts[1] == undefined){
+                                               alert('Usage: /connect servername [port]');
+                                               break;
+                                       }
+                                       
+                                       if(parts[2] == undefined) parts[2] = 6667;
+                                       front.cur_channel.addMsg(null, ' ', '=== Connecting to '+parts[1]+'...', 'status');
+                                       gateway.connect(parts[1], parts[2], 0);
+                                       break;
+                                       
+                               case '/part':
+                                       if(typeof parts[1] == "undefined"){
+                                               gateway.raw(msg.substring(1)+' '+front.cur_channel.name);
+                                       } else {
+                                               gateway.raw(msg.substring(1));
+                                       }
+                                       break;
+                                       
+                               case '/names':
+                                       if(typeof parts[1] != "undefined"){
+                                               gateway.raw(msg.substring(1));
+                                       }
+                                       break;
+                                       
+                               case '/debug':
+                                       gateway.debug();
+                                       break;
+                                       
+                               case '/q':
+                               case '/query':
+                                       if(typeof parts[1] != "undefined"){
+                                               front.tabviewAdd(parts[1]);
+                                       }
+                                       break;
+                                       
+                               case '/quote':
+                                       gateway.raw(msg.replace(/^\/quote /i,''));
+                                       break;
+                                       
+                               case '/me':
+                                       gateway.action(front.cur_channel.name, msg.substring(4));
+                                       //front.tabviews[destination.toLowerCase()].addMsg(null, ' ', '* '+data.nick+' '+data.msg, 'color:green;');
+                                       front.cur_channel.addMsg(null, ' ', '* '+gateway.nick+' '+msg.substring(4), 'action', 'color:#555;');
+                                       break;
+                                       
+                               default:
+                                       //front.cur_channel.addMsg(null, ' ', '--> Invalid command: '+parts[0].substring(1));
+                                       gateway.raw(msg.substring(1));
+                       }
+
+               } else {
+                       //alert('Sending message: '+msg);
+                       if(msg.trim() == '') return;
+                       gateway.msg(front.cur_channel.name, msg);
+                       var d = new Date();
+                       var d = d.getHours() + ":" + d.getMinutes();
+                       //front.addMsg(d, gateway.nick, msg);
+                       front.cur_channel.addMsg(null, gateway.nick, msg);
+               }
+       },
+       
+       
+       onMsg: function(e, data){
+               // Is this message from a user?
+               if(data.channel == gateway.nick){
+                       var destination = data.nick.toLowerCase();
+               } else {
+                       var destination = data.channel.toLowerCase();   
+               }
+               
+               if(!front.tabviewExists(destination)) front.tabviewAdd(destination);
+               front.tabviews[destination].addMsg(null, data.nick, data.msg);
+       },
+       
+       onDebug: function(e, data){
+               if(!front.tabviewExists('kiwi_debug')) front.tabviewAdd('kiwi_debug');
+               front.tabviews['kiwi_debug'].addMsg(null, ' ', data.msg);
+       },
+       
+       onAction: function(e, data){
+               // Is this message from a user?
+               if(data.channel == gateway.nick){
+                       var destination = data.nick;
+               } else {
+                       var destination = data.channel; 
+               }
+               
+               if(!front.tabviewExists(destination)) front.tabviewAdd(destination);
+               front.tabviews[destination.toLowerCase()].addMsg(null, ' ', '* '+data.nick+' '+data.msg, 'action', 'color:#555;');
+       },
+       
+       onTopic: function(e, data){
+               if(front.tabviewExists(data.channel)){
+                       front.tabviews[data.channel.toLowerCase()].changeTopic(data.topic);                     
+               }
+       },
+       
+       onNotice: function(e, data){
+               var nick = (data.nick=="") ? "" : '['+data.nick+']';
+               if(data.channel != undefined){
+                       //alert('notice for '+data.channel);
+                       if(front.tabviewExists(data.channel)){
+                               front.tabviews[data.channel.toLowerCase()].addMsg(null, nick, data.msg, 'notice');
+                       }
+               } else {
+                       //alert('direct notice');
+                       front.tabviews['server'].addMsg(null, nick, data.msg, 'notice');
+               }
+       },
+       onConnect: function(e, data){
+               if(data.connected){
+                       front.tabviews['server'].addMsg(null, ' ', '=== Connected OK :)', 'status');
+                       if(typeof init_data.channel == "string"){
+                               front.joinChannel(init_data.channel);
+                       }
+               } else {
+                       front.tabviews['server'].addMsg(null, ' ', '=== Failed to connect :(', 'status');
+               }
+       },
+       onOptions: function(e, data){
+               if(typeof gateway.network_name == "string" && gateway.network_name != ""){
+                       front.tabviews['server'].tab.text(gateway.network_name);
+               }
+       },
+       onMOTD: function(e, data){
+               front.tabviews['server'].addMsg(null, data.server, data.msg, 'motd');
+       },
+       onWhois: function(e, data){
+               front.cur_channel.addMsg(null, data.nick, data.msg, 'whois');
+       },
+       onUserList: function(e, data){
+               if(front.tabviews[data.channel.toLowerCase()] == undefined) return;
+               
+               var ul = front.tabviews[data.channel.toLowerCase()].userlist;
+               
+               if(!document.userlist_updating){
+                       document.userlist_updating = true;
+                       ul.empty();
+               }
+               
+               $.each(data.users, function(i,item){
+                       var nick = i; //i.match(/^.+!/g);
+                       var mode = item;
+                       $('<li><a class="nick" onclick="front.userClick(this);">'+mode+nick+'</a></li>').appendTo(ul);
+               });
+               
+               front.tabviews[data.channel.toLowerCase()].userlistSort();
+       },
+       onUserListEnd: function(e, data){
+               document.userlist_updating = false;
+       },
+       
+       onJoin: function(e, data){
+               if(!front.tabviewExists(data.channel)){
+                       front.tabviewAdd(data.channel.toLowerCase());
+               }
+               
+               if(data.nick == gateway.nick) return; // Not needed as it's already in nicklist
+               front.tabviews[data.channel.toLowerCase()].addMsg(null, ' ', '--> '+data.nick+' has joined', 'action', 'color:#009900;');
+               $('<li><a class="nick" onclick="front.userClick(this);">'+data.nick+'</a></li>').appendTo(front.tabviews[data.channel.toLowerCase()].userlist);
+               front.tabviews[data.channel.toLowerCase()].userlistSort();
+       },
+       onPart: function(e, data){
+               if(front.tabviewExists(data.channel)){
+                       // If this is us, close the tabview
+                       if(data.nick == gateway.nick){
+                               front.tabviews[data.channel.toLowerCase()].close();
+                               front.tabviews['server'].show();
+                               return;
+                       }
+                       
+                       front.tabviews[data.channel.toLowerCase()].addMsg(null, ' ', '<-- '+data.nick+' has left ('+data.message+')', 'action', 'color:#990000;');
+                       front.tabviews[data.channel.toLowerCase()].userlist.children().each(function(){
+                               if($(this).text() == data.nick){
+                                       $(this).remove();
+                               }
+                       });
+               }
+       },
+       onKick: function(e, data){
+               if(front.tabviewExists(data.channel)){
+                       // If this is us, close the tabview
+                       if(data.kicked == gateway.nick){
+                               front.tabviews[data.channel.toLowerCase()].close();
+                               return;
+                       }
+                       
+                       front.tabviews[data.channel.toLowerCase()].addMsg(null, ' ', '<-- '+data.kicked+' kicked by '+data.nick+'('+data.message+')', 'action', 'color:#990000;');
+                       front.tabviews[data.channel.toLowerCase()].userlist.children().each(function(){
+                               if($(this).text() == data.nick){
+                                       $(this).remove();
+                               }
+                       });
+               }
+       },
+       onNick: function(e, data){
+               if(data.nick == gateway.nick){
+                       gateway.nick = data.newnick;
+                       front.doLayout();
+               }
+               
+               $.each(front.tabviews, function(i,item){
+                       $.each(front.tabviews, function(i,item){
+                               item.changeNick(data.newnick, data.nick);
+                       });
+               });
+       },
+       onQuit: function(e, data){
+               $.each(front.tabviews, function(i,item){
+                       $.each(front.tabviews, function(i,item){
+                               item.userlist.children().each(function(){
+                                       if($(this).text() == data.nick){
+                                               $(this).remove();
+                                               item.addMsg(null, ' ', '<-- '+data.nick+' has quit ('+data.message+')', 'action', 'color:#990000;');
+                                       }
+                               });
+                       });
+               });
+       },
+       onChannelRedirect: function(e, data){
+               front.tabviews[data.from.toLowerCase()].close();
+               front.tabviewAdd(data.to.toLowerCase());
+               front.tabviews[data.to.toLowerCase()].addMsg(null, ' ', '=== Redirected from '+data.from, 'action');
+       },
+       
+       registerKeys: function(){
+               $('#kiwi_msginput').bind('keydown', function(e){
+               //$('input').keypress(function(e){
+                       switch(e.which){
+                               case 27:                        // escape                                       
+                                       return false;
+                                       break;
+                               case 13:                        // return
+                                       var msg = $('#kiwi_msginput').val();
+                                       msg = msg.trim();
+                                       
+                                       front.buffer.push(msg);
+                                       front.buffer_pos = front.buffer.length;
+                                       
+                                       front.run(msg);
+                                       $('#kiwi_msginput').val('');
+                                       
+                                       break;
+                                       
+                               case 38:                        // up
+                                       if(front.buffer_pos > 0){
+                                               front.buffer_pos--;
+                                               $('#kiwi_msginput').val(front.buffer[front.buffer_pos]);
+                                       }
+                                       break;
+                               case 40:                        // down
+                                       if(front.buffer_pos < front.buffer.length){
+                                               front.buffer_pos++;
+                                               $('#kiwi_msginput').val(front.buffer[front.buffer_pos]);
+                                       }
+                                       break;
+                                       
+                               case 9:                         // tab
+                                       // Get possible autocompletions
+                                       var data = [];
+                                       front.cur_channel.userlist.children().each(function(){
+                                               nick = front.nickStripPrefix($('a.nick', this).text());
+                                               data.push(nick);
+                                       });
+                                       
+                                       // Do the autocomplete
+                                       if (this.value.length == this.selectionStart && this.value.length == this.selectionEnd) {
+                                               var candidates = [];
+                                               
+                                               var word_pos = this.value.lastIndexOf(' ');
+                                               var word = "";
+                                               if(word_pos == -1){
+                                                       word = this.value;
+                                               } else {
+                                                       word = this.value.substr(word_pos);
+                                               }
+                                               word = word.trim();
+                                               
+                                               // filter data to find only strings that start with existing value
+                                               for (var i=0; i < data.length; i++) {
+                                                       if (data[i].indexOf(word) == 0 && data[i].length > word.length)
+                                                               candidates.push(data[i]);
+                                               }
+                                               
+                                               if (candidates.length > 0) {
+                                                       // some candidates for autocompletion are found
+                                                       this.value = this.value.substring(0, word_pos) + ' ' + candidates[0]+': ';
+                                                       this.selectionStart = this.value.length;
+                                               }
+                                       }
+                                       return false;
+                                       
+                                       break;
+                       }
+               });
+               
+               
+               $('#kiwi .control .msginput .nick').click(function(){
+                       var html = '<div class="newnick box">\
+       Your new nick:<br />\
+       <form class="form_newnick">\
+               <input type="text" class="txtnewnick" /><br />\
+               <button class="butnewnick" type="submit">Change</button> <a class="link cancelnewnick">Cancel</a>\
+       </form>\
+</div>';
+                       $('#kiwi').append(html);
+                       $('#kiwi .form_newnick').submit(function(){
+                               front.run('/NICK '+$('#kiwi .txtnewnick').val());
+                               $('#kiwi .newnick').remove();
+                               return false;
+                       });
+                       $('#kiwi .cancelnewnick').click(function(){
+                               $('#kiwi .newnick').remove();
+                       });
+                       
+                       $('#kiwi .txtnewnick').focus();
+                       
+               });
+               
+               
+               
+               
+               
+               $('#kiwi .plugins .load_plugin_file').click(function(){
+                       if(typeof front.boxes.plugins != "undefined") return;
+                       
+                       var html = '<div class="list">\
+               <h2>Kiwi plugins</h2>\
+               <p>\
+                       <select multiple="multiple" id="plugin_list">\
+                       </select>\
+                       <button id="plugins_list_unload">Unload</button>\
+               </p>\
+       </div>\
+       <div class="load">\
+               Plugin file URL:<br />\
+               <form>\
+                       <input type="text" class="txtpluginfile" /><br />\
+                       <button class="butnewnick" type="submit">Load..</button> <a class="link cancelpluginfile">Cancel</a>\
+               </form>\
+       </div>';
+                       front.boxes.plugins = new box("plugin_file");
+                       front.boxes.plugins.content.html(html);
+                       front.boxes.plugins.box.css('top', -(front.boxes.plugins.height+40));
+                       
+                       // Populate the plugin list..
+                       var lst = $('#plugin_list');
+                       lst.find('option').remove();
+                       for(var i in plugins.privmsg){
+                               var txt = plugins.privmsg[i].name;
+                               lst.append('<option value="'+txt+'">'+txt+'</option>');
+                       }
+                       
+                       // Event bindings
+                       $('#kiwi .plugin_file').submit(function(){
+                               $.getJSON($('.txtpluginfile').val(), function(data) {
+                                       var plg = {};
+                                       plg.name = data.name;
+                                       eval("plg.onprivmsg = "+data.onprivmsg);
+                                       eval("plg.onload = "+data.onload);
+                                       eval("plg.onunload = "+data.onunload);
+                                       plugins.privmsg.push(plg);
+                                       
+                                       if(plg.onload instanceof Function) plg.onload();
+                               });
+                               return false;
+                       });
+                       $('#kiwi .cancelpluginfile').click(function(){
+                               front.boxes.plugins.destroy();
+                       });
+                       
+                       $('#kiwi #plugins_list_unload').click(function(){
+                               var selected_plugin = $('#plugin_list').val();
+                               console.log("removing plugin: "+selected_plugin);
+                               for(var i in plugins.privmsg){
+                                       if(plugins.privmsg[i].name == selected_plugin){
+                                               if(plugins.privmsg[i].onunload instanceof Function)
+                                                       plugins.privmsg[i].onunload();
+                                               
+                                               delete plugins.privmsg[i];
+                                       }
+                               }
+                       });
+                       
+                       $('#kiwi .txtpluginfile').focus();
+                       
+               });
+               
+               $('#kiwi .plugins .reload_css').click(function(){
+                       var links = document.getElementsByTagName("link");
+                       for (var i=0; i < links.length; i++){
+                               if(links[i].rel === "stylesheet"){
+                                       if(links[i].href.indexOf("?")===-1){
+                                               links[i].href += "?";
+                                       }
+                                       links[i].href += "x";
+                               }
+                       }
+               });
+
+
+               $('#kiwi .about .about_close').click(function(){
+                       $('#kiwi .about').css('display', 'none');
+               });
+               
+               
+               $('#kiwi .poweredby').click(function(){
+                       $('#kiwi .about').css('display', 'block');
+               });
+               
+       },
+       
+       
+       tabviewExists: function(name){
+               return !(front.tabviews[name.toLowerCase()] == undefined);
+       },
+       
+       tabviewAdd: function(v_name){
+               if(v_name.charAt(0) == gateway.channel_prefix){
+                       var re = new RegExp(gateway.channel_prefix,"g");
+                       var htmlsafe_name = v_name.replace(re, 'pre');
+                       htmlsafe_name = "chan_"+htmlsafe_name;
+               } else {
+                       var htmlsafe_name = 'query_'+v_name;
+               }
+               
+               var tmp_divname = 'kiwi_window_'+htmlsafe_name;
+               var tmp_userlistname = 'kiwi_userlist_'+htmlsafe_name;
+               var tmp_tabname = 'kiwi_tab_'+htmlsafe_name
+               
+               $('#kiwi').append('<div id="'+tmp_divname+'" class="messages"></div>');
+               $('#kiwi .userlist').append('<ul id="'+tmp_userlistname+'"></ul>');
+               $('#kiwi .windowlist ul').append('<li id="'+tmp_tabname+'" onclick="front.tabviews[\''+v_name.toLowerCase()+'\'].show();">'+v_name+'</li>');
+               //$('#kiwi .windowlist ul .window_'+v_name).click(function(){ front.windowShow(v_name); });
+               //front.windowShow(v_name);
+               
+               front.tabviews[v_name.toLowerCase()] = new tabview();
+               front.tabviews[v_name.toLowerCase()].name = v_name;
+               front.tabviews[v_name.toLowerCase()].div = $('#'+tmp_divname);
+               front.tabviews[v_name.toLowerCase()].userlist = $('#'+tmp_userlistname);
+               front.tabviews[v_name.toLowerCase()].tab = $('#'+tmp_tabname);
+               front.tabviews[v_name.toLowerCase()].show();
+               
+               if(typeof registerTouches == "function"){
+                       //alert("Registering touch interface");
+                       //registerTouches($('#'+tmp_divname));
+                       registerTouches(document.getElementById(tmp_divname));
+               }
+               /*
+               front.tabviews[v_name.toLowerCase()].userlist.click(function(){
+                       alert($(this).attr('id'));
+               });
+               */
+
+               front.doLayoutSize();
+       },
+       
+       
+       userClick: function(item){
+               // Remove any existing userboxes
+               $('#kiwi .userbox').remove();
+               
+               var li = $(item).parent();
+               var html = '<div class="userbox">\
+       <input type="hidden" class="userbox_nick" value="'+front.nickStripPrefix($(item).text())+'" />\
+       <a href="#" class="userbox_query">Message</a>\
+       <a href="#" class="userbox_whois">Info</a>\
+</div>';
+               li.append(html);
+               
+               $('#kiwi .userbox .userbox_query').click(function(ev){
+                       var nick = $('#kiwi .userbox_nick').val();
+                       front.run('/query '+nick);
+               });
+               
+               $('#kiwi .userbox .userbox_whois').click(function(ev){
+                       var nick = $('#kiwi .userbox_nick').val();
+                       front.run('/whois '+nick);
+               });
+       },
+       
+       
+       sync: function(){
+               gateway.sync();
+       },
+       
+       onSync: function(e, data){
+               // Set any settings
+               if(data.nick != undefined) gateway.nick = data.nick;
+               
+               // Add the tabviews
+               if(data.tabviews != undefined){
+                       $.each(data.tabviews, function(i,tab){
+                               if(!front.tabviewExists(tab.name)){
+                                       front.tabviewAdd(gateway.channel_prefix+tab.name);
+                                       
+                                       if(tab.userlist != undefined)
+                                               front.onUserList({'channel':gateway.channel_prefix+tab.name, 'users':tab.userlist});
+                               }
+                       });
+               }
+               
+               front.doLayout();
+       },
+       
+       
+       setTopicText: function(new_topic){
+               $('#kiwi .cur_topic').text(new_topic);
+       },
+       
+       
+       
+       
+       
+       
+       
+       nickStripPrefix: function(nick){
+               var tmp = nick;
+               
+               prefix = tmp.charAt(0);
+               if(typeof gateway.user_prefixes[prefix] != "undefined") tmp = tmp.substring(1);
+               
+               return tmp;
+       },
+       
+       nickGetPrefix: function(nick){
+               var tmp = nick;
+               
+               prefix = tmp.charAt(0);
+               if(typeof gateway.user_prefixes[prefix] == "undefined"){
+                       prefix = "";
+               }
+               
+               return prefix;
+       },
+       
+       isChannel: function(name){
+               prefix = name.charAt(0);
+               if(gateway.channel_prefix.indexOf(prefix) > -1){
+                       is_chan = true;
+               } else {
+                       is_chan = false;
+               }
+               
+               return is_chan;
+       },
+       
+       tabviewsNext: function(){
+               var wl = $('#kiwi .windowlist ul');
+               var next_left = parseInt(wl.css('text-indent').replace('px', ''))+170;
+               wl.css('text-indent', next_left);
+       },
+       
+       tabviewsPrevious: function(){
+               var wl = $('#kiwi .windowlist ul');
+               var next_left = parseInt(wl.css('text-indent').replace('px', ''))-170;
+               wl.css('text-indent', next_left);
+       }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/*
+ *
+ *   TABVIEWS
+ *
+ */
+
+
+var tabview = function(){}
+tabview.prototype.name = null;
+tabview.prototype.div = null;
+tabview.prototype.userlist = null;
+tabview.prototype.tab = null;
+tabview.prototype.topic = "";
+
+tabview.prototype.show = function(){
+       $('#kiwi .messages').removeClass("active");
+       $('#kiwi .userlist ul').removeClass("active");
+       $('#kiwi .windowlist ul li').removeClass("active");
+       
+       // Activate this tab!
+       this.div.addClass('active');
+       this.userlist.addClass('active');
+       this.tab.addClass('active');
+       
+       document.tmp = this.div;
+       // Add the part image to the tab
+       this.addPartImage();
+       
+       this.clearHighlight();
+       front.setTopicText(this.topic);
+       front.cur_channel = this;
+       
+       this.scrollBottom();
+       $('#kiwi_msginput').focus();
+}
+
+tabview.prototype.close = function(){
+       this.div.remove();
+       this.userlist.remove();
+       this.tab.remove();
+       
+       front.tabviews['server'].show();
+       delete front.tabviews[this.name.toLowerCase()];
+}
+
+tabview.prototype.addPartImage = function(){
+       this.clearPartImage();
+       
+       var del_html = '<img src="img/redcross.png" class="tab_part" />';
+       this.tab.append(del_html);
+       
+       $('.tab_part', this.tab).click(function(){
+               if(front.isChannel($(this).parent().text())){
+                       front.run("/part");
+               } else {
+                       // Make sure we don't close the server tab
+                       if(front.cur_channel.name != "server") front.cur_channel.close();
+               }
+       });
+}
+
+tabview.prototype.clearPartImage = function(){
+       $('#kiwi .windowlist .tab_part').remove();
+}
+
+tabview.prototype.addMsg = function(time, nick, msg, type, style){
+       //if(nick.charAt(0) != "[" && nick != ""){
+       //      var html_nick = $('<div/>').text('<'+nick+'>').html();
+       //} else {
+               var html_nick = $('<div/>').text(nick).html();
+       //}
+       
+       var self = this;
+       
+       var tmp = msg;
+       var plugin_ret = '';
+       for(var i in plugins.privmsg){
+               if ((plugins.privmsg[i].onprivmsg instanceof Function)) {
+                       plugin_ret = '';
+                       try {
+                               plugin_ret = plugins.privmsg[i].onprivmsg(tmp, this.name);
+                               
+                               // If this plugin has returned false, do not add this message
+                               if(plugin_ret === false) return;
+                       } catch (e){}
+                       
+                       // If we actually have a string from the plugin, use it
+                       if(typeof plugin_ret == "string") tmp = plugin_ret;
+               }
+       }
+       msg = tmp;
+       
+       //var html_msg = $('<div/>').text(msg).html()+' '; // Add the space so the styling always has at least 1 character to go from
+       if(time == null){
+               var d = new Date();
+               time = d.getHours().toString().lpad(2, "0") + ":" + d.getMinutes().toString().lpad(2, "0") + ":" + d.getSeconds().toString().lpad(2, "0");
+       }
+       
+       // The CSS class (action, topic, notice, etc)
+       if(typeof type != "string") type = '';
+       
+       // Make sure we don't have NaN or something
+       if(typeof msg != "string") msg = '';
+       
+       // Text formatting
+       // bold
+       if(msg.indexOf(String.fromCharCode(2))){
+               next = '<b>';
+               while(msg.indexOf(String.fromCharCode(2)) != -1){
+                       msg = msg.replace(String.fromCharCode(2), next);
+                       next = (next=='<b>') ? '</b>' : '<b>';
+               }
+               if(next == '</b>') msg =+ '</b>';
+       }
+       
+       // Wierd thing noticed by Dux0r on the irc.esper.net server
+       if(typeof msg != "string") msg = '';
+       
+       // underline
+       if(msg.indexOf(String.fromCharCode(31))){
+               next = '<u>';
+               while(msg.indexOf(String.fromCharCode(31)) != -1){
+                       msg = msg.replace(String.fromCharCode(31), next);
+                       next = (next=='<u>') ? '</u>' : '<u>';
+               }
+               if(next == '</u>') msg =+ '</u>';
+       }
+       
+       
+       var line_msg = '<div class="msg '+type+'"><div class="time">'+time+'</div><div class="nick">'+html_nick+'</div><div class="text" style="'+style+'">'+msg+' </div></div>';
+       this.div.append(line_msg);
+       this.scrollBottom();
+}
+
+tabview.prototype.scrollBottom = function(){
+       this.div.attr({ scrollTop: this.div.attr("scrollHeight") });
+}
+
+tabview.prototype.changeNick = function(newNick, oldNick){
+       this.userlist.children().each(function(){
+               var item = $('a.nick', this);
+               if(front.nickStripPrefix(item.text()) == oldNick){
+                       item.text(front.nickGetPrefix(item.text())+newNick);
+                       document.temp_chan = 1;
+               }
+       });
+       
+       if(typeof document.temp_chan != "undefined"){
+               this.addMsg(null, ' ', '=== '+oldNick+' is now known as '+newNick, 'action');
+               delete document.temp_chan;
+               this.userlistSort();
+       }
+}
+
+tabview.prototype.userlistSort = function(){
+       var ul = this.userlist;
+       var listitems = ul.children('li').get();
+       listitems.sort(function(a, b) {
+               var compA = $(a).text().toUpperCase();
+               var compB = $(b).text().toUpperCase();
+               
+               // Sort by prefixes first
+               for (var prefix in gateway.user_prefixes) {
+                       mode = gateway.user_prefixes[prefix];
+                       
+                       if(compA.charAt(0) == prefix && compB.charAt(0) == prefix){
+                               // Both have the same prefix, string compare time
+                               return 0;
+                       }
+                       
+                       if(compA.charAt(0) == prefix && compB.charAt(0) != prefix){
+                               return -1;
+                       }
+                       
+                       if(compA.charAt(0) != prefix && compB.charAt(0) == prefix){
+                               return 1;
+                       }
+               }
+                          
+               // No prefixes, compare by string
+               return (compA < compB) ? -1 : (compA > compB) ? 1 : 0;
+       })
+       $.each(listitems, function(idx, itm) { ul.append(itm); });
+}
+
+tabview.prototype.highlight = function(){
+       this.tab.addClass('highlight');
+}
+tabview.prototype.activity = function(){
+       this.tab.addClass('activity');
+}
+tabview.prototype.clearHighlight = function(){
+       this.tab.removeClass('highlight');
+       this.tab.removeClass('activity');
+}
+tabview.prototype.changeTopic = function(new_topic){
+       this.topic = new_topic;
+       this.addMsg(null, ' ', '=== Topic for '+this.name+' is: '+new_topic, 'topic');
+       if(front.cur_channel.name == this.name) front.setTopicText(new_topic);
+}
+
+
+
+
+
+var box = function(classname){
+       this.id = randomString(10);
+       var tmp = $('<div id="'+this.id+'" class="box '+classname+'"><div class="boxarea"></div></div>');
+       $('#kiwi').append(tmp);
+       this.box = $('#'+this.id);
+       this.content = $('#'+this.id+' .boxarea');
+       
+       this.box.draggable({ stack: ".box" });
+       this.content.click(function(){});
+       //this.box.click(function(){ $(this)..css });
+}
+box.prototype.create = function(name, classname){
+       
+}
+box.prototype.id = null;
+box.prototype.box = null;
+box.prototype.content = null;
+box.prototype.destroy = function(){
+       this.box.remove();
+       for (var name in front.boxes) if(front.boxes[name].id = this.id) delete front.boxes[name];
+}
+box.prototype.height = function(){ return this.box.height(); }
\ No newline at end of file
diff --git a/js/front_backup.js b/js/front_backup.js
new file mode 100644 (file)
index 0000000..920359d
--- /dev/null
@@ -0,0 +1,143 @@
+var front = {
+       cur_channel: '',
+       windows: {},
+       
+       
+       init: function(){
+               front.registerKeys();
+               
+               gateway.nick = 'kiwiclone';
+               gateway.session_id = null;
+               gateway.onMsg = front.onMsg;
+               gateway.onNotice = front.onNotice;
+               gateway.onMOTD = front.onMOTD;
+               gateway.onConnect = front.onConnect;
+               gateway.onUserList = front.onUserList;
+               
+               front.doLayout();
+               front.windowAdd('server');
+               gateway.poll();
+       },
+       
+       doLayout: function(){
+               $('#kiwi .msginput .nick a').text(gateway.nick);
+       },
+       
+       
+       onMsg: function(data){
+               front.addMsg(null, data.nick,data.msg,data.channel);
+       },
+       onNotice: function(data){
+               front.addMsg(null, data.nick,  '--> '+data.msg);
+       },
+       onConnect: function(data){
+               if(data.connected){
+                       front.addMsg(null, ' ', '--> Connected to '+data.host);
+               } else {
+                       front.addMsg(null, ' ', '--> Failed to connect to '+data.host);
+               }
+       },
+       onMOTD: function(data){
+               front.addMsg(null, data.server, data.msg);
+       },
+       onUserList: function(data){
+               $.each(data.users, function(i,item){
+                       $('<li>'+i+'</li>').appendTo('#kiwi .userlist ul');
+               });
+       },
+       
+       registerKeys: function(){
+               $('input').keypress(function(e){
+                       if(e.which == 13){
+                               var msg = $('#kiwi_msginput').val();
+                               if(msg.substring(0,1) == '/'){
+                                       var parts = msg.split(' ');
+                                       switch(parts[0]){
+                                               case '/join':
+                                                       if(front.windows[parts[1]] == undefined){
+                                                               gateway.join(parts[1].replace('#', ''));
+                                                               front.windowAdd(parts[1]);
+                                                       } else {
+                                                               front.windowShow(parts[1]);
+                                                       }
+                                                       break;
+                                                       
+                                               case '/connect':
+                                                       if(parts[1] == undefined){
+                                                               alert('Usage: /connect servername [port]');
+                                                               break;
+                                                       }
+                                                       
+                                                       if(parts[2] == undefined) parts[2] = 6667;
+                                                       front.addMsg(null, ' ', '--> Connecting to '+parts[1]+'...');
+                                                       gateway.connect(parts[1], parts[2], 0);
+                                                       break;
+                                               
+                                               default:
+                                                       front.addMsg(null, ' ', '--> Invalid command: '+parts[0].substring(1));
+                                       }
+                                       
+                               } else {
+                                       gateway.msg(front.cur_channel, msg);
+                                       var d = new Date();
+                                       var d = d.getHours() + ":" + d.getMinutes();
+                                       front.addMsg(d, gateway.nick, msg);
+                               }
+                               $('#kiwi_msginput').val('');
+                       }
+               });
+       },
+       
+       
+       
+       addMsg: function(time, nick, msg, channel){
+               var html_nick = $('<div/>').text(nick).html();
+               var html_msg = $('<div/>').text(msg).html()+' '; // Add the space so the styling always has at least 1 character to go from
+               if(time == null){
+                       var d = new Date();
+                       time = d.getHours() + ":" + d.getMinutes();
+               }
+               
+               var msg = '<div class="msg"><div class="time">'+time+'</div><div class="nick">'+html_nick+'</div><div class="text">'+html_msg+'</div></div>';
+               if(channel == undefined){
+                       var messages = $("#kiwi_window_server");
+               } else {
+                       var messages = $("#kiwi_window_chan_"+channel.replace('#', ''));
+               }
+               messages.append(msg);
+               messages.attr({ scrollTop: messages.attr("scrollHeight") });
+       },
+       
+       
+       
+       windowExists: function(name){
+               return !(front.windows[name] == undefined);
+       },
+       windowAdd: function(v_name){
+               var tmp_divname = 'kiwi_window_'+v_name.replace('#', 'chan_');
+               front.windows[v_name] = { name: v_name, div_id: tmp_divname };
+               $('#kiwi').append('<div id="'+tmp_divname+'"class="messages"></div>');
+               $('#kiwi .windowlist ul').append('<li class="window_'+v_name+'"><a onclick="front.windowShow(\''+v_name+'\');">'+v_name+'</a></li>');
+               //$('#kiwi .windowlist ul .window_'+v_name).click(function(){ front.windowShow(v_name); });
+               front.windowShow(v_name);
+               
+               /*
+               var t = "";
+               for (key in front.windows)
+                       t += "Element value is " + front.windows[key].div_id + "\n";
+                       
+               alert(t);
+               */
+       },
+       windowDiv: function(name){
+               if(!front.windowExists(name)) return false;
+               return $('#'+front.windows[name].div_id);
+       },
+       windowShow: function(name){
+               if(!front.windowExists(name)) return false;
+               $('#kiwi .messages').removeClass("active");
+               var tmp = front.windowDiv(name);
+               tmp.addClass('active');
+               front.cur_channel = name;
+       }
+}
\ No newline at end of file
diff --git a/js/gateway.js b/js/gateway.js
new file mode 100644 (file)
index 0000000..610d6e5
--- /dev/null
@@ -0,0 +1,274 @@
+var gateway = {
+
+       revision: 16,
+
+       nick: 'kiwi',
+       session_id: null,
+       syncing: false,
+       channel_prefix: '#',
+       network_name: '',
+       user_prefixes: [],
+
+       
+       connect: function(s_host, s_port, s_ssl, callback){
+               var data = {
+                       method: 'connect',
+                       args: {
+                               server: s_host,
+                               port: s_port,
+                               ssl: s_ssl,
+                               nick: this.nick
+                       }
+               };
+               
+               //$.post('poll.php', data, callback, 'json');
+               gateway.sendData(data, callback);
+       },
+       
+       
+       
+       
+       start: function(){
+               if(typeof WebSocket != "undefined"){
+                       //alert("Starting websocket support..");
+                       gateway.socket();
+               } else {
+                       //alert("Starting polling support..");
+                       gateway.poll();
+               }
+       },
+       
+       
+       poll: function(){
+               if(this.session_id == null){
+                       gateway.sendData = function(data, callback){
+                               data = {
+                                       sid: this.session_id,
+                                       data: $.toJSON(data)
+                               }
+                               $.post('poll.php', data, callback, 'json');
+                       }
+                       
+                       $.post("poll.php", {},
+                               function(data, status){
+                                       if(data.session_id != undefined){
+                                               gateway.session_id = data.session_id;
+                                               gateway.poll();
+                                       } else {
+                                               if(typeof data.error == "string"){
+                                                       alert(data.error);
+                                               } else {
+                                                       alert('Woops, something went wrong! Unsure what it is though.. :(');
+                                               }
+                                       }
+                               }, 'json'
+                       );
+                       return;
+               }
+               $.post("poll.php", {sid: this.session_id},
+                       function(data, status){
+                               $.each(data, function(i,item){
+                                       gateway.parse(item);
+                               });
+                               gateway.poll(gateway.session_id);
+                       }, 'json');
+       },
+       
+       
+       socket: function(){
+               gateway.buffer = "";
+               
+               gateway.conn = new WebSocket("ws://p1.kiwiirc.com:7777/client");
+               
+               gateway.sendData = function(data, callback){
+                       gateway.conn.send($.toJSON(data));
+                       if(typeof callback == "function") callback();
+               }
+               
+               //gateway.conn.onopen = function(evt) { alert("Conn opened"); }
+               gateway.conn.onmessage = function(evt) {
+                       gateway.buffer += evt.data;
+                       
+                       if(gateway.buffer.indexOf("\n") > 0){
+                               var msgs = gateway.buffer.split("\n");
+                               for(var i=0; i<msgs.length; i++){
+                                       if(msgs[i].substring(msgs[i].length-1) != "}"){
+                                               gateway.buffer = msgs[i];
+                                               continue;
+                                       }
+                                       
+                                       cmd = (msgs[i].charCodeAt(0) == 0) ? msgs[i].substring(1) : msgs[i];
+                                       console.log(cmd.charCodeAt(0)+"-"+cmd+"-"+cmd.charCodeAt(cmd.length-1));
+                                       obj = eval("("+cmd+")");
+                                       gateway.parse(obj);
+                               }
+                       }
+               }
+               //gateway.conn.onclose = function(evt) { alert("Conn closed"); }
+       },
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       /*
+               Events:
+                       msg
+                       action
+                       server_connect
+                       options
+                       motd
+                       notice
+                       userlist
+                       nick
+                       join
+                       topic
+                       part
+                       kick
+                       quit
+                       whois
+                       syncchannel_redirect
+                       debug
+       */
+       parse: function(item){
+               if(item.event != undefined){
+                       $(gateway).trigger("on"+item.event, item);
+                       
+                       switch(item.event){
+                               case 'options':
+                                       $.each(item.options, function(name,value){
+                                               switch(name){
+                                                       case 'CHANTYPES':
+                                                               gateway.channel_prefix = value.charAt(0);
+                                                               break;
+                                                       case 'NETWORK':
+                                                               gateway.network_name = value;
+                                                               break;
+                                                       case 'PREFIX':
+                                                               gateway.user_prefixes = value;
+                                                               break;
+                                               }
+                                       });
+                                       break;
+                               
+                               case 'sync':
+                                       if(gateway.onSync && gateway.syncing){
+                                               gateway.syncing = false;
+                                               gateway.onSync(item);
+                                       }
+                                       break;
+
+                       }
+               }
+       },
+       
+       sendData: function(){},
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       sync: function(callback){
+               if(this.session_id == null) return;
+               
+               var data = {
+                       method: 'sync',
+                       args: {}
+               };
+               
+               gateway.syncing = true;
+               //$.post('poll.php', data, callback, 'json');
+               gateway.sendData(data, callback);
+       },
+       
+       debug: function(){
+               var data = {
+                       method: 'debug',
+                       args: {}
+               };
+               
+               //$.post('poll.php', data, function(){}, 'json');
+               gateway.sendData(data, callback);
+       },
+       
+       
+       msg: function(s_target, s_msg, callback){
+               var data = {
+                       method: 'msg',
+                       args: {
+                               target: s_target,
+                               msg: s_msg
+                       }
+               };
+               
+               //$.post('poll.php', data, callback, 'json');
+               gateway.sendData(data, callback);
+       },
+       
+       action: function(s_target, s_msg, callback){
+               var data = {
+                       method: 'action',
+                       args: {
+                               target: s_target,
+                               msg: s_msg
+                       }
+               };
+               
+               //$.post('poll.php', data, callback, 'json');
+               gateway.sendData(data, callback);
+       },
+       
+       
+       join: function(s_channel, callback){
+               var data = {
+                       method: 'join',
+                       args: {
+                               channel: s_channel,
+                       }
+               };
+               
+               //$.post('poll.php', data, callback, 'json');
+               gateway.sendData(data, callback);
+       },
+       
+       
+       raw: function(v_data, callback){
+               var data = {
+                       method: 'raw',
+                       args: {
+                               data: v_data,
+                       }
+               };
+               
+               //$.post('poll.php', data, callback, 'json');
+               gateway.sendData(data, callback);
+       },
+       
+       
+       quit: function(msg){
+               alert("closing");
+               msg = "";
+               var data = {
+                       method: 'quit',
+                       args: {
+                               message: msg,
+                       }
+               };
+               
+               gateway.sendData(data, callback);
+       }
+       
+       
+       
+
+};
diff --git a/js/jquery.json-2.2.min.js b/js/jquery.json-2.2.min.js
new file mode 100644 (file)
index 0000000..bad4a0a
--- /dev/null
@@ -0,0 +1,31 @@
+
+(function($){$.toJSON=function(o)
+{if(typeof(JSON)=='object'&&JSON.stringify)
+return JSON.stringify(o);var type=typeof(o);if(o===null)
+return"null";if(type=="undefined")
+return undefined;if(type=="number"||type=="boolean")
+return o+"";if(type=="string")
+return $.quoteString(o);if(type=='object')
+{if(typeof o.toJSON=="function")
+return $.toJSON(o.toJSON());if(o.constructor===Date)
+{var month=o.getUTCMonth()+1;if(month<10)month='0'+month;var day=o.getUTCDate();if(day<10)day='0'+day;var year=o.getUTCFullYear();var hours=o.getUTCHours();if(hours<10)hours='0'+hours;var minutes=o.getUTCMinutes();if(minutes<10)minutes='0'+minutes;var seconds=o.getUTCSeconds();if(seconds<10)seconds='0'+seconds;var milli=o.getUTCMilliseconds();if(milli<100)milli='0'+milli;if(milli<10)milli='0'+milli;return'"'+year+'-'+month+'-'+day+'T'+
+hours+':'+minutes+':'+seconds+'.'+milli+'Z"';}
+if(o.constructor===Array)
+{var ret=[];for(var i=0;i<o.length;i++)
+ret.push($.toJSON(o[i])||"null");return"["+ret.join(",")+"]";}
+var pairs=[];for(var k in o){var name;var type=typeof k;if(type=="number")
+name='"'+k+'"';else if(type=="string")
+name=$.quoteString(k);else
+continue;if(typeof o[k]=="function")
+continue;var val=$.toJSON(o[k]);pairs.push(name+":"+val);}
+return"{"+pairs.join(", ")+"}";}};$.evalJSON=function(src)
+{if(typeof(JSON)=='object'&&JSON.parse)
+return JSON.parse(src);return eval("("+src+")");};$.secureEvalJSON=function(src)
+{if(typeof(JSON)=='object'&&JSON.parse)
+return JSON.parse(src);var filtered=src;filtered=filtered.replace(/\\["\\\/bfnrtu]/g,'@');filtered=filtered.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']');filtered=filtered.replace(/(?:^|:|,)(?:\s*\[)+/g,'');if(/^[\],:{}\s]*$/.test(filtered))
+return eval("("+src+")");else
+throw new SyntaxError("Error parsing JSON, source is not valid.");};$.quoteString=function(string)
+{if(string.match(_escapeable))
+{return'"'+string.replace(_escapeable,function(a)
+{var c=_meta[a];if(typeof c==='string')return c;c=a.charCodeAt();return'\\u00'+Math.floor(c/16).toString(16)+(c%16).toString(16);})+'"';}
+return'"'+string+'"';};var _escapeable=/["\\\x00-\x1f\x7f-\x9f]/g;var _meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'};})(jQuery);
\ No newline at end of file
diff --git a/js/touchscreen_tweaks.js b/js/touchscreen_tweaks.js
new file mode 100644 (file)
index 0000000..ba4d79b
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+       Orientation stuff
+*/
+/*
+var supportsOrientationChange = "onorientationchange" in window,
+    orientationEvent = supportsOrientationChange ? "orientationchange" : "resize";
+
+window.addEventListener(orientationEvent, function() {
+    //alert('rotated' + window.orientation + " " + screen.width);
+       var or = window.orientation;
+       if(or == 90 || or == -90){
+               // We have a landscape screen
+               or = "landscape";
+       } else {
+               // We have a portrait screen
+               or = "portrait";
+       }
+       
+       if(agent == "ipad" || agent == "iphone" || agent == "ipod"){
+               if(or == "landscape"){
+                       width = window.height;
+                       height = window.width;
+               } else {
+                       width = window.width;
+                       height = window.height;
+               }
+       } else {
+               width = window.width;
+               height = window.height;
+       }
+       
+       //alert('adding class: '+or);
+       if(or=="landscape"){
+               $('#kiwi').removeClass("portrait");
+       } else {
+               $('#kiwi').removeClass("landscape");
+       }
+       $('#kiwi').addClass(or);
+}, false);
+*/
+
+
+
+
+
+
+
+
+/*
+       Scroll stuff
+*/
+
+function registerTouches(obj){
+       return;
+//$(document).ready(function(){
+       obj.ontouchstart = function(e) {
+               e.preventDefault();
+               document.touchx_start = e.touches[0].pageX;
+               document.touchy_start = e.touches[0].pageY;
+       }
+       obj.ontouchmove = function(e) {
+               e.preventDefault();
+               document.touchx_cur = e.touches[0].pageX;
+               document.touchy_cur = e.touches[0].pageY;
+               
+               var xdiff = document.touchx_start - document.touchx_cur
+               var ydiff = document.touchy_start - document.touchy_cur
+               
+               var obj = $(this);
+               if(ydiff < -20){
+                       var n = obj.attr("scrollTop")+ydiff;
+                       //alert("down (xdiff="+xdiff+" ydiff="+ydiff+" scrollTop="+obj.attr("scrollTop")+") "+n );
+                       obj.attr("scrollTop", n);
+               } else if(ydiff > 20){
+                       var n = obj.attr("scrollTop")+ydiff;
+                       //alert("up (xdiff="+xdiff+" ydiff="+ydiff+" scrollTop="+obj.attr("scrollTop")+") "+n);
+                       obj.attr("scrollTop", n);
+               }
+       }
+       obj.ontouchend = function(e) {
+               e.preventDefault();
+               
+               var xdiff = document.touchx_start - document.touchx_cur
+               var ydiff = document.touchy_start - document.touchy_cur
+               //alert('x='+xdiff+'   y='+ydiff);
+               //alert('Start: x='+document.touchx+'   y='+document.touchy+"\n"+'End: x='+e.pageX+'   y='+e.pageY);
+               
+               if(xdiff < -150 && (ydiff > -250 && ydiff < 250)){
+                       //alert("next window (xdiff="+xdiff+" ydiff="+ydiff+")");
+                       front.tabviewsNext();
+               } else if(xdiff > 150 && (ydiff > -250 && ydiff < 250)){
+                       //alert("previous window (xdiff="+xdiff+" ydiff="+ydiff+")");
+                       front.tabviewsPrevious();
+               }
+       }
+//});
+}
\ No newline at end of file
diff --git a/js/util.js b/js/util.js
new file mode 100644 (file)
index 0000000..f36703f
--- /dev/null
@@ -0,0 +1,105 @@
+function randomString(string_length) {
+       var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
+       var randomstring = '';
+       for (var i=0; i<string_length; i++) {
+               var rnum = Math.floor(Math.random() * chars.length);
+               randomstring += chars.substring(rnum,rnum+1);
+       }
+       return randomstring;
+}
+
+String.prototype.trim = function() {
+  return this.replace(/^\s+|\s+$/g, "");
+};
+
+String.prototype.lpad = function(length, character){
+       var padding = "";
+       for(var i=0; i<length; i++) padding += character;
+       return (padding + this).slice (-length)
+};
+
+
+
+/*
+       PLUGINS
+       Each function in each object is looped through and ran. The resulting text
+       is expected to be returned.
+*/
+var plugins={}; 
+plugins.privmsg = [
+       {
+               name: "html_safe",
+               onprivmsg: function(inp, tabview){
+                       return $('<div/>').text(inp).html();
+               }
+       },
+       
+       {
+               name: "activity",
+               onprivmsg: function(inp, tabview){
+                       if(front.cur_channel.name.toLowerCase() != front.tabviews[tabview.toLowerCase()].name){
+                               front.tabviews[tabview].activity();
+                       }
+               }
+       },
+       
+       {
+               name: "highlight",
+               onprivmsg: function(inp, tabview){
+                       if(inp.toLowerCase().indexOf(gateway.nick.toLowerCase()) > -1){
+                               if(front.cur_channel.name.toLowerCase() != front.tabviews[tabview.toLowerCase()].name){
+                                       front.tabviews[tabview].highlight();
+                               }
+                               if(front.isChannel(front.tabviews[tabview].name))
+                                       inp = '<span style="color:red;">'+inp+'</span>';
+                       }
+                       
+                       if(
+                               !front.isChannel(front.tabviews[tabview].name) && front.tabviews[tabview].name != "server"
+                               && front.cur_channel.name.toLowerCase() != front.tabviews[tabview.toLowerCase()].name
+                       ){
+                               front.tabviews[tabview].highlight();
+                       }
+                       return inp;
+               }
+       },
+
+       /*
+       {
+               name: "images",
+               onprivmsg: function(text){
+                       if( !text ) return text;
+                       //alert("-"+text+"-");
+                       text = text.replace(/^((https?\:\/\/|ftp\:\/\/)|(www\.))(\S+)(\w{2,4})(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?(\.jpg|\.jpeg|\.gif|\.bmp|\.png)$/gi,function(url){
+                               var img = '<img src="'+url+'" height="50" width="50" />';
+                               return '<a target="_blank" rel="nofollow" href="'+ url +'">'+ img +'</a>';
+                       });
+                       
+                       return text;
+               }
+       },
+       
+       */
+       
+       
+       {
+               //Following method taken from: http://snipplr.com/view/13533/convert-text-urls-into-links/
+               name: "linkify_plain",
+               onprivmsg: function(text){
+                       if( !text ) return text;
+                       
+                       text = text.replace(/((https?\:\/\/|ftp\:\/\/)|(www\.))(\S+)(\w{2,4})(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/gi,function(url){
+                               nice = url;
+                               if(url.match('^https?:\/\/')){
+                                       //nice = nice.replace(/^https?:\/\//i,'')
+                               }else{
+                                       url = 'http://'+url;
+                               }
+                               
+                               return '<a target="_blank" rel="nofollow" href="'+ url +'">'+ nice +'</a>';
+                       });
+                       
+                       return text;
+               }
+       }
+];
\ No newline at end of file
diff --git a/layout.html b/layout.html
new file mode 100644 (file)
index 0000000..33dee7e
--- /dev/null
@@ -0,0 +1,107 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml">\r
+<head>\r
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />\r
+<title>Kiwi IRC</title>\r
+<style type="text/css">\r
+* { margin:0px; padding:0px; }\r
+body {\r
+       background:#1b1b1b;\r
+       font: 14px Tahoma, Geneva, sans-serif;\r
+       color: #D4D4D4;\r
+       height:100%;\r
+}\r
+\r
+a {\r
+       color: #8D9713;\r
+       text-decoration:none;\r
+}\r
+\r
+\r
+\r
+.logo { display:block; width:264px; height:90px; text-indent:-1000px; margin:70px auto 100px; background:url(img/logo.png) no-repeat; }\r
+#login { width:665px; margin:0px auto; }\r
+#login ul { list-style:none; }\r
+#login ul li { overflow:hidden; margin-bottom:10px; }\r
+#login ul li label { float:left; text-align:right; padding-right:.5em; }\r
+#login input { border:none; }\r
+#login .more { border-top: 1px dotted #888888; }\r
+#login .more_link {\r
+       font-size:12px;\r
+       position:relative;\r
+       top:-16px;\r
+       background: url(img/more.png) no-repeat right 5px;\r
+       padding-right: 12px;\r
+       color:#888;\r
+}\r
+\r
+#login .content.top {\r
+       width:470px;\r
+       margin:30px auto 60px;\r
+}\r
+#login .content.top ul { font-size:27px; }\r
+#login .content.top label { width:40%; }\r
+#login .content.top input {\r
+       width:240px; padding:.2em .5em;\r
+       font-size:.8em; text-align:center;\r
+       background:#D4D4D4;\r
+       border-radius:8px; -moz-border-radius:8px;\r
+}\r
+#login .content.top a { display:block; text-align:right; font-size:22px; margin-top:30px; }\r
+\r
+#login .content.bottom { width:350px; margin:30px auto; display:none; }\r
+#login .content.bottom ul { font-size:20px; }\r
+#login .content.bottom label { width:25%; }\r
+#login .content.bottom input { width:216px; font-size:.8em; no-repeat; padding:.2em .5em; background:#D4D4D4; border-radius:8px; -moz-border-radius:8px; }\r
+#login .content.bottom a { display:block; text-align:right; font-size:16px; margin-top:30px; }\r
+</style>\r
+\r
+<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js"></script>\r
+<script type="text/javascript"> \r
\r
+       $(document).ready(function(){\r
+               addEvents();\r
+               $('.nick').focus();\r
+       });\r
+       \r
+       \r
+       function addEvents(){\r
+               $('.more_link').click(function(){ $('.content.bottom').slideDown('fast'); $('.network').focus(); return false; });\r
+               $('.formconnectwindow').submit(function(){ alert("logging in.."); return false; });\r
+               $('a.connect').click(function(){ $('.formconnectwindow').submit(); return false; });\r
+       }\r
+</script>\r
+</head>\r
+\r
+<body>\r
+\r
+<div class="connectwindow">\r
+       <h1 class="logo">Kiwi IRC</h1>\r
+       <div id="login">\r
+               <form class="formconnectwindow">\r
+                       <div class="content top">\r
+                               <ul>\r
+                                       <li><label for="nick">Your nickname:</label>\r
+                                               <input type="text" id="nick" name="nick" class="nick" /></li>\r
+                               </ul>\r
+                               <a class="connect" href="">Connect Â»</a>\r
+                       </div>\r
+                       \r
+                       <div class="more">\r
+                               <a href="" class="more_link">More</a>\r
+                               <div class="content bottom">\r
+                                       <ul>\r
+                                               <li><label for="network">Server:</label>\r
+                                                       <input type="text" id="network" name="network" class="network" value="irc.anonnet.org" /></li>\r
+                                               <li><label for="channel">Channel:</label>\r
+                                                       <input type="text" id="channel" name="channel" class="channel" value="#kiwi" /></li>\r
+                                       </ul>\r
+                                       <a class="connect" href="">Connect Â»</a>\r
+                               </div>\r
+                       </div>\r
+               </form>\r
+       </div>\r
+</div>\r
+\r
+</body>\r
+</html>\r
diff --git a/plugins.js b/plugins.js
new file mode 100644 (file)
index 0000000..8d50ee0
--- /dev/null
@@ -0,0 +1,3 @@
+plugins.privmsg.lol = function(message, tab_name){
+       return message+' .. lol';
+}
\ No newline at end of file
diff --git a/plugins/console.php b/plugins/console.php
new file mode 100644 (file)
index 0000000..90c71fd
--- /dev/null
@@ -0,0 +1,13 @@
+<?php\r
+       \r
+       require("plugin.php");\r
+       $p = new Plugin();\r
+       $p->name = "Console Logger";\r
+\r
+       $p->onprivmsg = <<<JS\r
+function(inp, tabview){\r
+       console.log("> "+tabview+": "+inp);\r
+}\r
+JS;\r
+\r
+       echo $p->export();
\ No newline at end of file
diff --git a/plugins/dev.php b/plugins/dev.php
new file mode 100644 (file)
index 0000000..f8352d1
--- /dev/null
@@ -0,0 +1,18 @@
+<?php\r
+\r
+       require("plugin.php");\r
+       $p = new Plugin();\r
+       $p->name = "Dev Tools";\r
+       \r
+       $p->onload = <<<JS\r
+function(){\r
+       $('#kiwi .plugins ul').append('<li><a id="dev_tools_open">Dev Tools</a></li>');\r
+       $('#dev_tools_open').click(function(){ alert("Opening Dev Tools window"); });\r
+}\r
+JS;\r
+\r
+       $p->onunload = <<<JS\r
+function(){ alert('unloading dev tools'); $('#dev_tools_open').remove(); }\r
+JS;\r
+\r
+       echo $p->export();
\ No newline at end of file
diff --git a/plugins/plugin.php b/plugins/plugin.php
new file mode 100644 (file)
index 0000000..0e8062c
--- /dev/null
@@ -0,0 +1,18 @@
+<?php\r
+       \r
+       class Plugin {\r
+               var $name = "Kiwi Plugin";\r
+               var $onload = "function(){}";\r
+               var $onunload = "function(){}";\r
+               var $onprivmsg = "function(inp, tabview){}";\r
+               \r
+               public function export(){\r
+                       //header("Content-Type: application/json");\r
+                       return json_encode(array(\r
+                               'name'=>$this->name,\r
+                               'onprivmsg'=>$this->onprivmsg,\r
+                               'onload'=>$this->onload,\r
+                               'onunload'=>$this->onunload\r
+                       ));\r
+               }\r
+       }
\ No newline at end of file
diff --git a/poll.php b/poll.php
new file mode 100644 (file)
index 0000000..91a6a5f
--- /dev/null
+++ b/poll.php
@@ -0,0 +1,110 @@
+<?php\r
+       \r
+       require(dirname(__FILE__).'/config.php');\r
+       require(dirname(__FILE__).'/common.php');\r
+       require(dirname(__FILE__).'/class_session.php');\r
+       \r
+       $starttime = time();\r
+       \r
+       ############################################\r
+       ## New sessions\r
+       ############################################\r
+       \r
+       // If no session has been specified, start a new one\r
+       if(empty($_POST)){\r
+               $new_session = SESSIONS::create($_SERVER['REMOTE_ADDR']);\r
+               \r
+               // Session creation fail?\r
+               if($new_session === false){\r
+                       die(gen_response(array('error'=>'session_error')));\r
+               }\r
+               \r
+               // Session create OK, yay\r
+               die(gen_response(array('session_id'=>$new_session)));\r
+       }\r
+       \r
+       \r
+       \r
+       ############################################\r
+       ## Existing sessions\r
+       ############################################\r
+       \r
+       // Quit here if a session hasn't been specified\r
+       if(!isset($_POST['sid']) || empty($_POST['sid'])){\r
+               die(gen_response(array('error'=>'session_not_set')));\r
+       }\r
+       \r
+       $session_id = $_POST['sid'];\r
+       \r
+       // Make sure the session exists\r
+       if(!SESSIONS::exists($session_id)){\r
+               die(gen_response(array('error'=>'no_session')));\r
+       }\r
+       \r
+       // Connect to the IRC session\r
+       $ses = SESSIONS::open($session_id);\r
+       if(!$ses){\r
+               die(gen_response(array('error'=>'session_error')));\r
+       }\r
+       \r
+       if(!isset($_POST['data'])){\r
+               // Read any commands to be sent to the web client\r
+               $data = array();\r
+               \r
+               /* This was used for blocking the first call which caused a wait of timeout seconds if the user quits\r
+               // Make this read block\r
+               $tmp = json_decode(trim(SESSIONS::read($ses, true)),1);\r
+               if($tmp != null) $data[] = $tmp;\r
+               */\r
+               \r
+               // Unblocked reads just incase only 1 message is actually available\r
+               $start_time = time();\r
+               while(time() - $start_time < $config['max_time_per_poll'] && count($data) == 0 && !connection_aborted()){\r
+                       for($i=0; $i<$config['messages_per_poll']; $i++){\r
+                               if(connection_aborted()){\r
+                                       deb("Connection aborted");\r
+                                       break;\r
+                               }\r
+                               deb("Polling..");\r
+                               $tmp = json_decode(trim(SESSIONS::read($ses, false)),1);\r
+                               if($tmp != null){\r
+                                       $data[] = $tmp;\r
+                               } else {\r
+                                       break;\r
+                               }\r
+                               \r
+                               echo " ";\r
+                               flush();\r
+                       }\r
+                       \r
+                       if(count($data) == 0) sleep(1);\r
+               }\r
+               deb("Polled");\r
+               \r
+               if(!empty($data)){\r
+                       echo gen_response($data);\r
+               } else {\r
+                       echo gen_response(array());\r
+               }\r
+       } else {\r
+               fwrite($ses, $_POST['data']);\r
+       }\r
+       \r
+       // We're done here, close the session connection\r
+       SESSIONS::close($ses);\r
+       \r
+       \r
+       \r
+       \r
+       \r
+       \r
+       \r
+       \r
+       \r
+       ############################################\r
+       ## Functions\r
+       ############################################\r
+       \r
+       function gen_response($data){\r
+               return json_encode($data);\r
+       }\r
diff --git a/test.html b/test.html
new file mode 100644 (file)
index 0000000..9a55f7a
--- /dev/null
+++ b/test.html
@@ -0,0 +1,25 @@
+<html>\r
+\r
+<head>\r
+<script type="text/javascript">\r
+\r
+       var init_data = {};\r
+\r
+\r
+               //#nick@irc.anonnet.org:6679+/#channel,##channel,&channel\r
+               var con_string = 'nick@irc.anonnet.org:6679+/#channel,##channel,&channel';\r
+               var chans = con_string.match(/[#&]+[^ ,\007]+/g);\r
+               var addr = con_string.match(/[A-Za-z0-9.-]+(:\d+\+?)?/);\r
+\r
+               var parts = con_string.match(/^(([A-Za-z0-9-_\[\]<>]+)@)?([A-Za-z0-9.-]+(:\d+\+?)?)([#&]+[^ ,\007]+)/);\r
+               //alert(parts);\r
+               \r
+               alert('nick="'+parts[2]+'"');\r
+               alert('server="'+parts[3]+'"');\r
+               alert('channels="'+parts[5]+'"');\r
+</script>\r
+\r
+</head>\r
+\r
+<body></body>\r
+</html>
\ No newline at end of file
diff --git a/ws.html b/ws.html
new file mode 100644 (file)
index 0000000..a4a38ad
--- /dev/null
+++ b/ws.html
@@ -0,0 +1,11 @@
+<script>\r
+alert(typeof WebSocket);\r
+/*\r
+var conn = new WebSocket("ws://wuthost.net:7777/~wok/miblol/sessionname");\r
+conn.onopen = function(evt) { alert("Conn opened"); }\r
+conn.onread = function(evt) { alert("Read: " + evt.data); }\r
+conn.onclose = function(evt) { alert("Conn closed"); }\r
+\r
+conn.send("Hello World")\r
+*/\r
+</script>
\ No newline at end of file
diff --git a/ws.php b/ws.php
new file mode 100644 (file)
index 0000000..315c137
--- /dev/null
+++ b/ws.php
@@ -0,0 +1,324 @@
+<?php\r
+       \r
+       /*\r
+        * This script takes a WebSocket connection from a web client, creates a\r
+        * new session and simply proxies the data between the 2 connections. No\r
+        * data processing is done here at all.\r
+        * \r
+        */\r
+       \r
+       \r
+       $msg = <<<MSG\r
+       \r
+       \r
+######################################################\r
+    Lol, Kiwi websocket support is starting.\r
+\r
+                    Support: support@kiwiirc.com\r
+######################################################\r
+\r
+MSG;\r
+       echo $msg;\r
+       \r
+       \r
+       $debug = true;\r
+       \r
+       // Stores the sockets in pairs, websocket=>ircsocket\r
+       $pairs = array();\r
+       \r
+       // Stores all sockets\r
+       $soks = array();\r
+       \r
+       // Handshaken sockets\r
+       $handshakes = array();\r
+\r
+       \r
+       \r
+       require(dirname(__FILE__).'/config.php');\r
+       require(dirname(__FILE__).'/common.php');\r
+       require(dirname(__FILE__).'/class_session.php');\r
+       \r
+       \r
+       error_reporting(E_ALL);\r
+       set_time_limit(0);\r
+       ob_implicit_flush();\r
+       \r
+       $server = WebSocket($config['websocket']['bind_addr'], $config['websocket']['bind_port']);\r
+       $soks[(int)$server] = $server;\r
+       $last_dump = 0;\r
+       while(1){\r
+               echo time()." - $last_dump (".(time() - $last_dump).")\n";\r
+               if(time() - $last_dump >= 3){\r
+                       $last_dump = time();\r
+                       echo "\nPairs: ";\r
+                       var_dump($pairs);\r
+                       \r
+                       echo "\nSoks: ";\r
+                       var_dump($soks);\r
+               }\r
+               \r
+               $changed = $soks;\r
+               stream_select($changed, $write=NULL, $except=NULL, NULL);\r
+               \r
+               foreach($changed as $socket){\r
+                       // New connection?\r
+                       if($socket == $server){\r
+                               $client = stream_socket_accept($server);\r
+                               if($client<0){\r
+                                       console("socket_accept() failed"); continue;\r
+                               } else {\r
+                                       connect($client);\r
+                               }\r
+                       } else {\r
+                       \r
+                               $buffer = fread($socket, 2048);\r
+                               if($buffer === false || $buffer == ''){\r
+                                       // Disconnected\r
+                                       disconnect($socket);\r
+                               } else {\r
+                                       //$buffer = substr($buffer, 0, strlen($buffer));\r
+                                       //console("INCOMING:\n".$buffer."########\n");\r
+                                       if(isset($handshakes[(int)$socket])){\r
+                                               // websocket upgrade\r
+                                               dohandshake($socket, $buffer);\r
+                                       } else {\r
+                                               // Data transfering..\r
+                                               transfer($socket, $buffer);\r
+                                       }\r
+                               }\r
+                               \r
+                       }\r
+               }\r
+       }\r
+       \r
+       \r
+       \r
+       \r
+       function WebSocket($address,$port){\r
+               //$master=socket_create(AF_INET, SOCK_STREAM, SOL_TCP)     or die("socket_create() failed");\r
+               $master = stream_socket_server("tcp://$address:$port")          or die("socket_create() failed");\r
+               //socket_set_option($master, SOL_SOCKET, SO_REUSEADDR, 1)  or die("socket_option() failed");\r
+               //socket_bind($master, $address, $port)                    or die("socket_bind() failed");\r
+               //socket_listen($master,20)                                or die("socket_listen() failed");\r
+               echo "Server Started : ".date('Y-m-d H:i:s')."\n";\r
+               echo "Master socket  : ".$master."\n";\r
+               echo "Listening on   : ".$address." port ".$port."\n\n";\r
+               return $master;\r
+       }\r
+       \r
+       \r
+       \r
+       \r
+       \r
+       function connect($socket){\r
+               global $soks, $pairs, $handshakes;\r
+\r
+               //$session_id = SESSIONS::create();\r
+               //if(!$session_id) return false;\r
+               \r
+               //$pairs[$socket]= SESSION::open($session_id);\r
+\r
+               $soks[(int)$socket] = $socket;\r
+               $handshakes[(int)$socket] = false;\r
+               //array_push($soks, $pairs[$socket]);\r
+               \r
+               console($socket." connection..");\r
+       }\r
+\r
+       function disconnect($sok){\r
+               global $soks, $pairs;\r
+               console("disconnected?\n");\r
+               \r
+               $pair = findPair($sok);\r
+               if($pair === false) return false;\r
+               foreach($pair as $websocket => $local_con){                     \r
+                       @fclose($soks[$websocket]);\r
+                       unset($soks[$websocket]);\r
+                       \r
+                       @fclose($soks[$local_con]);\r
+                       unset($soks[$local_con]);\r
+                       \r
+                       unset($pairs[$websocket]);\r
+               }\r
+               \r
+               console($sok." DISCONNECTED!");\r
+       }\r
+       \r
+       function transfer($sok, $buffer){\r
+               global $soks, $pairs;\r
+               console("Transfering data?\n");\r
+               \r
+               $pair = findPair($sok);\r
+               if($pair === false) return false;\r
+               \r
+               console("Transfering ".strlen($buffer)." bytes.. '".$buffer."'");\r
+               //$buffer = wrap($buffer);\r
+               foreach($pair as $websocket => $local_con){\r
+                       if($sok == $soks[$websocket]){\r
+                               // From websocket..\r
+                               fwrite($soks[$local_con], unwrap($buffer));\r
+                               break;\r
+                       } elseif($sok == $soks[$local_con]){\r
+                               // From irc client\r
+                               fwrite($soks[$websocket], chr(0).$buffer.chr(255));\r
+                               break;\r
+                       }\r
+               }\r
+       }\r
+       \r
+       function findPair($socket){\r
+               global $soks, $pairs;\r
+               console("Finding pair: ".(int)$socket."\n");\r
+               \r
+               // If it's a websocket, then this will find it..\r
+               if(isset($pairs[(int)$socket]))\r
+                       return array((int)$socket=>$pairs[(int)$socket]);\r
+               \r
+               // If it's an irc client socket, then we will find it when flipped..\r
+               $flipped = array_flip($pairs);\r
+               if(isset($flipped[(int)$socket]))\r
+                       return array($flipped[(int)$socket] => (int)$socket);\r
+               \r
+               return false;\r
+       }\r
+\r
+       function dohandshake($sok, $buffer){\r
+               global $handshakes, $soks, $pairs;\r
+               console("\nRequesting handshake...");\r
+               \r
+               console("Handshaking...");\r
+               /*\r
+               list($resource, $host, $origin) = getheaders($buffer);\r
+               $upgrade  = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .\r
+                                 "Upgrade: WebSocket\r\n" .\r
+                                 "Connection: Upgrade\r\n" .\r
+                                 "WebSocket-Origin: " . $origin . "\r\n" .\r
+                                 "WebSocket-Location: ws://" . $host . $resource . "\r\n" .\r
+                                 "\r\n";\r
+               */\r
+               if(!strpos($buffer, 'WebSocket-Key1:')){\r
+                       $upgrade = (string)new WebSocket75($buffer);\r
+               } else {\r
+                       $upgrade = (string)new WebSocket76($buffer);\r
+               }\r
+               \r
+               fwrite($sok, $upgrade.chr(0));\r
+               \r
+               // Done the handshake so remove it from the handshaking array\r
+               unset($handshakes[(int)$sok]);\r
+               \r
+               console("Done handshaking...");\r
+               \r
+               //socket_getsockname($sok, $sok_name);\r
+               $sok_name = stream_socket_get_name($sok, true);\r
+               $session_id = SESSIONS::create($sok_name);\r
+               if(!$session_id) return false;\r
+               $irc_client_sok = SESSIONS::open($session_id);\r
+               \r
+               $soks[(int)$irc_client_sok] = $irc_client_sok;\r
+               $pairs[(int)$sok] = (int)$irc_client_sok;\r
+               \r
+               fwrite($irc_client_sok, json_encode(array('method'=>'read')));\r
+               \r
+               console($sok." CONNECTED!");\r
+               return true;\r
+       }\r
+       \r
+       \r
+       \r
+       \r
+       \r
+       class WebSocket75 {\r
+               private $__value__;\r
+               \r
+               public function __toString() {\r
+                       return $this->__value__;\r
+               }\r
+               \r
+               public function __construct($buffer){\r
+                       list($resource, $host, $origin) = $this->getheaders($buffer);\r
+                       $upgrade  = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .\r
+                                 "Upgrade: WebSocket\r\n" .\r
+                                 "Connection: Upgrade\r\n" .\r
+                                 "WebSocket-Origin: " . $origin . "\r\n" .\r
+                                 "WebSocket-Location: ws://" . $host . $resource . "\r\n" .\r
+                                 "\r\n";\r
+                                 \r
+                       $this->__value__ = $upgrade;\r
+               }\r
+               \r
+               private function getheaders($req){\r
+                       $r=$h=$o=null;\r
+                       if(preg_match("/GET (.*) HTTP/"   ,$req,$match)){ $r=$match[1]; }\r
+                       if(preg_match("/Host: (.*)\r\n/"  ,$req,$match)){ $h=$match[1]; }\r
+                       if(preg_match("/Origin: (.*)\r\n/",$req,$match)){ $o=$match[1]; }\r
+                       return array($r,$h,$o);\r
+               }\r
+       }\r
+       \r
+       \r
+       class WebSocket76 {\r
+\r
+               /*! Easy way to handshake a WebSocket via draft-ietf-hybi-thewebsocketprotocol-00\r
+                * @link    http://www.ietf.org/id/draft-ietf-hybi-thewebsocketprotocol-00.txt\r
+                * @author  Andrea Giammarchi\r
+                * @blog    webreflection.blogspot.com\r
+                * @date    4th June 2010\r
+                * @example\r
+                *          // via function call ...\r
+                *          $handshake = WebSocketHandshake($buffer);\r
+                *          // ... or via class\r
+                *          $handshake = (string)new WebSocketHandshake($buffer);\r
+                *\r
+                *          socket_write($socket, $handshake, strlen($handshake));\r
+                */\r
+\r
+               private $__value__;\r
+\r
+               public function __construct($buffer) {\r
+                       $resource = $host = $origin = $key1 = $key2 = $protocol = $code = $handshake = null;\r
+                       preg_match('#GET (.*?) HTTP#', $buffer, $match) && $resource = $match[1];\r
+                       preg_match("#Host: (.*?)\r\n#", $buffer, $match) && $host = $match[1];\r
+                       preg_match("#Sec-WebSocket-Key1: (.*?)\r\n#", $buffer, $match) && $key1 = $match[1];\r
+                       preg_match("#Sec-WebSocket-Key2: (.*?)\r\n#", $buffer, $match) && $key2 = $match[1];\r
+                       preg_match("#Sec-WebSocket-Protocol: (.*?)\r\n#", $buffer, $match) && $protocol = $match[1];\r
+                       preg_match("#Origin: (.*?)\r\n#", $buffer, $match) && $origin = $match[1];\r
+                       preg_match("#\r\n(.*?)\$#", $buffer, $match) && $code = $match[1];\r
+                       $this->__value__ =\r
+                               "HTTP/1.1 101 WebSocket Protocol Handshake\r\n".\r
+                               "Upgrade: WebSocket\r\n".\r
+                               "Connection: Upgrade\r\n".\r
+                               "Sec-WebSocket-Origin: {$origin}\r\n".\r
+                               "Sec-WebSocket-Location: ws://{$host}{$resource}\r\n".\r
+                               ($protocol ? "Sec-WebSocket-Protocol: {$protocol}\r\n" : "").\r
+                               "\r\n".\r
+                               $this->_createHandshakeThingy($key1, $key2, $code)\r
+                       ;\r
+               }\r
+\r
+               public function __toString() {\r
+                       return $this->__value__;\r
+               }\r
+               \r
+               private function _doStuffToObtainAnInt32($key) {\r
+                       return preg_match_all('#[0-9]#', $key, $number) && preg_match_all('# #', $key, $space) ?\r
+                               implode('', $number[0]) / count($space[0]) :\r
+                               ''\r
+                       ;\r
+               }\r
+\r
+               private function _createHandshakeThingy($key1, $key2, $code) {\r
+                       return md5(\r
+                               pack('N', $this->_doStuffToObtainAnInt32($key1)).\r
+                               pack('N', $this->_doStuffToObtainAnInt32($key2)).\r
+                               $code,\r
+                               true\r
+                       );\r
+               }\r
+       }\r
+       \r
+       \r
+function     say($msg=""){ echo $msg."\n"; }\r
+function    wrap($msg=""){ return chr(0).$msg.chr(255); }\r
+function  unwrap($msg=""){ return substr($msg,1,strlen($msg)-2); }\r
+function console($msg=""){ global $debug; if($debug){ echo time().' '.trim($msg)."\n"; } }\r