Source

Vesperia / server.coffee

###
 Vesperia: A Tethealla management port proxy and api server

 Copyright 2012 Pol Cámara
 Released under the MIT license
 Check LICENSE.MIT for more details.
###

Security = require "./model/security"
crypto = require "crypto"
querystring = require 'querystring'
http = require 'http'
settings = require './settings'
ent = require 'ent'

#The global var is used to jump from a socket to the server.
gServer = false


class Server
  chatbox_req = false
  clients:  {}
  socket: false
  security: false

  constructor: (@net, @port, @host, @connection, @client) ->
    gServer = this
    this.security = new Security connection
    this.client.events.on 'message', (message) -> gServer._parseTethPacket(message)

      
  ###
  Initializes the server
  Example usage:
    serverInstance.initialize()
  ###
  initialize: ->
    server = this.net.createServer this._startSocket
    server.listen this.port, this.host

  _startSocket: (socket) -> 
    socket.authenticated = false
    socket.uid = false
    socket.write "100"
    socket.on 'data', (data) -> gServer._onData this,data.toString().replace("\n","")

  _onData: (socket, data) ->
    this._parsePacket socket, data if this._authenticate socket, data

  _authenticate: (socket, data) ->
    hashed = false
    return true if socket.authenticated == true
    if data.length > 3
      command = data.substring 0, 3
      if command == "110" || command == "111"
        hashed = true if command == "111"
        password = data.substring 3, data.length
        password = password.trim()
        this.security.valKey password, hashed, (keyExists) ->
          if keyExists
            socket.authenticated = true
            socket.uid =  crypto.createHash("md5").update((new Date().getTime()+Math.floor(Math.random()*1000)).toString()).digest("hex")
            socket.write "102"
            gServer.clients[socket.uid] = socket
          else
            gServer._disconnectClient socket, "101"
      else
        gServer._disconnectClient socket, "101"
    return false
 
  _disconnectClient: (socket,code) ->
    delete gServer.clients[socket.uid] if socket.uid
    socket.write(code)
    socket.end()

  _parsePacket: (socket,data) ->
    command = data.substring 0, 3
    details = data.substring 3, data.length
    details = details.trim()
    switch command
      when "210" then gServer._sendToTethealla(socket,details)

  _parseTethPacket: (data) ->
    if data.length >= 3
      command = data[0..2].toString()
      switch command
        when "225"
          lobby = data[3..4].toString().trim();
          gc = data[5..13].toString().trim();
          char= data[17..36].toString('utf16le').trim();
          message = data[37..].toString('utf16le').trim();
          console.log "Message recieved: Sending it to the chatbox"
          this._sendToChatbox char, message, gc, lobby

        when  "220", "221"
          dataString = data.toString();
          if data.length > 35
            uid = data.substring 3, 35
            message = data.substring 35, data.length
            if typeof gServer.clients[uid] != "undefined"
              gServer.clients[uid].write message

    ###
      command = data.substring 0, 3
      if command == "220" || command == "221"

      else
        console.log "Doing stuff with the chat message"
    ###
  _sendToTethealla: (socket,action) ->    
    if gServer.client
      if gServer.client.connected
        gServer.client.stream.write "200"+socket.uid+action
      else
        gServer.client.reconnect()        
        socket.write "202"

  _sendToChatbox: (char, message, gc, lobby) ->
    postData = querystring.stringify
      'username' : ent.encode char
      'message' : ent.encode message
      'gc' : ent.encode gc
      'lobby': ent.encode lobby
      'secret': settings.chatboxSecret
      'submit': 'true'
      'json': 'true'
    postOptions =
      host: settings.chatboxHost
      port: settings.chatboxPort
      path: settings.chatboxPath
      method: 'POST'
      headers:
        'Content-Type': 'application/x-www-form-urlencoded'
        'Content-Length': postData.length
    chatboxReq = http.request postOptions, (res) ->
      res.setEncoding('utf8')
      res.on 'data', (chunk) ->
        console.log('Response: ' + chunk)
    chatboxReq.write(postData)
    chatboxReq.end()

module.exports.Server = Server