edit_server.el 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. ;;
  2. ;; Emacs edit-server
  3. ;;
  4. ;; This provides an edit server to respond to requests from the Chrome
  5. ;; Emacs Chrome plugin. This is my first attempt at doing something
  6. ;; with sockets in Emacs. I based it on the following examples:
  7. ;;
  8. ;; http://www.emacswiki.org/emacs/EmacsEchoServer
  9. ;; http://nullprogram.com/blog/2009/05/17/
  10. ;;
  11. ;; (C) 2009 Alex Bennee (alex@bennee.com)
  12. ;; (C) 2010 Riccardo Murri (riccardo.murri@gmail.com)
  13. ;;
  14. ;; Licensed under GPLv3
  15. ;;
  16. ; still debugging
  17. (setq debug-on-error 't)
  18. (setq edebug-all-defs 't)
  19. ;; Customization
  20. (defcustom edit-server-port 9292
  21. "Local port the edit server listens to."
  22. :group 'edit-server
  23. :type 'integer)
  24. (defcustom edit-server-new-frame t
  25. "If not nil, edit each buffer in a new frame (and raise it)."
  26. :group 'edit-server
  27. :type 'boolean)
  28. ;; Vars
  29. (defvar edit-server-proc 'nil
  30. "Network process associated with the current edit, made local when
  31. the edit buffer is created")
  32. (defvar edit-server-frame 'nil
  33. "The frame created for a new edit-server process, made local when
  34. then edit buffer is created")
  35. (defvar edit-server-clients '()
  36. "List of all client processes associated with the server process.")
  37. (defconst edit-server-buffer-name " *edit-server*"
  38. "Template name of the edit-server process buffers")
  39. ;; Mode magic
  40. ;
  41. ; We want to re-map some of the keys to trigger edit-server-done
  42. ; instead of the usual emacs like behaviour. However using
  43. ; local-set-key will affect all buffers of the same mode, hence we
  44. ; define a special (derived) mode for handling editing of text areas.
  45. ;
  46. (defvar edit-server-text-mode-map
  47. (let ((map (make-sparse-keymap)))
  48. (define-key map (kbd "C-x #") 'edit-server-done)
  49. ;; XXX: should rather invoke 'edit-server-abort when the buffer is deleted,
  50. ;; and send the original content back to the HTTP client
  51. (define-key map (kbd "C-x k") 'edit-server-abort)
  52. (define-key map (kbd "C-x C-s") 'edit-server-done)
  53. map)
  54. "Keymap for `edit-server-text-mode'.")
  55. ;; XXX: make it a minor mode? server-mode is minor
  56. (define-derived-mode edit-server-text-mode text-mode "Edit Server Text Mode"
  57. "A derived version of text-mode with a few common Emacs keystrokes
  58. rebound to more functions that can deal with the response to the
  59. edit-server request")
  60. ;; Edit Server socket code
  61. ;
  62. (defun edit-server-start nil
  63. "Start the edit server"
  64. (interactive)
  65. (if (process-status "edit-server")
  66. (message "An edit-server process is already running")
  67. (make-network-process
  68. :name "edit-server"
  69. :buffer edit-server-buffer-name
  70. :family 'ipv4
  71. :host 'local ; only listen to local connections
  72. :service edit-server-port
  73. :log 'edit-server-accept
  74. :server 't)
  75. (setq edit-server-clients '())
  76. (message "Created a new edit-server process")))
  77. (defun edit-server-stop nil
  78. "Stop the edit server"
  79. (interactive)
  80. (while edit-server-clients
  81. (edit-server-kill-client (car edit-server-clients))
  82. (setq edit-server-clients (cdr edit-server-clients)))
  83. (if (process-status "edit-server")
  84. (delete-process "edit-server")
  85. (message "No edit server running"))
  86. (if (get-buffer edit-server-buffer-name)
  87. (kill-buffer edit-server-buffer-name)))
  88. ; Write log entries
  89. (defun edit-server-log (string &optional client)
  90. "If a `*edit-server-log*' buffer exists, write STRING to it for logging purposes."
  91. (if (get-buffer " *edit-server-log*")
  92. (with-current-buffer " *edit-server-log*"
  93. (goto-char (point-max))
  94. (insert (current-time-string)
  95. (if client (format " %s:" client) " ")
  96. string)
  97. (or (bolp) (newline)))))
  98. (defun edit-server-accept (server client msg)
  99. "Accept a new client connection."
  100. (let ((buffer (generate-new-buffer edit-server-buffer-name)))
  101. (buffer-disable-undo buffer)
  102. (set-process-buffer client buffer)
  103. (set-process-filter client 'edit-server-filter)
  104. (set-process-query-on-exit-flag client nil) ; kill-buffer kills the associated process
  105. (with-current-buffer buffer
  106. (set (make-local-variable 'edit-server-phase) 'wait)
  107. (set (make-local-variable 'edit-server-received) 0)
  108. (set (make-local-variable 'edit-server-request) nil))
  109. (set (make-local-variable 'edit-server-content-length) nil))
  110. (add-to-list 'edit-server-clients client))
  111. (defun edit-server-filter (proc string)
  112. "Process data received from the client."
  113. ;; there is no guarantee that data belonging to the same client
  114. ;; request will arrive all in one go; therefore, we must accumulate
  115. ;; data in the buffer and process it in different phases, which
  116. ;; requires us to keep track of the processing state.
  117. (with-current-buffer (process-buffer proc)
  118. (insert string)
  119. (setq edit-server-received
  120. (+ edit-server-received (string-bytes string)))
  121. (when (eq edit-server-phase 'wait)
  122. ;; look for a complete HTTP request string
  123. (save-excursion
  124. (goto-char (point-min))
  125. (when (re-search-forward "^\\([A-Z]+\\)\\s-+\\(\\S-+\\)\\s-+\\(HTTP/[0-9\.]+\\)\r?\n" nil t)
  126. (message "edit-server: Got HTTP `%s' request, processing in buffer `%s'..."
  127. (match-string 1) (current-buffer))
  128. (setq edit-server-request (match-string 1))
  129. (setq edit-server-content-length nil)
  130. (setq edit-server-phase 'head))))
  131. (when (eq edit-server-phase 'head)
  132. ;; look for "Content-length" header
  133. (save-excursion
  134. (goto-char (point-min))
  135. (when (re-search-forward "^Content-Length:\\s-+\\([0-9]+\\)" nil t)
  136. (setq edit-server-content-length (string-to-number (match-string 1)))))
  137. ;; look for head/body separator
  138. (save-excursion
  139. (goto-char (point-min))
  140. (when (re-search-forward "\\(\r?\n\\)\\{2\\}" nil t)
  141. ;; HTTP headers are pure ASCII (1 char = 1 byte), so we can subtract
  142. ;; the buffer position from the count of received bytes
  143. (setq edit-server-received
  144. (- edit-server-received (- (match-end 0) (point-min))))
  145. ;; discard headers - keep only HTTP content in buffer
  146. (delete-region (point-min) (match-end 0))
  147. (setq edit-server-phase 'body))))
  148. (when (eq edit-server-phase 'body)
  149. (if (and edit-server-content-length
  150. (> edit-server-content-length edit-server-received))
  151. (message "edit-server: Received %d bytes of %d ..."
  152. edit-server-received edit-server-content-length)
  153. ;; all content trasnferred - process request now
  154. (cond
  155. ((string= edit-server-request "POST")
  156. ;; create editing buffer, and move content to it
  157. (edit-server-create-edit-buffer proc))
  158. (t
  159. ;; send 200 OK response to any other request
  160. (edit-server-send-response proc "edit-server is running.\n" t)))
  161. ;; wait for another connection to arrive
  162. (setq edit-server-received 0)
  163. (setq edit-server-phase 'wait)))))
  164. (defun edit-server-create-edit-buffer(proc)
  165. "Create an edit buffer, place content in it and save the network
  166. process for the final call back"
  167. (let ((buffer (generate-new-buffer "TEXTAREA")))
  168. (copy-to-buffer buffer (point-min) (point-max))
  169. (with-current-buffer buffer
  170. (edit-server-text-mode)
  171. (buffer-enable-undo)
  172. (set (make-local-variable 'edit-server-proc) proc)
  173. (set (make-local-variable 'edit-server-frame)
  174. (if edit-server-new-frame (make-frame) nil))
  175. (if edit-server-new-frame
  176. (raise-frame edit-server-frame)
  177. (pop-to-buffer buffer)))))
  178. (defun edit-server-send-response (proc &optional string close)
  179. "Send an HTTP 200 OK response back to process PROC.
  180. Optional second argument STRING specifies the response content.
  181. If optional third argument CLOSE is non-nil, then process PROC
  182. and its buffer are killed with `edit-server-kill-client'."
  183. (interactive)
  184. (message "edit-server-send-response")
  185. (if proc
  186. (let ((response-header (concat
  187. "HTTP/1.0 200 OK\n"
  188. "Server: Emacs\n"
  189. "Date: "
  190. (format-time-string
  191. "%a, %d %b %Y %H:%M:%S GMT\n"
  192. (current-time)))))
  193. (process-send-string proc response-header)
  194. (process-send-string proc "\n")
  195. (if string
  196. (process-send-string proc string))
  197. (process-send-eof proc)
  198. (if close
  199. (edit-server-kill-client proc)))
  200. (message "edit-server-send-response: null proc (bug?)")))
  201. (defun edit-server-kill-client (proc)
  202. "Kill client process PROC and remove it from the list."
  203. (let ((procbuf (process-buffer proc)))
  204. (delete-process proc)
  205. (kill-buffer procbuf)
  206. (setq edit-server-clients (delq procbuf edit-server-clients))))
  207. (defun edit-server-done ()
  208. "Once someone is done with editing their text edit-server-done is
  209. called and the response is sent back to the browser"
  210. (interactive)
  211. (let ((buffer (current-buffer))
  212. (proc edit-server-proc)
  213. (procbuf (process-buffer edit-server-proc)))
  214. ;; edit-server-* vars are buffer-local, so they must be used before issuing kill-buffer
  215. (edit-server-send-response edit-server-proc (buffer-string))
  216. (if edit-server-frame (delete-frame edit-server-frame))
  217. ;; delete-frame may change the current buffer
  218. (kill-buffer buffer)
  219. (edit-server-kill-client proc)))
  220. (defun edit-server-abort ()
  221. "Send the original text back to the browser."
  222. (interactive)
  223. (let ((buffer (current-buffer))
  224. (proc edit-server-proc)
  225. (procbuf (process-buffer edit-server-proc)))
  226. ;; edit-server-* vars are buffer-local, so they must be used before issuing kill-buffer
  227. (with-current-buffer procbuf
  228. (edit-server-send-response proc (buffer-string)))
  229. (if edit-server-frame (delete-frame edit-server-frame))
  230. ;; delete-frame may change the current buffer
  231. (kill-buffer buffer)
  232. (edit-server-kill-client proc)))