Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/gjt/sp/jedit/EditServer.java


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  
23  package org.gjt.sp.jedit;
24  
25  //{{{ Imports
26  import bsh.NameSpace;
27  import javax.swing.SwingUtilities;
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  /**
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,v 1.19 2003/05/05 23:11:47 spestov Exp $
58   */
59  public 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        out.write("b\n");
90        out.write(String.valueOf(port));
91        out.write("\n");
92        out.write(String.valueOf(authKey));
93        out.write("\n");
94        out.close();
95  
96        Log.log(Log.DEBUG,this,"jEdit server started on port "
97          + socket.getLocalPort());
98        Log.log(Log.DEBUG,this,"Authorization key is "
99          + authKey);
100 
101       ok = true;
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 
114   //{{{ run() method
115   public void run()
116   {
117     for(;;)
118     {
119       if(abort)
120         return;
121 
122       Socket client = null;
123       try
124       {
125         client = socket.accept();
126 
127         // Stop script kiddies from opening the edit
128         // server port and just leaving it open, as a
129         // DoS
130         client.setSoTimeout(1000);
131 
132         Log.log(Log.MESSAGE,this,client + ": connected");
133 
134         DataInputStream in = new DataInputStream(
135           client.getInputStream());
136         OutputStream out = client.getOutputStream();
137 
138         if(!handleClient(client,in))
139           abort = true;
140       }
141       catch(Exception e)
142       {
143         if(!abort)
144           Log.log(Log.ERROR,this,e);
145         abort = true;
146       }
147       finally
148       {
149         /* if(client != null)
150         {
151           try
152           {
153             client.close();
154           }
155           catch(Exception e)
156           {
157             Log.log(Log.ERROR,this,e);
158           }
159 
160           client = null;
161         } */
162       }
163     }
164   } //}}}
165 
166   //{{{ handleClient() method
167   /**
168    * @param restore Ignored unless no views are open
169    * @param parent The client's parent directory
170    * @param args A list of files. Null entries are ignored, for convinience
171    * @since jEdit 3.2pre7
172    */
173   public static void handleClient(boolean restore, String parent,
174     String[] args)
175   {
176     handleClient(restore,false,false,parent,args);
177   } //}}}
178 
179   //{{{ handleClient() method
180   /**
181    * @param restore Ignored unless no views are open
182    * @param newView Open a new view?
183    * @param newPlainView Open a new plain view?
184    * @param parent The client's parent directory
185    * @param args A list of files. Null entries are ignored, for convinience
186    * @since jEdit 4.2pre1
187    */
188   public static Buffer handleClient(boolean restore,
189     boolean newView, boolean newPlainView, String parent,
190     String[] args)
191   {
192     // we have to deal with a huge range of possible border cases here.
193     if(jEdit.getFirstView() == null)
194     {
195       // coming out of background mode.
196       // no views open.
197       // no buffers open if args empty.
198 
199       Buffer buffer = jEdit.openFiles(null,parent,args);
200 
201       boolean restoreFiles = restore && jEdit.getBooleanProperty("restore")
202         && (buffer == null || jEdit.getBooleanProperty("restore.cli"));
203 
204       View view = PerspectiveManager.loadPerspective(restoreFiles);
205 
206       if(view == null)
207       {
208         if(buffer == null)
209           buffer = jEdit.getFirstBuffer();
210         view = jEdit.newView(null,buffer);
211       }
212       else if(buffer != null)
213         view.setBuffer(buffer);
214 
215       return buffer;
216     }
217     else if(newPlainView)
218     {
219       // no background mode, and opening a new view
220       Buffer buffer = jEdit.openFiles(null,parent,args);
221       if(buffer == null)
222         buffer = jEdit.getFirstBuffer();
223       jEdit.newView(null,buffer,true);
224       return buffer;
225     }
226     else if(newView)
227     {
228       // no background mode, and opening a new view
229       Buffer buffer = jEdit.openFiles(null,parent,args);
230       if(buffer == null)
231         buffer = jEdit.getFirstBuffer();
232       jEdit.newView(jEdit.getActiveView(),buffer,false);
233       return buffer;
234     }
235     else
236     {
237       // no background mode, and reusing existing view
238       View view = jEdit.getActiveView();
239 
240       Buffer buffer = jEdit.openFiles(view,parent,args);
241 
242       // Hack done to fix bringing the window to the front.
243       // At least on windows, Frame.toFront() doesn't cut it.
244       // Remove the isWindows check if it's broken under other
245       // OSes too.
246       if (jEdit.getBooleanProperty("server.brokenToFront"))
247         view.setState(java.awt.Frame.ICONIFIED);
248 
249       // un-iconify using JDK 1.3 API
250       view.setState(java.awt.Frame.NORMAL);
251       view.requestFocus();
252       view.toFront();
253 
254       return buffer;
255     }
256   } //}}}
257 
258   //{{{ isOK() method
259   boolean isOK()
260   {
261     return ok;
262   } //}}}
263 
264   // stopServer() method
265   void stopServer()
266   {
267     abort = true;
268     try
269     {
270       socket.close();
271     }
272     catch(IOException io)
273     {
274     }
275 
276     new File(portFile).delete();
277   } //}}}
278 
279   //{{{ Private members
280 
281   //{{{ Instance variables
282   private String portFile;
283   private ServerSocket socket;
284   private int authKey;
285   private boolean ok;
286   private boolean abort;
287   //}}}
288 
289   //{{{ handleClient() method
290   private boolean handleClient(final Socket client, DataInputStream in)
291     throws Exception
292   {
293     int key = in.readInt();
294     if(key != authKey)
295     {
296       Log.log(Log.ERROR,this,client + ": wrong"
297         + " authorization key (got " + key
298         + ", expected " + authKey + ")");
299       in.close();
300       client.close();
301 
302       return false;
303     }
304     else
305     {
306       // Reset the timeout
307       client.setSoTimeout(0);
308 
309       Log.log(Log.DEBUG,this,client + ": authenticated"
310         + " successfully");
311 
312       final String script = in.readUTF();
313       Log.log(Log.DEBUG,this,script);
314 
315       SwingUtilities.invokeLater(new Runnable()
316       {
317         public void run()
318         {
319           try
320           {
321             NameSpace ns = new NameSpace(
322               BeanShell.getNameSpace(),
323               "EditServer namespace");
324             ns.setVariable("socket",client);
325             BeanShell.eval(null,ns,script);
326           }
327           catch(bsh.UtilEvalError e)
328           {
329             Log.log(Log.ERROR,this,e);
330           }
331           finally
332           {
333             try
334             {
335               BeanShell.getNameSpace().setVariable("socket",null);
336             }
337             catch(bsh.UtilEvalError e)
338             {
339               Log.log(Log.ERROR,this,e);
340             }
341           }
342         }
343       });
344 
345       return true;
346     }
347   } //}}}
348 
349   //}}}
350 }