PageRenderTime 47ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/jEdit/tags/jedit-4-5-pre1/org/gjt/sp/jedit/EditServer.java

#
Java | 384 lines | 228 code | 41 blank | 115 comment | 32 complexity | f7f7d0970698f480cfcef53c430eff2a MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
  1. /*
  2. * EditServer.java - jEdit server
  3. * :tabSize=8:indentSize=8:noTabs=false:
  4. * :folding=explicit:collapseFolds=1:
  5. *
  6. * Copyright (C) 1999, 2003 Slava Pestov
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, write to the Free Software
  20. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  21. */
  22. package org.gjt.sp.jedit;
  23. //{{{ Imports
  24. import org.gjt.sp.jedit.bsh.NameSpace;
  25. import javax.swing.*;
  26. import java.awt.*;
  27. import java.awt.event.*;
  28. import java.io.*;
  29. import java.net.*;
  30. import java.util.Random;
  31. import org.gjt.sp.jedit.io.FileVFS;
  32. import org.gjt.sp.util.Log;
  33. //}}}
  34. /**
  35. * Inter-process communication.<p>
  36. *
  37. * The edit server protocol is very simple. <code>$HOME/.jedit/server</code>
  38. * is an ASCII file containing two lines, the first being the port number,
  39. * the second being the authorization key.<p>
  40. *
  41. * You connect to that port on the local machine, sending the authorization
  42. * key as four bytes in network byte order, followed by the length of the
  43. * BeanShell script as two bytes in network byte order, followed by the
  44. * script in UTF8 encoding. After the socked is closed, the BeanShell script
  45. * will be executed by jEdit.<p>
  46. *
  47. * The snippet is executed in the AWT thread. None of the usual BeanShell
  48. * variables (view, buffer, textArea, editPane) are set so the script has to
  49. * figure things out by itself.<p>
  50. *
  51. * In most cases, the script will call the static
  52. * {@link #handleClient(boolean,String,String[])} method, but of course more
  53. * complicated stuff can be done too.
  54. *
  55. * @author Slava Pestov
  56. * @version $Id: EditServer.java 19727 2011-08-01 17:45:18Z kpouer $
  57. */
  58. public class EditServer extends Thread
  59. {
  60. //{{{ EditServer constructor
  61. EditServer(String portFile)
  62. {
  63. super("jEdit server daemon [" + portFile + "]");
  64. setDaemon(true);
  65. this.portFile = portFile;
  66. try
  67. {
  68. // On Unix, set permissions of port file to rw-------,
  69. // so that on broken Unices which give everyone read
  70. // access to user home dirs, people can't see your
  71. // port file (and hence send arbitriary BeanShell code
  72. // your way. Nasty.)
  73. if(OperatingSystem.isUnix())
  74. {
  75. new File(portFile).createNewFile();
  76. FileVFS.setPermissions(portFile,0600);
  77. }
  78. // Bind to any port on localhost; accept 2 simultaneous
  79. // connection attempts before rejecting connections
  80. socket = new ServerSocket(0, 2,
  81. InetAddress.getByName("127.0.0.1"));
  82. authKey = new Random().nextInt(Integer.MAX_VALUE);
  83. int port = socket.getLocalPort();
  84. FileWriter out = new FileWriter(portFile);
  85. try
  86. {
  87. out.write("b\n");
  88. out.write(String.valueOf(port));
  89. out.write("\n");
  90. out.write(String.valueOf(authKey));
  91. out.write("\n");
  92. }
  93. finally
  94. {
  95. out.close();
  96. }
  97. ok = true;
  98. Log.log(Log.DEBUG,this,"jEdit server started on port "
  99. + socket.getLocalPort());
  100. Log.log(Log.DEBUG,this,"Authorization key is "
  101. + authKey);
  102. }
  103. catch(IOException io)
  104. {
  105. /* on some Windows versions, connections to localhost
  106. * fail if the network is not running. To avoid
  107. * confusing newbies with weird error messages, log
  108. * errors that occur while starting the server
  109. * as NOTICE, not ERROR */
  110. Log.log(Log.NOTICE,this,io);
  111. }
  112. } //}}}
  113. //{{{ run() method
  114. public void run()
  115. {
  116. for(;;)
  117. {
  118. if(abort)
  119. return;
  120. Socket client = null;
  121. try
  122. {
  123. client = socket.accept();
  124. // Stop script kiddies from opening the edit
  125. // server port and just leaving it open, as a
  126. // DoS
  127. client.setSoTimeout(1000);
  128. Log.log(Log.MESSAGE,this,client + ": connected");
  129. DataInputStream in = new DataInputStream(
  130. client.getInputStream());
  131. if(!handleClient(client,in))
  132. abort = true;
  133. }
  134. catch(Exception e)
  135. {
  136. if(!abort)
  137. Log.log(Log.ERROR,this,e);
  138. abort = true;
  139. }
  140. finally
  141. {
  142. /* if(client != null)
  143. {
  144. try
  145. {
  146. client.close();
  147. }
  148. catch(Exception e)
  149. {
  150. Log.log(Log.ERROR,this,e);
  151. }
  152. client = null;
  153. } */
  154. }
  155. }
  156. } //}}}
  157. //{{{ handleClient() method
  158. /**
  159. * @param restore Ignored unless no views are open
  160. * @param parent The client's parent directory
  161. * @param args A list of files. Null entries are ignored, for convinience
  162. * @since jEdit 3.2pre7
  163. */
  164. public static void handleClient(boolean restore, String parent,
  165. String[] args)
  166. {
  167. handleClient(restore,false,false,parent,args);
  168. } //}}}
  169. //{{{ handleClient() method
  170. /**
  171. * @param restore Ignored unless no views are open
  172. * @param newView Open a new view?
  173. * @param newPlainView Open a new plain view?
  174. * @param parent The client's parent directory
  175. * @param args A list of files. Null entries are ignored, for convinience
  176. * @since jEdit 4.2pre1
  177. */
  178. public static Buffer handleClient(boolean restore,
  179. boolean newView, boolean newPlainView, String parent,
  180. String[] args)
  181. {
  182. // we have to deal with a huge range of possible border cases here.
  183. if(jEdit.getFirstView() == null)
  184. {
  185. // coming out of background mode.
  186. // no views open.
  187. // no buffers open if args empty.
  188. boolean hasBufferArgs = false;
  189. for (String arg : args)
  190. {
  191. if (arg != null)
  192. {
  193. hasBufferArgs = true;
  194. break;
  195. }
  196. }
  197. boolean restoreFiles = restore
  198. && jEdit.getBooleanProperty("restore")
  199. && (!hasBufferArgs
  200. || jEdit.getBooleanProperty("restore.cli"));
  201. View view = PerspectiveManager.loadPerspective(
  202. restoreFiles);
  203. Buffer buffer = jEdit.openFiles(view,parent,args);
  204. if(view == null)
  205. {
  206. if(buffer == null)
  207. buffer = jEdit.getFirstBuffer();
  208. jEdit.newView(null,buffer);
  209. }
  210. else if(buffer != null)
  211. view.setBuffer(buffer,false);
  212. return buffer;
  213. }
  214. else if(newPlainView)
  215. {
  216. // no background mode, and opening a new view
  217. Buffer buffer = jEdit.openFiles(null,parent,args);
  218. if(buffer == null)
  219. buffer = jEdit.getFirstBuffer();
  220. jEdit.newView(null,buffer,true);
  221. return buffer;
  222. }
  223. else if(newView)
  224. {
  225. // no background mode, and opening a new view
  226. Buffer buffer = jEdit.openFiles(null,parent,args);
  227. if(buffer == null)
  228. buffer = jEdit.getFirstBuffer();
  229. jEdit.newView(jEdit.getActiveView(),buffer,false);
  230. return buffer;
  231. }
  232. else
  233. {
  234. // no background mode, and reusing existing view
  235. View view = jEdit.getActiveView();
  236. Buffer buffer = jEdit.openFiles(view,parent,args);
  237. // Hack done to fix bringing the window to the front.
  238. // At least on windows, Frame.toFront() doesn't cut it.
  239. // Remove the isWindows check if it's broken under other
  240. // OSes too.
  241. if (jEdit.getBooleanProperty("server.brokenToFront"))
  242. view.setState(java.awt.Frame.ICONIFIED);
  243. // un-iconify using JDK 1.3 API
  244. view.setState(java.awt.Frame.NORMAL);
  245. view.requestFocus();
  246. view.toFront();
  247. // In some platforms (e.g. Windows), only setAlwaysOnTop works
  248. if (! view.isAlwaysOnTop())
  249. {
  250. view.setAlwaysOnTop(true);
  251. view.setAlwaysOnTop(false);
  252. }
  253. return buffer;
  254. }
  255. } //}}}
  256. //{{{ isOK() method
  257. boolean isOK()
  258. {
  259. return ok;
  260. } //}}}
  261. //{{{ getPort method
  262. public int getPort()
  263. {
  264. return socket.getLocalPort();
  265. } //}}}
  266. //{{{ stopServer() method
  267. void stopServer()
  268. {
  269. abort = true;
  270. try
  271. {
  272. socket.close();
  273. }
  274. catch(IOException io)
  275. {
  276. }
  277. new File(portFile).delete();
  278. } //}}}
  279. //{{{ Private members
  280. //{{{ Instance variables
  281. private String portFile;
  282. private ServerSocket socket;
  283. private int authKey;
  284. private boolean ok;
  285. private boolean abort;
  286. //}}}
  287. //{{{ handleClient() method
  288. private boolean handleClient(final Socket client, DataInputStream in)
  289. throws Exception
  290. {
  291. int key = in.readInt();
  292. if(key != authKey)
  293. {
  294. Log.log(Log.ERROR,this,client + ": wrong"
  295. + " authorization key (got " + key
  296. + ", expected " + authKey + ")");
  297. in.close();
  298. client.close();
  299. return false;
  300. }
  301. else
  302. {
  303. // Reset the timeout
  304. client.setSoTimeout(0);
  305. Log.log(Log.DEBUG,this,client + ": authenticated"
  306. + " successfully");
  307. final String script = in.readUTF();
  308. Log.log(Log.DEBUG,this,script);
  309. SwingUtilities.invokeLater(new Runnable()
  310. {
  311. public void run()
  312. {
  313. try
  314. {
  315. NameSpace ns = new NameSpace(
  316. BeanShell.getNameSpace(),
  317. "EditServer namespace");
  318. ns.setVariable("socket",client);
  319. BeanShell.eval(null,ns,script);
  320. }
  321. catch(org.gjt.sp.jedit.bsh.UtilEvalError e)
  322. {
  323. Log.log(Log.ERROR,this,e);
  324. }
  325. finally
  326. {
  327. try
  328. {
  329. BeanShell.getNameSpace().setVariable("socket",null);
  330. }
  331. catch(org.gjt.sp.jedit.bsh.UtilEvalError e)
  332. {
  333. Log.log(Log.ERROR,this,e);
  334. }
  335. }
  336. }
  337. });
  338. return true;
  339. }
  340. } //}}}
  341. //}}}
  342. }