source: main/trunk/gli/src/org/greenstone/gatherer/gui/DownloadPane.java@ 31852

Last change on this file since 31852 was 31852, checked in by ak19, 7 years ago

Java code no longer works out what the ultimate URL to download from is, after all the redirects have been resolved. Wget seems to handle some of this, and for the rest, assume the user specificed URL is what the user is after

  • Property svn:keywords set to Author Date Id Revision
File size: 30.8 KB
Line 
1/**
2 *#########################################################################
3 *
4 * A component of the Gatherer application, part of the Greenstone digital
5 * library suite from the New Zealand Digital Library Project at the
6 * University of Waikato, New Zealand.
7 *
8 * <BR><BR>
9 *
10 * Author: John Thompson, Greenstone Digital Library, University of Waikato
11 *
12 * <BR><BR>
13 *
14 * Copyright (C) 1999 New Zealand Digital Library Project
15 *
16 * <BR><BR>
17 *
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
22 *
23 * <BR><BR>
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * <BR><BR>
31 *
32 * You should have received a copy of the GNU General Public License
33 * along with this program; if not, write to the Free Software
34 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
35 *########################################################################
36 */
37package org.greenstone.gatherer.gui;
38
39import java.awt.*;
40import java.awt.event.*;
41import java.io.*;
42import java.net.*;
43import java.util.*;
44import javax.swing.*;
45import javax.swing.border.*;
46import javax.swing.event.*;
47import javax.swing.tree.*;
48import org.greenstone.gatherer.Configuration;
49import org.greenstone.gatherer.DebugStream;
50import org.greenstone.gatherer.Dictionary;
51import org.greenstone.gatherer.Gatherer;
52import org.greenstone.gatherer.file.WorkspaceTree;
53import org.greenstone.gatherer.greenstone.LocalGreenstone;
54import org.greenstone.gatherer.util.SafeProcess;
55import org.greenstone.gatherer.util.StaticStrings;
56import org.greenstone.gatherer.util.Utility;
57import org.greenstone.gatherer.download.URLConnectionManager;
58import org.greenstone.gatherer.download.Download;
59import org.greenstone.gatherer.download.DownloadScrollPane;
60import org.greenstone.gatherer.download.ServerInfoDialog;
61import org.greenstone.gatherer.util.XMLTools;
62import org.greenstone.gatherer.cdm.*;
63import org.greenstone.gatherer.gui.*;
64import org.w3c.dom.*;
65import org.xml.sax.*;
66import org.greenstone.gatherer.GAuthenticator;
67
68/**
69 * @author John Thompson, Greenstone Digital Library, University of Waikato
70 * @version 2.1
71 */
72public class DownloadPane
73 extends JPanel {
74
75 static final private Dimension LABEL_SIZE = new Dimension(225, 25);
76 static final private Dimension TREE_SIZE = new Dimension(150, 500);
77 //static final private String CONTENTS[] = { "DOWNLOAD.MODE.WebDownload", "DOWNLOAD.MODE.MediaWikiDownload", "DOWNLOAD.MODE.OAIDownload", "DOWNLOAD.MODE.ZDownload" , "DOWNLOAD.MODE.SRWDownload"};
78 private String CONTENTS[] = null;
79
80 private boolean download_button_enabled = false;
81 private boolean ready = false;
82
83 private JPanel options_pane;
84 // TODO should use Vector to store all loaded downloads!!
85
86 private DesignTree tree;
87 private HashMap download_map;
88 private ServerInfoDialog server_info;
89 private JScrollPane list_scroll;
90 private DownloadScrollPane getter;
91 private String mode = null;
92 private TreePath previous_path;
93 private String proxy_url = "";
94 private Proxy proxyObject = null;
95
96 /** Main System code */
97 public DownloadPane() {
98 super();
99 JScrollPane scrol_tmp;
100 this.setComponentOrientation(Dictionary.getOrientation());
101 // TODO: Download the WDownload and the download panel fixed!!
102 getter = new DownloadScrollPane();
103 getter.start();
104 list_scroll = getter.getDownloadJobList();
105 list_scroll.setComponentOrientation(Dictionary.getOrientation());
106
107 // TODO should use Vector to store all loaded downloads!!
108 String lang = Configuration.getLanguage();
109 download_map = new HashMap();
110
111 // run downloadinfo.pl -describeall, load the downloaders into the download_map,
112 // and get back their list of names, which are of the form "<downloader>Download".
113 // Store these names in the CONTENTS[] array as "DOWNLOAD.MODE.<downloader>Download",
114 // with z3950 as a minor exception: DOWNLOAD.MODE.ZDownload.
115 ArrayList<String> downloaderNamesList = loadDownloadersInfo(lang);
116 int size = downloaderNamesList.size();
117 CONTENTS = new String[size];
118 for(int i = 0; i < size; i++) {
119 String downloadName = downloaderNamesList.get(i); // e.g. "WebDownload"
120 CONTENTS[i] = "DOWNLOAD.MODE."+downloadName.replace("3950", ""); // A special case is Z3950Download,
121 // which has to be stored in CONTENTS array as DOWNLOAD.MODE.ZDownload
122 }
123
124 // Creation
125 tree = new DesignTree();
126 tree.setComponentOrientation(Dictionary.getOrientation());
127 options_pane = new JPanel();
128 options_pane.setComponentOrientation(Dictionary.getOrientation());
129
130 JButton clear_cache_button = new GLIButton(Dictionary.get("Mirroring.ClearCache"), Dictionary.get("Mirroring.ClearCache_Tooltip"));
131 clear_cache_button.setEnabled(true);
132 clear_cache_button.setMnemonic(KeyEvent.VK_C);
133
134 JButton download_button = new GLIButton(Dictionary.get("Mirroring.Download"), Dictionary.get("Mirroring.Download_Tooltip"));
135 download_button.setEnabled(true);
136 download_button.setMnemonic(KeyEvent.VK_D);
137
138 JButton information_button = new GLIButton(Dictionary.get("Download.ServerInformation"), Dictionary.get("Download.ServerInformation_Tooltip"));
139 information_button.setEnabled(true);
140 information_button.setMnemonic(KeyEvent.VK_S);
141
142
143 JButton preferences_button = new GLIButton(Dictionary.get("Mirroring.Preferences"), Dictionary.get("Mirroring.Preferences_Tooltip"));
144 preferences_button.setEnabled(true);
145 preferences_button.setMnemonic(KeyEvent.VK_P);
146
147 // Connect
148 clear_cache_button.addActionListener(new ClearCacheListener());
149 download_button.addActionListener(new DownloadButtonListener());
150 preferences_button.addActionListener(new PreferencesButtonActionListener());
151 information_button.addActionListener(new InformationButtonActionListener());
152 tree.addTreeSelectionListener(new TreeListener());
153
154 // Add to Panel
155 JPanel button_pane = new JPanel();
156 button_pane.setComponentOrientation(Dictionary.getOrientation());
157 button_pane.setLayout(new GridLayout(1,4)); // GridLayout so button pane resizes with window-width
158 button_pane.setBorder(BorderFactory.createEtchedBorder());
159 button_pane.add(clear_cache_button);
160 button_pane.add(download_button);
161 button_pane.add(information_button);
162 button_pane.add(preferences_button);
163
164 JPanel tree_pane = new JPanel();
165 tree_pane.setComponentOrientation(Dictionary.getOrientation());
166 tree_pane.setLayout(new BorderLayout());
167 scrol_tmp = new JScrollPane(tree);
168 scrol_tmp.setComponentOrientation(Dictionary.getOrientation());
169 tree_pane.add(scrol_tmp, BorderLayout.CENTER);
170 tree_pane.setPreferredSize(TREE_SIZE);
171
172
173 Color colour_two = Configuration.getColor("coloring.collection_tree_background", false);
174 options_pane.setBackground(colour_two);
175 options_pane.setBorder(BorderFactory.createEtchedBorder());
176
177
178 JScrollPane options_scroll_pane = new JScrollPane(options_pane);
179 options_scroll_pane.setComponentOrientation(Dictionary.getOrientation());
180 JSplitPane mode_pane = new JSplitPane();
181 mode_pane.setComponentOrientation(Dictionary.getOrientation());
182 mode_pane.setBorder(BorderFactory.createEmptyBorder(0,0,0,0));
183 if (Dictionary.getOrientation().isLeftToRight()){
184 mode_pane.add(tree_pane,JSplitPane.LEFT);
185 mode_pane.add(options_scroll_pane,JSplitPane.RIGHT);
186 mode_pane.setDividerLocation(TREE_SIZE.width);
187 }else{
188 mode_pane.add(tree_pane,JSplitPane.RIGHT);
189 mode_pane.add(options_scroll_pane,JSplitPane.LEFT);
190 mode_pane.setDividerLocation(1-TREE_SIZE.width);
191 }
192
193
194 JPanel edit_pane = new JPanel();
195 edit_pane.setComponentOrientation(Dictionary.getOrientation());
196 edit_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(2,0,0,0), BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Download Setting"), BorderFactory.createEmptyBorder(2,2,2,2))));
197 edit_pane.setLayout(new BorderLayout());
198 edit_pane.add(mode_pane,BorderLayout.CENTER);
199 edit_pane.add(button_pane,BorderLayout.PAGE_END);
200
201 // Add to "this"
202 setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
203 setLayout(new GridLayout(2,1));
204 add(edit_pane);
205 add(list_scroll);
206
207 //set the mode to the first downloader in the list
208 mode = convertCONTENTStoMode(CONTENTS[0]); // e.g. Web
209 generateOptions(options_pane,(Download)download_map.get(mode));
210 previous_path = tree.getSelectionPath();
211 }
212
213 /** System Utilities */
214 public void modeChanged(int gli_mode) {
215 // do nothing at this stage - should we be renewing download options??
216 }
217
218 private void addHeader(String name, Color color, JPanel target_pane) {
219 JPanel header = new JPanel();
220 header.setComponentOrientation(Dictionary.getOrientation());
221 header.setBackground(color);
222 JPanel inner_pane = new JPanel();
223 inner_pane.setComponentOrientation(Dictionary.getOrientation());
224 inner_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(5,5,5,5), BorderFactory.createRaisedBevelBorder()));
225 inner_pane.setBackground(color);
226 JLabel header_label = new JLabel("<html><strong>" + name + "</strong></html>");
227 header_label.setComponentOrientation(Dictionary.getOrientation());
228 header_label.setBackground(Configuration.getColor("coloring.collection_heading_background", false));
229 header_label.setHorizontalAlignment(JLabel.CENTER);
230 header_label.setOpaque(true);
231
232 // Layout
233 inner_pane.setLayout(new BorderLayout());
234 inner_pane.add(header_label, BorderLayout.CENTER);
235
236 header.setLayout(new BorderLayout());
237 header.add(inner_pane, BorderLayout.CENTER);
238 target_pane.add(header);
239 }
240
241 /** Supporting Functions */
242 private ArrayList<String> loadDownloadersInfo(String lang) {
243 Document document = null;
244
245 try {
246 if (Gatherer.isGsdlRemote) {
247 String output = Gatherer.remoteGreenstoneServer.getScriptOptions("downloadinfo.pl", "&describeall");
248 Reader reader = new StringReader(output);
249 document = XMLTools.parseXML(reader);
250 }
251 else {
252 ArrayList args_list = new ArrayList();
253 String args[] = null;
254 if(Configuration.perl_path != null) {
255 args_list.add(Configuration.perl_path);
256 } else if(Utility.isWindows()) {
257 args_list.add("Perl.exe");
258 } else {
259 args_list.add("perl");
260 }
261 args_list.add("-S");
262 args_list.add(LocalGreenstone.getBinScriptDirectoryPath()+"downloadinfo.pl");
263 args_list.add("-describeall");
264 args_list.add("-xml");
265 args_list.add("-language");
266 args_list.add(lang);
267
268 // Create the process.
269 args = (String []) args_list.toArray(new String[0]);
270
271 SafeProcess process = new SafeProcess(args);
272 DebugStream.println("Getting Download Info: "+args_list);
273
274 // run the SafeProcess
275 int exitVal = process.runProcess();
276 if(exitVal != 0) {
277 throw new Exception("*** Error running Download Info process, process exited with: "
278 + exitVal);
279 }
280
281 // get the result and process it.
282 // This time we expect XML to have come out of the process std error stream.
283 String errStreamOutput = process.getStdError();
284 ///System.err.println("*********\nDownload Pane data, got:\n" + errStreamOutput + "\n**********\n");
285 StringReader xmlStrReader = new StringReader(errStreamOutput);
286 document = XMLTools.parseXML(xmlStrReader);
287 xmlStrReader.close();
288
289 }
290
291
292 }
293 catch (Exception error) {
294 System.err.println("Failed when trying to parse downloadinfo.pl -describeall");
295 error.printStackTrace();
296 }
297
298 if(document != null) {
299 return parseXML(document.getDocumentElement());
300 }
301
302 return null;
303 }
304
305 private ArrayList<String> parseXML(Node root) {
306 ArrayList<String> downloaders = null;
307 Element downloadList = (Element)root;
308 int length = -1;
309 if(downloadList.hasAttribute("length")) {
310 length = Integer.parseInt(downloadList.getAttribute("length"));
311 downloaders = new ArrayList<String>(length);
312
313 for (Node node = downloadList.getFirstChild(); node != null; node = node.getNextSibling()) {
314 // goes through each <DownloadInfo> of describeAll
315
316 String download_name = null;
317
318 for(Node infoNode = node.getFirstChild();
319 infoNode != null; infoNode = infoNode.getNextSibling()) {
320
321 String node_name = infoNode.getNodeName();
322 if(node_name.equalsIgnoreCase("Name")) { // <Name>WebDownload</Name>
323 download_name = XMLTools.getValue(infoNode); // e.g. WebDownload
324 }
325
326 // At this top level of <DownloadInfo> elements,
327 // skip all the downloaders that are Abstract, as these are pure superclasses
328 else if(node_name.equalsIgnoreCase("Abstract")) {
329 String isAbstract = XMLTools.getValue(infoNode);
330
331 if(isAbstract.equalsIgnoreCase("no") && download_name != null) {
332 downloaders.add(download_name);
333 Download downloader = parseDownloadInfoXML(node); // parse the <DownloadInfo> node properly
334 // now embedded references to abstract superclasses (embedded <DownloadInfo> nodes)
335 // will be handled
336
337 String shortName = download_name.replace("Download", ""); // e.g. "Web"
338 download_map.put(shortName, downloader);
339 }
340 }
341 }
342 }
343 }
344
345 return downloaders;
346 }
347
348 private Download parseDownloadInfoXML(Node root) {
349
350 Download download = new Download();
351 String node_name = null;
352 for (Node node = root.getFirstChild(); node != null; node = node.getNextSibling()) {
353 node_name = node.getNodeName();
354 if(node_name.equalsIgnoreCase("Name")) {
355 String name = XMLTools.getValue(node);
356 download.setName(name);
357 }
358 else if (node_name.equalsIgnoreCase("Desc")) {
359 download.setDescription(XMLTools.getValue(node));
360 }
361 else if (node_name.equalsIgnoreCase("Abstract")) {
362 download.setIsAbstract(XMLTools.getValue(node).equalsIgnoreCase(StaticStrings.YES_STR));
363 }
364 else if(node_name.equalsIgnoreCase("Arguments")) {
365 for(Node arg = node.getFirstChild(); arg != null; arg = arg.getNextSibling()) {
366 node_name = arg.getNodeName();
367 if(node_name.equalsIgnoreCase("Option")) {
368 Argument argument = new Argument((Element)arg);
369 argument.parseXML((Element)arg);
370 argument.setValue(argument.getDefaultValue());
371 download.addArgument(argument);
372 }
373
374 }
375 }
376 else if(node_name.equalsIgnoreCase("DownloadInfo")) {
377 Download super_download = parseDownloadInfoXML(node);
378 download.setSuper(super_download);
379 }
380 }
381
382 if(download.getName() != null) {
383 return download;
384 }
385 return null;
386 }
387
388 /** Update the previous setup */
389 private boolean updateArguments(boolean checkRequired)
390 {
391 boolean cont = true;
392 for(int i = 0; i < options_pane.getComponentCount(); i++) {
393
394 Component component = options_pane.getComponent(i);
395 if(component instanceof ArgumentControl) {
396 cont = cont && ((ArgumentControl)component).updateArgument(checkRequired);
397 }
398 }
399
400 if(cont){return true; }
401
402 return false;
403 }
404
405 /** Generate Controls for Options */
406 /* at some stage we should think about which options should be shown for
407 * different modes. Currently, always show all options (unless hidden)*/
408 private void generateOptions(JPanel options_pane, ArgumentContainer data) {
409 options_pane.removeAll();
410 /** Create the current option panel */
411
412 ArrayList arguments = data.getArguments(true, false);
413 int mode = Configuration.getMode();
414 ArrayList added_arguments = new ArrayList();
415
416 for(int i = 0; i < arguments.size(); i++) {
417 Argument argument = (Argument) arguments.get(i);
418
419 if (argument.isHiddenGLI()) continue;
420 ArgumentControl argument_control = new ArgumentControl(argument,false,null);
421 added_arguments.add(argument_control);
422 }
423
424
425 options_pane.setLayout(new GridLayout(added_arguments.size(),1));
426 for(int i = 0; i < added_arguments.size(); i++) {
427 options_pane.add((ArgumentControl)added_arguments.get(i));
428
429 }
430 }
431
432 /** Behaviour Functions */
433 public void afterDisplay() {
434 ready = true;
435 }
436
437
438 public void gainFocus() {
439 if(!ready) {
440 return;
441 }
442
443 // It is also a good time to determine if the download should be enabled - ie if its allowed to be enabled and a valid URL is present in the field.
444 download_button_enabled = true;
445 //download_button.setEnabled(download_button_enabled);
446 }
447
448
449
450 public void refresh(int refresh_reason, boolean ready)
451 {
452 }
453
454 /** Private classes */
455 /** This tree provides a 'table of contents' for the various components of the design process (collection configuration in more technical terms). */
456 private class DesignTree extends JTree {
457
458 private DesignNode root = null;
459 /** Constructor. Automatically generates all of the nodes, in the order of CONTENTS. */
460 public DesignTree() {
461 super();
462 this.setComponentOrientation(Dictionary.getOrientation());
463 resetModel(Configuration.getMode());
464 expandRow(0);
465 setRootVisible(false);
466 setSelectionRow(0);
467 }
468
469 /** Reset the model used by the design page contents tree. This is necessary to hide the partitions entry when in lower detail modes
470 * @param mode the current detail mode as an int
471 */
472 public void resetModel(int mode) {
473 root = new DesignNode("DOWNLOAD.MODE.Root");
474 // Now add the design categories.
475 for(int i = 0; i < CONTENTS.length; i++) {
476 root.add(new DesignNode(CONTENTS[i]));
477 }
478 this.setModel(new DefaultTreeModel(root));
479 updateUI();
480 }
481 /** Set the current view to the one specified.
482 * @param type the name of the desired view as a String
483 */
484 public void setSelectedView(String type) {
485 type = Dictionary.get(type);
486 for(int i = 0; i < root.getChildCount(); i++) {
487 DesignNode child = (DesignNode) root.getChildAt(i);
488 if(child.toString().equals(type)) {
489 TreePath path = new TreePath(child.getPath());
490 setSelectionPath(path);
491 }
492 }
493 }
494 }
495
496 /** A tree node that retains a reference to one of the possible design sub-views relating to the different sub-managers. */
497 private class DesignNode extends DefaultMutableTreeNode {
498 /** Constructor.
499 * @param object The <strong>Object</strong> assigned to this node.
500 */
501 public DesignNode(String object) {
502 super(object);
503 }
504 /** Retrieve a textual representation of the object.
505 * @return a String
506 */
507 public String toString() {
508 // return Dictionary.get("CDM.GUI." + (String)getUserObject());
509 return Dictionary.get((String) getUserObject());
510 }
511 }
512
513 /** Listens for selection changes in the 'contents' tree, and switches to the appropriate view. */
514 private class TreeListener
515 implements TreeSelectionListener {
516 /** Called whenever the selection changes, we must update the view so it matches the node selected.
517 * @param event A <strong>TreeSelectionEvent</strong> containing more information about the tree selection.
518 * @see org.greenstone.gatherer.cdm.ClassifierManager
519 * @see org.greenstone.gatherer.cdm.CollectionDesignManager
520 * @see org.greenstone.gatherer.cdm.CollectionMetaManager
521 * @see org.greenstone.gatherer.cdm.FormatManager
522 * @see org.greenstone.gatherer.cdm.LanguageManager
523 * @see org.greenstone.gatherer.cdm.MetadataSetView
524 * @see org.greenstone.gatherer.cdm.SubcollectionManager
525 * @see org.greenstone.gatherer.cdm.TranslationView
526 * @see org.greenstone.gatherer.cdm.PlugInManager
527 */
528 public void valueChanged(TreeSelectionEvent event) {
529 if(!tree.isSelectionEmpty()) {
530 TreePath path = tree.getSelectionPath();
531
532 DesignNode node = (DesignNode)path.getLastPathComponent();
533 String type = (String)node.getUserObject();
534 Gatherer.g_man.wait(true);
535
536 // type has the value DOWNLOAD.MODE.<downloader>Download,
537 // mode should then be of the form <downloader>
538 mode = convertCONTENTStoMode(type);
539 generateOptions(options_pane,(Download)download_map.get(mode));
540
541 tree.setSelectionPath(path);
542 previous_path = path;
543 repaint();
544
545 Gatherer.g_man.wait(false);
546 }
547 }
548 }
549
550 private String convertCONTENTStoMode(String content) {
551 return content.replace("DOWNLOAD.MODE.", "").replace("ZDownload", "Z3950").replace("Download", "");
552 }
553
554 private class ClearCacheListener
555 implements ActionListener {
556 public void actionPerformed(ActionEvent event) {
557 // Retrieve the cache folder and delete it.
558 Utility.delete(Utility.getCacheDir());
559 // ...and refresh the node in the workspace tree to show it's all gone
560 Gatherer.g_man.refreshWorkspaceTree(WorkspaceTree.DOWNLOADED_FILES_CHANGED);
561 }
562 }
563
564 private class DownloadButtonListener
565 implements ActionListener {
566 public void actionPerformed(ActionEvent event) {
567
568 if(checkURL(true) && checkProxy() == true) {
569
570 // Proxy settings are now set. Check that the url is not a redirect, else get
571 // redirect url (we do this step in order to avoid some unintuitive behaviour from wget)
572 Download current_download = (Download)download_map.get(mode);
573 Argument arg_url = current_download.getArgument("url");
574
575 if(arg_url != null) { // it's null for z3950 and possibly for other downloaders
576 String url_str = arg_url.getValue();
577
578 // No longer following URL redirects, since some of this has been taken care of by wget
579 // For the rest, assume the user will get the URL right that they want to download from
580 /*
581 String redirect_url_str = getRedirectURL(url_str);
582
583 // only update the Argument and its GUI ArgumentControl if the URL
584 // has in fact changed
585 if(!url_str.equals(redirect_url_str)) {
586 arg_url.setValue(redirect_url_str);
587 updateArgument(arg_url, redirect_url_str);
588 }
589 */
590 }
591
592 getter.newDownloadJob((Download)download_map.get(mode) ,mode,proxy_url);
593 }
594 }
595 }
596
597 /**
598 * The Java code here will retrieve the page at the given url. If the response code is
599 * a redirect, it will get the redirect url so that wget may be called with the proper url.
600 * This preprocessing of the URL is necessary because:
601 * Wget does not behave the way the browser does when faced with urls of the form
602 * http://www.englishhistory.net/tudor/citizens and if that page does not exist.
603 * The directory listing with a slash at the end (http://www.englishhistory.net/tudor/citizens/)
604 * does exist, however. In order to prevent wget from assuming that the root URL
605 * to traverse is http://www.englishhistory.net/tudor/ instead of the intended
606 * http://www.englishhistory.net/tudor/citizens/, we need give wget the redirect location
607 * that's returned when we initially make a request for http://www.englishhistory.net/tudor/citizens
608 * The proper url is sent back in the Location header, allowing us to bypass wget's
609 * unexpected behaviour.
610 * This method ensures that urls like http://www.nzdl.org/niupepa also continue to work:
611 * there is no http://www.nzdl.org/niupepa/ page, because this url actually redirects to an
612 * entirely different URL.
613 * @return the redirect url for the given url if any redirection is involved, or the
614 * url_str.
615 *
616 * Adding another useful URL on setting Java System Properties:
617 * https://stackoverflow.com/questions/12181843/using-java-to-download-files-from-a-https-url
618 */
619 private String getRedirectURL(String url_str) {
620 boolean noMoreRedirects = false;
621 boolean gotException = false;
622 final int TIMEOUT = 2 * 1000; // ms
623
624 HttpURLConnection connection = null;
625 if(url_str.startsWith("http:") || url_str.startsWith("https:")) { // only test http urls
626 try {
627 // URLConnectionManager class has special handling for https URLs,
628 // so you can control whether you want it to check an HTTPS site's security certificates for you or not
629 boolean noCheckCertificates = true;
630 connection = (HttpURLConnection)URLConnectionManager.getConnection(url_str, this.proxyObject, noCheckCertificates);
631
632 // don't let it automatically follow redirects, since we want to
633 // find out whether we are dealing with redirects in the first place
634 connection.setInstanceFollowRedirects(false);
635 // Connection timeout: if we can't connect, like if the proxy is wrong, don't wait forever
636 // Read timeout: *idle time* when retrieving a link. Don't wait forever to retrieve a page (e.g. if page doesn't exist)
637 // https://stackoverflow.com/questions/6829801/httpurlconnection-setconnecttimeout-has-no-effect
638 connection.setConnectTimeout(TIMEOUT);
639 connection.setReadTimeout(TIMEOUT);
640
641 // now check for whether we get a redirect response
642 // HTTP Codes 3xx are redirects, http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
643 int responseCode = connection.getResponseCode();
644 if(responseCode >= 300 && responseCode < 400) {
645 //String responseMsg = connection.getResponseMessage();
646
647 // Get the Location header since this specifies the new location of the resource
648 String location = connection.getHeaderField("Location");
649
650 // this becomes the url that wget should download from
651 url_str = location.trim();
652 } else {
653 noMoreRedirects = true;
654 }
655 connection.disconnect();
656 } catch(Exception e) {
657 gotException = true;
658 if(connection != null) {
659 connection.disconnect();
660 }
661 System.err.println("Checking redirection. Tried to connect to "
662 + url_str + ",\nbut got exception: " + e);
663 }
664 }
665
666 if(noMoreRedirects || gotException) {
667 return url_str;
668 }
669 else { // continue checking for whether the new URL redirects elsewhere again
670 return getRedirectURL(url_str);
671 }
672 }
673
674
675 /** For a string-based Argument whose value has changed, this method
676 * updates the GUI ArgumentControl's value correspondingly. */
677 private void updateArgument(Argument arg, String value) {
678 for(int i = 0; i < options_pane.getComponentCount(); i++) {
679 Component component = options_pane.getComponent(i);
680 if(component instanceof ArgumentControl) {
681 ArgumentControl control = (ArgumentControl)component;
682 if(control.getArgument() == arg) {
683 control.setValue(value);
684 control.repaint();
685 }
686 }
687 }
688 }
689
690 private boolean checkURL(boolean checkRequired){
691
692 if (!updateArguments(checkRequired)){
693 return false;
694 }
695
696 Download current_download = (Download)download_map.get(mode);
697 Argument arg_url = current_download.getArgument("url");
698
699 if (arg_url == null) return true;
700
701 String url_str = arg_url.getValue();
702 URL url = null;
703 try {
704 url = new URL(url_str);
705 }
706 catch(MalformedURLException error) {
707 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("Mirroring.Invalid_URL"), Dictionary.get("Mirroring.Invalid_URL_Title"), JOptionPane.ERROR_MESSAGE);
708 return false;
709 }
710
711 return true;
712 }
713
714 // TODO: Still need to read up on and test how to set wget proxy on windows
715 // Need to be on a windows machine that requires proxy, and get wget to work on cmdline
716 // If that works, then need to check https URLS also work.
717 // See https://superuser.com/questions/526710/how-to-set-http-proxy-address-for-wget-under-windows
718 private boolean checkProxy(){
719
720 proxy_url = null;
721
722 Download current_download = (Download)download_map.get(mode);
723
724 Argument arg = current_download.getArgument("proxy_on");
725
726 if (arg == null) return true;
727
728 // Determine if we have to use a proxy.
729 if(Configuration.get("general.use_proxy", true)) {
730
731 String proxy_host = Configuration.getString("general.proxy_host", true);
732 String proxy_port = Configuration.getString("general.proxy_port", true);
733 // Find out whether the user has already authenticated themselves
734 String user_pass = "";
735 String address = proxy_host + ":" + proxy_port;
736
737 int count = 0;
738 // Only for wget, need to avoid a second automatic authentication popup (first asks
739 // the proxy authentication for wget, and the second will ask the same for the realm)
740 // Once the authentication has been reused, it will set the GAuthenticator state back to REGULAR
741 GAuthenticator.setMode(GAuthenticator.DOWNLOAD);
742 while(count < 3 && (user_pass = (String) GAuthenticator.authentications.get(address)) == null) {
743 Authenticator.requestPasswordAuthentication(proxy_host, null, Integer.parseInt(proxy_port), "http://", Dictionary.get("WGet.Prompt"), "HTTP");
744 count++;
745 }
746 if(count >= 3) {
747 return false;
748 }
749
750 // https://askubuntu.com/questions/664777/systemwide-proxy-settings-in-ubuntu
751 // http://www.rgagnon.com/javadetails/java-0085.html
752 // how-do-i-make-httpurlconnection-use-a-proxy
753 // https://stackoverflow.com/questions/8030908/how-to-check-if-proxy-is-working-in-java
754 proxyObject = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxy_host, Integer.parseInt(proxy_port)));
755
756 if(user_pass.indexOf("@") != -1) {
757
758 // Write the use proxy command - we don't do this anymore, instead we set environment variables - hopefully these can't be spied on like the following can (using ps) - actually the environment stuff didn't work for windows, so lets go back to this
759 if (Utility.isWindows()) {
760
761 arg.setValue("true");
762 arg.setAssigned(true);
763
764 arg = current_download.getArgument("proxy_host");
765 arg.setValue(proxy_host);
766 arg.setAssigned(true);
767
768 arg = current_download.getArgument("proxy_port");
769 arg.setValue(proxy_port);
770 arg.setAssigned(true);
771
772 arg = current_download.getArgument("user_name");
773 arg.setValue(user_pass.substring(0, user_pass.indexOf("@")));
774 arg.setAssigned(true);
775
776 arg = current_download.getArgument("user_password");
777 arg.setValue(user_pass.substring(user_pass.indexOf("@") + 1));
778 arg.setAssigned(true);
779 }
780 else{
781 String user_name = user_pass.substring(0, user_pass.indexOf("@"));
782 String user_pwd = user_pass.substring(user_pass.indexOf("@") + 1);
783 proxy_url = user_name+":"+user_pwd+"@"+proxy_host+":"+proxy_port+"/";
784
785 }
786
787 return true;
788 }
789 else{
790 return false;
791 }
792
793 }
794
795 return true;
796 }
797
798 /*
799 private class PreferencesButtonActionListener
800 implements ActionListener {
801 public void actionPerformed(ActionEvent event) {
802 new Preferences(Preferences.CONNECTION_PREFS);
803 }
804 }*/
805
806 private class InformationButtonActionListener
807 implements ActionListener {
808 public void actionPerformed(ActionEvent event) {
809 //turn off the check for find argument
810 Download current_download = (Download)download_map.get(mode);
811
812 if (!checkProxy() || !checkURL(false) )return;
813
814 if(server_info != null) {
815 server_info.dispose();
816 }
817
818 Argument arg_url = current_download.getArgument("url");
819 String str_url = "";
820 if( arg_url!= null && arg_url.isAssigned()) {
821 str_url = arg_url.getValue();
822 }
823
824 //str_url = getRedirectURL(str_url); // work out the real URL
825
826 server_info = new ServerInfoDialog(str_url ,proxy_url, mode,(Download)download_map.get(mode));
827
828 }
829 }
830
831 private class PreferencesButtonActionListener
832 implements ActionListener {
833 public void actionPerformed(ActionEvent event) {
834 new Preferences(Preferences.CONNECTION_PREFS);
835 }
836 }
837}
Note: See TracBrowser for help on using the repository browser.