View Javadoc
1   //
2   // $Id: JUploadApplet.java 750 2009-05-06 14:36:50Z etienne_sf $
3   //
4   // jupload - A file upload applet.
5   // Copyright 2007 The JUpload Team
6   //
7   // Created: ?
8   // Creator: William JinHua Kwong
9   // Last modified: $Date: 2009-05-06 16:36:50 +0200 (mer., 06 mai 2009) $
10  //
11  // This program is free software; you can redistribute it and/or modify it under
12  // the terms of the GNU General Public License as published by the Free Software
13  // Foundation; either version 2 of the License, or (at your option) any later
14  // version. This program is distributed in the hope that it will be useful, but
15  // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16  // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17  // details. You should have received a copy of the GNU General Public License
18  // along with this program; if not, write to the Free Software Foundation, Inc.,
19  // 675 Mass Ave, Cambridge, MA 02139, USA.
20  
21  package wjhk.jupload2.context;
22  
23  import java.awt.Container;
24  import java.awt.Cursor;
25  import java.awt.Frame;
26  import java.io.File;
27  import java.io.IOException;
28  import java.net.URI;
29  import java.net.URISyntaxException;
30  import java.net.URL;
31  import java.util.Vector;
32  import java.util.regex.Matcher;
33  
34  import javax.swing.JApplet;
35  import javax.swing.JFileChooser;
36  import javax.swing.JOptionPane;
37  
38  import netscape.javascript.JSException;
39  import netscape.javascript.JSObject;
40  import wjhk.jupload2.exception.JUploadException;
41  import wjhk.jupload2.gui.filepanel.FilePanel.FileListViewMode;
42  
43  /**
44   * Implementation of the Jupload Context, for an applet. One such context is created at run time.
45   * 
46   * @see DefaultJUploadContext
47   * @author etienne_sf
48   * @version $Revision: 750 $
49   */
50  @SuppressWarnings("restriction")
51  public class JUploadContextApplet extends DefaultJUploadContext {
52  
53      /**
54       * The current applet. All applet parameters are reading by using this attribute.
55       */
56      JApplet theApplet = null;
57  
58      /**
59       * The default constructor.
60       * 
61       * @param theApplet The applet is mandatory, to read the applet parameters.
62       */
63      public JUploadContextApplet(JApplet theApplet) {
64          if (theApplet == null) {
65              throw new IllegalArgumentException("theApplet may not be null");
66          }
67          this.theApplet = theApplet;
68  
69          // The applet must be signed !
70          checkAppletIsSigned();
71  
72          // Let's initialize the DefaultJUploadContext.
73          init(findParentFrame(theApplet), theApplet);
74      }
75  
76      /**
77       * This method checks that the applet is signed. To do this, it just check that the current folder is readable, and
78       * that the applet can create temporary files. Should be enough.<BR>
79       * If anyone has a better idea ... I'll be hapy to listen to it!
80       */
81      void checkAppletIsSigned() {
82  
83          // Let's be optimistic! ;-)
84          java.lang.SecurityException ex = null;
85          try {
86              // I found no way to directly get the current directory (access to
87              // user.xxx system properties is prohibited from within applets)
88              JFileChooser fc = new JFileChooser();
89              File currentDir = fc.getCurrentDirectory();
90              if (!currentDir.canRead()) {
91                  ex = new java.lang.SecurityException("The applet must be signed (can't write in '"
92                          + currentDir.getAbsolutePath() + "')");
93              }
94  
95              // Let's now check that the applet may create a temporary file. May
96              // be necessary for further processing, and for logging.
97              try {
98                  File tmpTestFile = File.createTempFile("jupload", "test");
99                  tmpTestFile.delete();
100             } catch (IOException ioe) {
101                 String msg = ioe.getClass().getName()
102                         + ": Can't create temporary file (the applet is perhaps not signed (original error message: "
103                         + ioe.getMessage() + ")";
104                 System.out.println(msg);
105                 ex = new java.lang.SecurityException(msg, ioe);
106                 ioe.printStackTrace();
107             }
108         } catch (java.lang.SecurityException e) {
109             String msg = "The applet must be signed (original error message: " + e.getMessage() + ")";
110             System.out.println(msg);
111             ex = new java.lang.SecurityException(msg, e);
112         }
113 
114         if (ex != null) {
115             String msg = ex.getClass().getName() + " - " + ex.getMessage();
116             JOptionPane.showMessageDialog(null, msg, "Alert", JOptionPane.ERROR_MESSAGE);
117             System.out.println(msg);
118             ex.printStackTrace();
119 
120             throw ex;
121         }
122     }
123 
124     /**
125      * Find the JFrame which contains the applet
126      * 
127      * @param theApplet
128      * @return
129      */
130     private Frame findParentFrame(JApplet theApplet) {
131         Container c = theApplet;
132         while (c != null) {
133             if (c instanceof Frame)
134                 return (Frame) c;
135 
136             c = c.getParent();
137         }
138         return (Frame) null;
139     }
140 
141     /** {@inheritDoc} */
142     @Override
143     public JApplet getApplet() {
144         return this.theApplet;
145     }
146 
147     /** {@inheritDoc} */
148     @Override
149     public String getParameter(String key, String def) {
150         String paramStr = (this.theApplet.getParameter(key) != null ? this.theApplet.getParameter(key) : def);
151         displayDebugParameterValue(key, paramStr);
152         return paramStr;
153     }
154 
155     /** {@inheritDoc} */
156     @Override
157     public int getParameter(String key, int def) {
158         String paramDef = Integer.toString(def);
159         String paramStr = this.theApplet.getParameter(key) != null ? this.theApplet.getParameter(key) : paramDef;
160         displayDebugParameterValue(key, paramStr);
161         return parseInt(paramStr, def);
162     }
163 
164     /** {@inheritDoc} */
165     @Override
166     public float getParameter(String key, float def) {
167         String paramDef = Float.toString(def);
168         String paramStr = this.theApplet.getParameter(key) != null ? this.theApplet.getParameter(key) : paramDef;
169         displayDebugParameterValue(key, paramStr);
170         return parseFloat(paramStr, def);
171     }
172 
173     /** {@inheritDoc} */
174     @Override
175     public long getParameter(String key, long def) {
176         String paramDef = Long.toString(def);
177         String paramStr = this.theApplet.getParameter(key) != null ? this.theApplet.getParameter(key) : paramDef;
178         displayDebugParameterValue(key, paramStr);
179         return parseLong(paramStr, def);
180     }// getParameter(int)
181 
182     /** {@inheritDoc} */
183     @Override
184     public boolean getParameter(String key, boolean def) {
185         String paramDef = (def ? "true" : "false");
186         String paramStr = this.theApplet.getParameter(key) != null ? this.theApplet.getParameter(key) : paramDef;
187         displayDebugParameterValue(key, paramStr);
188         return parseBoolean(paramStr, def);
189     }// getParameter(boolean)
190 
191     /** {@inheritDoc} */
192     public FileListViewMode getParameter(String key, FileListViewMode def) {
193         String paramDef = def.toString();
194         String paramStr = this.theApplet.getParameter(key) != null ? this.theApplet.getParameter(key) : paramDef;
195         displayDebugParameterValue(key, paramStr);
196         return parseFileListViewMode(paramStr, def);
197     }// getParameter(FileListViewMode)
198 
199     /**
200      * Loads cookies, and add them to the specific headers for upload requests. {@inheritDoc}
201      */
202     @Override
203     public void readCookieFromNavigator(Vector<String> headers) {
204         String cookie = null;
205 
206         try {
207             // Patch given by Stani: corrects the use of JUpload for
208             // Firefox on Mac.
209             cookie = (String) JSObject.getWindow(this.theApplet).eval("document.cookie");
210             uploadPolicy.displayDebug("Cookie read from the navigator: " + cookie, 80);
211         } catch (JSException e) {
212             String msg = "JSException (" + e.getClass() + ": " + e.getMessage()
213                     + ") in DefaultUploadPolicy, trying default values.";
214             System.out.println(msg);
215             uploadPolicy.displayWarn(msg);
216 
217             // If we can't have access to the JS objects, we're in development :
218             // Let's put some 'hard value', to test the juploadContext from the
219             // development tool (mine is eclipse).
220 
221             // felfert: I need different values so let's make that
222             // configurable...
223             cookie = System.getProperty("debug_cookie");
224 
225             msg = "  no navigator found, reading 'debug_cookie' from system properties (" + cookie + ")";
226             System.out.println(msg);
227             uploadPolicy.displayDebug(msg, 80);
228             /*
229              * Example of parameter when calling the JVM: -Ddebug_cookie="Cookie:cpg146_data=
230              * YTo0OntzOjI6IklEIjtzOjMyOiJhZGU3MWIxZmU4OTZjNThhZjQ5N2FiY2ZiNmFlZTUzOCI7czoyOiJhbSI7aToxO3M6NDoibGFuZyI7czo2OiJmcmVuY2giO3M6MzoibGl2IjthOjI6e2k6MDtOO2k6MTtzOjQ6IjE0ODgiO319
231              * "
232              */
233         }
234         // The cookies and user-agent will be added to the header sent by the
235         // juploadContext:
236         if (cookie != null)
237             headers.add("Cookie: " + cookie);
238     }
239 
240     /**
241      * Loads userAgent, and add it as a header to the specific headers for upload requests. {@inheritDoc}
242      */
243     @Override
244     public void readUserAgentFromNavigator(Vector<String> headers) {
245         String userAgent = null;
246 
247         try {
248             // Patch given by Stani: corrects the use of JUpload for
249             // Firefox on Mac.
250             userAgent = (String) JSObject.getWindow(this.theApplet).eval("navigator.userAgent");
251             uploadPolicy.displayDebug("userAgent read from the navigator: " + userAgent, 80);
252         } catch (JSException e) {
253             String msg = "JSException (" + e.getClass() + ": " + e.getMessage()
254                     + ") in DefaultUploadPolicy, trying default values.";
255             System.out.println(msg);
256             uploadPolicy.displayWarn(msg);
257 
258             // If we can't have access to the JS objects, we're in development :
259             // Let's put some 'hard value', to test the juploadContext from the
260             // development tool (mine is eclipse).
261 
262             // felfert: I need different values so let's make that
263             // configurable...
264             userAgent = System.getProperty("debug_agent");
265             msg = "  no navigator found, reading 'debug_agent' from system properties (" + userAgent + ")";
266             System.out.println(msg);
267             uploadPolicy.displayDebug(msg, 80);
268             /*
269              * Example of parameter when calling the JVM: -Ddebug_agent="userAgent: Mozilla/5.0 (Windows; U; Windows NT
270              * 5.0; fr; rv:1.8.1.3) Gecko/20070309 Firefox/2.0.0.3"
271              */
272         }
273         // The user-agent will be added to the header sent by the
274         // juploadContext:
275         if (userAgent != null)
276             headers.add("User-Agent: " + userAgent);
277 
278     }
279 
280     /**
281      * @return The current cursor
282      * @see JUploadContext#setCursor(Cursor)
283      */
284     @Override
285     public Cursor getCursor() {
286         return this.theApplet.getCursor();
287     }
288 
289     /** @see JUploadContext#setCursor(Cursor) */
290     @Override
291     public Cursor setCursor(Cursor cursor) {
292         Cursor previousCursor = this.theApplet.getCursor();
293         this.theApplet.setCursor(cursor);
294         return previousCursor;
295     }
296 
297     /** {@inheritDoc} */
298     @Override
299     public void showStatus(String status) {
300         this.getUploadPanel().getStatusLabel().setText(status);
301     }
302 
303     /**
304      * @see JUploadContext#displayURL(String, boolean)
305      */
306     @Override
307     public void displayURL(String url, boolean success) {
308         try {
309             if (url.toLowerCase().startsWith("javascript:")) {
310                 // A JavaScript expression was specified. Execute it.
311                 String expr = url.substring(11);
312 
313                 // Replacement of %msg%. Will do something only if the %msg%
314                 // string exists in expr.
315                 expr = expr.replaceAll("%msg%",
316                         Matcher.quoteReplacement(jsString(getUploadPolicy().getLastResponseMessage())));
317 
318                 // Replacement of %body%. Will do something only if the
319                 // %body% string exists in expr.
320                 expr = expr.replaceAll("%body%",
321                         Matcher.quoteReplacement(jsString(getUploadPolicy().getLastResponseBody())));
322 
323                 // Replacement of %success%. Will do something only if the
324                 // %success% string exists in expr.
325                 expr = expr.replaceAll("%success%", Matcher.quoteReplacement((success) ? "true" : "false"));
326 
327                 displayDebug("Calling javascript expression: " + expr, 80);
328                 JSObject.getWindow(this.theApplet).eval(expr);
329             } else if (success) {
330                 // This is not a javascript URL: we change the current page
331                 // only if no error occurred.
332                 String target = getUploadPolicy().getAfterUploadTarget();
333                 if (getUploadPolicy().getDebugLevel() >= 100) {
334                     getUploadPolicy().alertStr(
335                             "No switch to getAfterUploadURL, because debug level is "
336                                     + getUploadPolicy().getDebugLevel() + " (>=100)");
337                 } else {
338                     // Let's change the current URL to edit names and
339                     // comments, for the selected album. Ok, let's go and
340                     // add names and comments to the newly updated pictures.
341                     this.theApplet.getAppletContext().showDocument(new URL(url), (null == target) ? "_self" : target);
342                 }
343             }
344         } catch (Exception ee) {
345             // Oops, no navigator. We are probably in debug mode, within
346             // eclipse for instance.
347             try {
348                 getUploadPolicy().displayErr(ee);
349             } catch (JUploadException e) {
350                 // Can't use standard JUpload log mode...
351                 ee.printStackTrace();
352             }
353         }
354     }
355 
356     /**
357      * Generates a valid URL, from a String. The generation may add the documentBase of the applet.
358      * 
359      * @param url A url. Can be a path relative to the current one.
360      * @return The normalized URL
361      * @throws JUploadException
362      */
363     @Override
364     public String normalizeURL(String url) throws JUploadException {
365         if (null == url || url.length() == 0)
366             return this.theApplet.getDocumentBase().toString();
367         URI uri = null;
368         try {
369             uri = new URI(url);
370             if (null == uri.getScheme())
371                 uri = this.theApplet.getDocumentBase().toURI().resolve(url);
372             if (!uri.getScheme().equals("http") && !uri.getScheme().equals("https") && !uri.getScheme().equals("ftp")) {
373                 throw new JUploadException("URI scheme " + uri.getScheme() + " not supported.");
374             }
375         } catch (URISyntaxException e) {
376             throw new JUploadException(e);
377         }
378         return uri.toString();
379     }
380 
381     /**
382      * Generate a js String, that can be written in a javascript expression. It's up to the caller to put the starting
383      * and ending quotes. The double quotes are replaced by simple quotes (to let simple quotes unchanged, as it may be
384      * used in common language). Thus, start and end of JS string should be with double quotes, when using the return of
385      * this function.
386      * 
387      * @param s
388      * @return The transformed string, that can be written in the output, into a javascript string. It doesn't contain
389      *         the starting and ending double quotes.
390      */
391     public String jsString(String s) {
392         String dollarReplacement = Matcher.quoteReplacement("\\$");
393         String singleQuoteReplacement = Matcher.quoteReplacement("\\'");
394         String linefeedReplacement = Matcher.quoteReplacement("\\n");
395 
396         if (s == null || s.equals("")) {
397             return "";
398         } else {
399             s = s.replaceAll("\\$", dollarReplacement);
400             s = s.replaceAll("\"", "'");
401             s = s.replaceAll("'", singleQuoteReplacement);
402             s = s.replaceAll("\n", linefeedReplacement);
403             s = s.replaceAll("\r", "");
404             return s;
405         }
406     }
407 }