Permalink
Browse files

added jabber rpc support

  • Loading branch information...
1 parent 9ffff53 commit 531aef2f5674e5b72f8574be5ef62c9643add0f4 @flosse committed Sep 14, 2012
Showing with 204 additions and 39 deletions.
  1. +0 −1 README.markdown
  2. +5 −0 lib/JOAPObjects.coffee
  3. +80 −17 lib/Manager.coffee
  4. +4 −2 lib/Parser.coffee
  5. +4 −2 lib/Router.coffee
  6. +5 −3 lib/Serializer.coffee
  7. +27 −13 lib/stanza.coffee
  8. +79 −1 spec/Manager.spec.coffee
View
@@ -148,7 +148,6 @@ cake test
## ToDo's
- describe support
-- Jabber RPC support
## License
@@ -1,3 +1,8 @@
+###
+This program is distributed under the terms of the MIT license.
+Copyright 2012 (c) Markus Kohlhase <mail@markus-kohlhase.de>
+###
+
JID = require("node-xmpp").JID
getDefaultOpts = ->
View
@@ -12,49 +12,53 @@ class Manager extends events.EventEmitter
constructor: (@xmpp) ->
- @classes = {}
- @serverDescription={'en-US':"JOAP Server"}
- @serverAttributes = {}
- @serverMethods = {}
+ @serverDescription = {'en-US':"JOAP Server"}
+ @serverAttributes = {}
+ @serverMethods = {}
+ @classes = {}
+ @objects = {}
@router = new joap.Router @xmpp
@router.on "describe", @onDescribe
@router.on "add", @onAdd
@router.on "read", @onRead
@router.on "edit", @onEdit
@router.on "delete", @onDelete
@router.on "search", @onSearch
- @objects = {}
+ @router.on "rpc", @onRPC
@getArgNames: (fn) ->
args = fn.toString().match(/function\b[^(]*\(([^)]*)\)/)[1]
args.split /\s*,\s*/
- # override if you want to manipule the request
+ # override if you want to manipulate the request
beforeAdd: (a, next) -> next null, a
- # override if you want to manipule the reading of instances
+ # override if you want to manipulate the reading of instances
beforeRead: (a, next) -> next null, a
- # override if you want to manipule the deletion of instances
+ # override if you want to manipulate the deletion of instances
beforeDelete: (a, next) -> next null, a
- # override if you want to manipule the description of instances
+ # override if you want to manipulate the description of instances
beforeDescribe: (a, next) -> next null, a
- # override if you want to manipule the search of instances
+ # override if you want to manipulate the search of instances
beforeSearch: (a, next) -> next null, a
- # override if you want to manipule the editing of instances
+ # override if you want to manipulate the editing of instances
beforeEdit: (a, next) -> next null, a
+ # override if you want to manipulate the rpc request
+ beforeRPC: (a, next) -> next null, a
+
# override if you want to use a database
saveInstance: (a, obj, next) =>
@objects[a.class][obj.id] = obj
next null, a
# override if you want to use a database
loadInstance: (a, next) =>
- inst = @objects[a.class][a.instance]
+ inst = @objects[a.class]?[a.instance]
if not inst?
err = new joap.Error "Object '#{a.instance}' does not exists", 404
next err, a, inst
@@ -80,19 +84,24 @@ class Manager extends events.EventEmitter
addClass: (name, creator, opts={}) ->
{ required, objects } = opts
prot = opts.protected
- objects ?={}
+ objects ?= {}
clazz = new joap.object.Class "#{name}@#{@xmpp.jid.toString()}",
creator: creator
required: required
protected: prot
+ for k,v of clazz.prototype when typeof v is "function" and not (k in prot)
+ prot.push k
+
if typeof creator is "function" and not @classes[name]?
@classes[name] = clazz
@objects[name] = objects
true
else false
+ addServerMethod: (name, fn) -> @serverMethods[name] ?= fn
+
createInstance: (a, next) =>
clazz = @classes[a.class]
argNames = Manager.getArgNames clazz.creator
@@ -185,6 +194,7 @@ class Manager extends events.EventEmitter
(next) -> next null, a
@grant
@beforeDescribe
+ @checkTarget
@createDescription
@sendResponse
], (err) => @sendError err, a
@@ -204,6 +214,56 @@ class Manager extends events.EventEmitter
@sendResponse
], (err) => @sendError err, a
+ onRPC: (a) =>
+ async.waterfall [
+ (next) -> next null, a
+ @grant
+ @beforeRPC
+ @checkTarget
+ @execRPC
+ @sendResponse
+ ], (err) => @sendError err, a
+
+ checkTarget: (a, next) =>
+ err = null
+ if a.instance
+ a.target = "instance"
+ err ?= @isInstanceAddress a
+ else if a.class and not a.instance
+ a.target = "class"
+ err ?= @isClassAddress a
+ else
+ a.target = "server"
+ if a.class or a.instance
+ err ?= @classExists a
+ next err, a
+
+ execRPC: (a, next) =>
+ switch a.target
+ when "server"
+ method = @serverMethods[a.method]
+ if typeof method isnt "function"
+ err = new joap.Error "Server method '#{a.method}' does not exist"
+ else
+ data = method.apply null, a.params
+ next err, a, data
+ when "class"
+ clazz = @classes[a.class].creator
+ if typeof clazz?[a.method] isnt "function"
+ err = new joap.Error "Class method '#{a.method}' does not exist"
+ else
+ data = clazz[a.method].apply clazz, a.params
+ next err, a, data
+ when "instance"
+ @loadInstance a, (err, a, inst) =>
+ if err? or typeof inst?[a.method] isnt "function"
+ err ?= new joap.Error "Instance method '#{a.method}' does not exist"
+ else
+ data = inst[a.method].apply inst, a.params
+ next err, a, data
+ else
+ next (new Error), a
+
createDescription: (a, next) =>
data = null
if not a.class?
@@ -232,7 +292,8 @@ class Manager extends events.EventEmitter
classExists: (a, next) =>
if not @classes[a.class]?
err = new joap.Error "Class '#{a.class}' does not exists", 404
- next err, a
+ next? err, a
+ err
isValidSearch: (a, next) =>
if a.attributes? and typeof a.attributes isnt "object" or a.attributes instanceof Array
@@ -242,12 +303,14 @@ class Manager extends events.EventEmitter
isClassAddress: (a, next) ->
if not a.class? or a.instance?
err = new joap.Error "'#{a.iq.attrs.to}' isn't a class", 405
- next err, a
+ next? err, a
+ err
isInstanceAddress: (a, next) ->
if not a.class? or not a.instance?
err = new joap.Error "'#{a.iq.attrs.to}' is not an instance", 405
- next err, a
+ next? err, a
+ err
areRequiredAttributes: (a, next) =>
for r in @classes[a.class].required
@@ -273,7 +336,7 @@ class Manager extends events.EventEmitter
next err, a
sendError: (err, a) =>
- if err.code then @router.sendError err, a
+ if err.code or a.type is "rpc" then @router.sendError err, a
else @sendInternalServerError err, a
sendResponse: (a, data) => @router.sendResponse a, data
View
@@ -1,5 +1,7 @@
-# This program is distributed under the terms of the MIT license.
-# Copyright 2012 (c) Markus Kohlhase <mail@markus-kohlhase.de>
+###
+This program is distributed under the terms of the MIT license.
+Copyright 2012 (c) Markus Kohlhase <mail@markus-kohlhase.de>
+###
ltx = require "ltx"
joap = require "./node-xmpp-joap"
View
@@ -1,5 +1,7 @@
-# This program is distributed under the terms of the MIT license.
-# Copyright 2012 (c) Markus Kohlhase <mail@markus-kohlhase.de>
+###
+This program is distributed under the terms of the MIT license.
+Copyright 2012 (c) Markus Kohlhase <mail@markus-kohlhase.de>
+###
ltx = require "ltx"
events = require "events"
@@ -1,5 +1,7 @@
-# This program is distributed under the terms of the MIT license.
-# Copyright 2012 (c) Markus Kohlhase <mail@markus-kohlhase.de>
+###
+This program is distributed under the terms of the MIT license.
+Copyright 2012 (c) Markus Kohlhase <mail@markus-kohlhase.de>
+###
ltx = require "ltx"
joap = require "./node-xmpp-joap"
@@ -52,7 +54,7 @@ class Serializer
el
else if action.type is "rpc"
- el = (new Element "query", {xmlns:RPC_NS})
+ new stanza.MethodResponse val
else if val?
View
@@ -1,5 +1,7 @@
-# This program is distributed under the terms of the MIT license.
-# Copyright 2012 (c) Markus Kohlhase <mail@markus-kohlhase.de>
+###
+This program is distributed under the terms of the MIT license.
+Copyright 2012 (c) Markus Kohlhase <mail@markus-kohlhase.de>
+###
ltx = require "ltx"
joap = require "./node-xmpp-joap"
@@ -68,20 +70,32 @@ class ErrorIq extends ltx.Element
else if type is "rpc"
@c("query", xmlns: RPC_NS)
- @c("methodResponse")
- @c("fault")
- @cnode(new Value { faultCode: code, faultString: msg })
+ .c("methodResponse")
+ .c("fault")
+ .cnode(new Value { faultCode: (code or 0), faultString: msg })
class Error extends ltx.Element
constructor: (code, msg) ->
super "error", {code:code}
@t msg if msg?
-exports.Attribute = Attribute
-exports.AttributeDescription = AttributeDescription
-exports.Description = Description
-exports.Member = Member
-exports.Struct = Struct
-exports.Array = Array
-exports.Value = Value
-exports.ErrorIq = ErrorIq
+class MethodResponse extends ltx.Element
+ constructor: (val) ->
+ super "query", xmlns: RPC_NS
+ # success but no return value => return true
+ val = true if not val?
+ @c("methodResponse")
+ .c("params")
+ .c("param")
+ .cnode(new Value val)
+
+module.exports =
+ Attribute: Attribute
+ AttributeDescription: AttributeDescription
+ Description: Description
+ Member: Member
+ Struct: Struct
+ Array: Array
+ Value: Value
+ ErrorIq: ErrorIq
+ MethodResponse: MethodResponse
Oops, something went wrong.

0 comments on commit 531aef2

Please sign in to comment.