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