PageRenderTime 54ms CodeModel.GetById 42ms app.highlight 9ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-2-pre14/org/gjt/sp/jedit/EditServer.java

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