summaryrefslogtreecommitdiff
path: root/jabber-ahc.el
diff options
context:
space:
mode:
Diffstat (limited to 'jabber-ahc.el')
-rw-r--r--jabber-ahc.el231
1 files changed, 231 insertions, 0 deletions
diff --git a/jabber-ahc.el b/jabber-ahc.el
new file mode 100644
index 0000000..605c0f8
--- /dev/null
+++ b/jabber-ahc.el
@@ -0,0 +1,231 @@
+;; jabber-ahc.el - Ad-Hoc Commands by JEP-0050
+
+;; Copyright (C) 2003, 2004, 2007, 2008 - Magnus Henoch - mange@freemail.hu
+;; Copyright (C) 2002, 2003, 2004 - tom berger - object@intelectronica.net
+
+;; This file is a part of jabber.el.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program; if not, write to the Free Software
+;; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+(require 'jabber-disco)
+(require 'jabber-widget)
+
+(defvar jabber-ahc-sessionid nil
+ "session id of Ad-Hoc Command session")
+
+(defvar jabber-ahc-node nil
+ "node to send commands to")
+
+(defvar jabber-ahc-commands nil
+ "Commands provided
+
+This is an alist, where the keys are node names as strings (which
+means that they must not conflict). The values are plists having
+following properties:
+
+acl - function taking connection object and JID of requester,
+ returning non-nil for access allowed. No function means
+ open for everyone.
+name - name of command
+func - function taking connection object and entire IQ stanza as
+ arguments and returning a <command/> node
+
+Use the function `jabber-ahc-add' to add a command to this list.")
+
+
+;;; SERVER
+(add-to-list 'jabber-disco-info-nodes
+ (list "http://jabber.org/protocol/commands"
+ '((identity ((category . "automation")
+ (type . "command-list")
+ (name . "Ad-Hoc Command list")))
+ (feature ((var . "http://jabber.org/protocol/commands")))
+ (feature ((var . "http://jabber.org/protocol/disco#items")))
+ (feature
+ ((var . "http://jabber.org/protocol/disco#info"))))))
+
+(defun jabber-ahc-add (node name func acl)
+ "Add a command to internal lists.
+NODE is the node name to be used. It must be unique.
+NAME is the natural-language name of the command.
+FUNC is a function taking the entire IQ stanza as single argument when
+this command is invoked, and returns a <command/> node.
+ACL is a function taking JID as single argument, returning non-nil for
+access allowed. nil means open for everyone."
+ (add-to-list 'jabber-ahc-commands (cons node (list 'name name
+ 'func func
+ 'acl acl)))
+ (add-to-list 'jabber-disco-info-nodes
+ (list node `((identity ((category . "automation")
+ (type . "command-node")
+ (name . ,name)))
+ (feature ((var . "http://jabber.org/protocol/commands")))
+ (feature ((var . "http://jabber.org/protocol/disco#info")))
+ (feature ((var . "jabber:x:data")))))))
+
+(jabber-disco-advertise-feature "http://jabber.org/protocol/commands")
+(add-to-list 'jabber-disco-items-nodes
+ (list "http://jabber.org/protocol/commands" #'jabber-ahc-disco-items nil))
+(defun jabber-ahc-disco-items (jc xml-data)
+ "Return commands in response to disco#items request"
+ (let ((jid (jabber-xml-get-attribute xml-data 'from)))
+ (mapcar (function
+ (lambda (command)
+ (let ((node (car command))
+ (plist (cdr command)))
+ (let ((acl (plist-get plist 'acl))
+ (name (plist-get plist 'name))
+ (func (plist-get plist 'func)))
+ (when (or (not (functionp acl))
+ (funcall acl jc jid))
+ `(item ((name . ,name)
+ (jid . ,(jabber-connection-jid jc))
+ (node . ,node))))))))
+ jabber-ahc-commands)))
+
+(add-to-list 'jabber-iq-set-xmlns-alist
+ (cons "http://jabber.org/protocol/commands" 'jabber-ahc-process))
+(defun jabber-ahc-process (jc xml-data)
+
+ (let ((to (jabber-xml-get-attribute xml-data 'from))
+ (id (jabber-xml-get-attribute xml-data 'id))
+ (node (jabber-xml-get-attribute (jabber-iq-query xml-data) 'node)))
+ ;; find command
+ (let* ((plist (cdr (assoc node jabber-ahc-commands)))
+ (acl (plist-get plist 'acl))
+ (func (plist-get plist 'func)))
+ (if plist
+ ;; found
+ (if (or (not (functionp acl))
+ (funcall acl jc to))
+ ;; access control passed
+ (jabber-send-iq jc to "result"
+ (funcall func jc xml-data)
+ nil nil nil nil id)
+ ;; ...or failed
+ (jabber-signal-error "cancel" 'not-allowed))
+ ;; No such node
+ (jabber-signal-error "cancel" 'item-not-found)))))
+
+;;; CLIENT
+(add-to-list 'jabber-jid-service-menu
+ (cons "Request command list" 'jabber-ahc-get-list))
+(defun jabber-ahc-get-list (jc to)
+ "Request list of ad-hoc commands. (JEP-0050)"
+ (interactive (list (jabber-read-account)
+ (jabber-read-jid-completing "Request command list from: " nil nil nil 'full t)))
+ (jabber-get-disco-items jc to "http://jabber.org/protocol/commands"))
+
+(add-to-list 'jabber-jid-service-menu
+ (cons "Execute command" 'jabber-ahc-execute-command))
+(defun jabber-ahc-execute-command (jc to node)
+ "Execute ad-hoc command. (JEP-0050)"
+ (interactive (list (jabber-read-account)
+ (jabber-read-jid-completing "Execute command of: " nil nil nil 'full t)
+ (jabber-read-node "Node of command: ")))
+ (jabber-send-iq jc to
+ "set"
+ `(command ((xmlns . "http://jabber.org/protocol/commands")
+ (node . ,node)
+ (action . "execute")))
+ #'jabber-process-data #'jabber-ahc-display
+ #'jabber-process-data "Command execution failed"))
+
+(defun jabber-ahc-display (jc xml-data)
+ (let* ((from (jabber-xml-get-attribute xml-data 'from))
+ (query (jabber-iq-query xml-data))
+ (node (jabber-xml-get-attribute query 'node))
+ (notes (jabber-xml-get-children query 'note))
+ (sessionid (jabber-xml-get-attribute query 'sessionid))
+ (status (jabber-xml-get-attribute query 'status))
+ (actions (car (jabber-xml-get-children query 'actions)))
+ xdata
+ (inhibit-read-only t))
+
+ (make-local-variable 'jabber-ahc-sessionid)
+ (setq jabber-ahc-sessionid sessionid)
+ (make-local-variable 'jabber-ahc-node)
+ (setq jabber-ahc-node node)
+ (make-local-variable 'jabber-buffer-connection)
+ (setq jabber-buffer-connection jc)
+
+ (dolist (x (jabber-xml-get-children query 'x))
+ (when (string= (jabber-xml-get-attribute x 'xmlns) "jabber:x:data")
+ (setq xdata x)))
+
+ (cond
+ ((string= status "executing")
+ (insert "Executing command\n\n"))
+ ((string= status "completed")
+ (insert "Command completed\n\n"))
+ ((string= status "canceled")
+ (insert "Command canceled\n\n")))
+
+ (dolist (note notes)
+ (let ((note-type (jabber-xml-get-attribute note 'type)))
+ (cond
+ ((string= note-type "warn")
+ (insert "Warning: "))
+ ((string= note-type "error")
+ (insert "Error: ")))
+ (insert (car (jabber-xml-node-children note)) "\n")))
+ (insert "\n")
+
+ (when xdata
+ (jabber-init-widget-buffer from)
+
+ (let ((formtype (jabber-xml-get-attribute xdata 'type)))
+ (if (string= formtype "result")
+ (jabber-render-xdata-search-results xdata)
+ (jabber-render-xdata-form xdata)
+
+ (when (string= status "executing")
+ (let ((button-titles
+ (cond
+ ((null actions)
+ '(complete cancel))
+ (t
+ (let ((children (mapcar #'jabber-xml-node-name (jabber-xml-node-children actions)))
+ (default-action (jabber-xml-get-attribute actions 'execute)))
+ (if (or (null default-action) (memq (intern default-action) children))
+ children
+ (cons (intern default-action) children)))))))
+ (dolist (button-title button-titles)
+ (widget-create 'push-button :notify `(lambda (&rest ignore) (jabber-ahc-submit (quote ,button-title))) (symbol-name button-title))
+ (widget-insert "\t")))
+ (widget-insert "\n"))))
+
+ (widget-setup)
+ (widget-minor-mode 1))))
+
+(defun jabber-ahc-submit (action)
+ "Submit Ad-Hoc Command."
+
+ (jabber-send-iq jabber-buffer-connection jabber-submit-to
+ "set"
+ `(command ((xmlns . "http://jabber.org/protocol/commands")
+ (sessionid . ,jabber-ahc-sessionid)
+ (node . ,jabber-ahc-node)
+ (action . ,(symbol-name action)))
+ ,(if (and (not (eq action 'cancel))
+ (eq jabber-form-type 'xdata))
+ (jabber-parse-xdata-form)))
+
+ #'jabber-process-data #'jabber-ahc-display
+ #'jabber-process-data "Command execution failed"))
+
+(provide 'jabber-ahc)
+
+;;; arch-tag: c0d5ed8c-50cb-44e1-8e0f-4058b79ee353