diff --git a/gomuks-structs.el b/gomuks-structs.el index c2eb564..87a2636 100644 --- a/gomuks-structs.el +++ b/gomuks-structs.el @@ -41,6 +41,8 @@ (cl-defstruct gomuks-session user token transaction-id + websocket + ping-timer ;; Hash table of available rooms rooms) diff --git a/gomuks.el b/gomuks.el index 36c0954..6e4fb6a 100644 --- a/gomuks.el +++ b/gomuks.el @@ -36,14 +36,12 @@ (defvar client-state (make-gomuks-state :rooms '() :spaces '())) +(defvar gomuks-current-session nil) + (defvar gomuks-server-buffer "*gomuks-server*") (defvar gomuks-socket-buffer "*gomuks-socket-frame*") (defvar gomuks-server-name "*gomuks-server*") -(defvar gomuks-auth-cookie nil) -(defvar gomuks-websocket nil) -(defvar gomuks-ping-timer nil) - (defcustom gomuks-server-binary "~/tmp/gomuks" "The Gomuks backend binary." :type 'string @@ -69,61 +67,61 @@ (notifications-notify :title "Gomuks Server Closed" :app-name "gomuks.el")) (defvar gomuks-auth-endpoint (concat "http://" gomuks-base-url "/_gomuks/auth")) -(defvar gomuks-auth-cookie) (defvar gomuks-ws-endpoint (concat "ws://" gomuks-base-url "/_gomuks/websocket")) (defun gomuks-connect (gomuks-username) "Opens a websocket connection with the specified gomuks endpoint" (interactive "sGomuks Username: \n") - (let ((gomuks-password (read-passwd "Gomuks Password: "))) - (setq gomuks-auth-cookie (let ((request--curl-cookie-jar (expand-file-name (make-temp-name "gomuks-cookie-") - temporary-file-directory))) - (request gomuks-auth-endpoint - :type "POST" - :headers `(("Authorization" . - ,(concat "Basic " - (base64-encode-string - (concat gomuks-username ":" gomuks-password)))))) - (sleep-for 0.5) - ;; TODO: this needs to select domain dynamically, instead of hardcoding a string here - (cdar (request--netscape-get-cookies request--curl-cookie-jar "localhost" "/_gomuks" t))))) - (setq gomuks-ping-timer (run-with-timer - 0 - 15 - (lambda () - (message "pinging") - (websocket-send-text gomuks-websocket - "{\"command\":\"ping\"}")))) - (setq gomuks-websocket (websocket-open gomuks-ws-endpoint - :custom-header-alist - `(("Cookie" . ,(concat "gomuks_auth=" gomuks-auth-cookie))) - :on-message (lambda (_ws frame) - (message "frame %s %s" (websocket-frame-completep frame) (websocket-frame-text - frame)) - (cond - ((not (websocket-frame-completep frame)) - (message "frame incomplete") - (with-current-buffer (get-buffer-create gomuks-socket-buffer) - (goto-char (point-max)) - (insert (websocket-frame-payload frame)))) - (t - (message "frame complete") - (let* ((combined-payload - (unwind-protect - (with-current-buffer (get-buffer-create gomuks-socket-buffer) - (goto-char (point-max)) - (insert (websocket-frame-payload frame)) - (message "%s" (buffer-string)) - (buffer-string)) - (kill-buffer (get-buffer-create gomuks-socket-buffer)))) - (msg (json-parse-string (decode-coding-string combined-payload 'utf-8) - :object-type 'alist - :array-type 'list - :null-object '()))) - (message "test %S" msg) - (cond ((equal (alist-get 'command msg) "sync_complete") - (gomuks-sync-state (alist-get 'data msg))))))))))) + (let ((gomuks-password (read-passwd "Gomuks Password: ")) + (gomuks-auth-cookie (let ((request--curl-cookie-jar (expand-file-name (make-temp-name "gomuks-cookie-") + temporary-file-directory))) + (request gomuks-auth-endpoint + :type "POST" + :headers `(("Authorization" . + ,(concat "Basic " + (base64-encode-string + (concat gomuks-username ":" gomuks-password)))))) + (sleep-for 0.5) + ;; TODO: this needs to select domain dynamically, instead of hardcoding a string here + (cdar (request--netscape-get-cookies request--curl-cookie-jar "localhost" "/_gomuks" t))))) + (setq gomuks-current-session + (make-gomuks-session :token gomuks-auth-cookie :transaction-id 0 + :ping-timer (run-with-timer + 0 + 15 + (lambda () + (gomuks-send-message "ping"))) + :websocket (websocket-open + gomuks-ws-endpoint + :custom-header-alist + `(("Cookie" . ,(concat "gomuks_auth=" gomuks-auth-cookie))) + :on-message (lambda (_ws frame) + (message "frame %s %s" (websocket-frame-completep frame) (websocket-frame-text + frame)) + (cond + ((not (websocket-frame-completep frame)) + (message "frame incomplete") + (with-current-buffer (get-buffer-create gomuks-socket-buffer) + (goto-char (point-max)) + (insert (websocket-frame-payload frame)))) + (t + (message "frame complete") + (let* ((combined-payload + (unwind-protect + (with-current-buffer (get-buffer-create gomuks-socket-buffer) + (goto-char (point-max)) + (insert (websocket-frame-payload frame)) + (message "%s" (buffer-string)) + (buffer-string)) + (kill-buffer (get-buffer-create gomuks-socket-buffer)))) + (msg (json-parse-string (decode-coding-string combined-payload 'utf-8) + :object-type 'alist + :array-type 'list + :null-object '()))) + (message "test %S" msg) + (cond ((equal (alist-get 'command msg) "sync_complete") + (gomuks-sync-state (alist-get 'data msg))))))))))))) (defun gomuks-disconnect () "Disconnect the gomuks websocket." @@ -131,6 +129,20 @@ (cancel-timer gomuks-ping-timer) (websocket-close gomuks-websocket)) +(defun gomuks-send-message (command &optional data) + "Send a message down the gomuks websocket, automatically incrementing the session transaction ID. +`command' is the command string to send, and `data' is an alist that is converted to JSON as required by the command." + (setf (gomuks-session-transaction-id gomuks-current-session) (+ 1 (gomuks-session-transaction-id gomuks-current-session))) + (websocket-send-text + (gomuks-session-websocket gomuks-current-session) + (json-serialize + (let ((command-header `((command . ,command) + (request_id . ,(gomuks-session-transaction-id gomuks-current-session))))) + (if data + (append command-header + `((data . ,data))) + command-header))))) + (defun gomuks-process-initial-events (events) (mapcar (lambda (event)