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

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

All the changes that were required to set up multiple proxy servers, one for HTTP, one for HTTPS, one for FTP. Still need to test on Windows

  • Property svn:keywords set to Author Date Id Revision
File size: 39.1 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 Properties proxy_urls = new Properties(); // proxy_urls for each of HTTP, HTTPS, FTP
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
574 boolean noCheckCertificate = Configuration.get("general.no_check_certificate", true);
575 Argument no_check_cert_arg = current_download.getArgument("no_check_certificate");
576 if(noCheckCertificate) {
577 no_check_cert_arg.setValue("true");
578 no_check_cert_arg.setAssigned(true);
579 } else {
580 no_check_cert_arg.setValue("false");
581 no_check_cert_arg.setAssigned(false); // only assigned Arguments have values
582 }
583
584 Argument arg_url = current_download.getArgument("url");
585
586 if(arg_url != null) { // it's null for z3950 and possibly for other downloaders
587 String url_str = arg_url.getValue();
588
589 // No longer following URL redirects, since some of this has been taken care of by wget
590 // For the rest, assume the user will get the URL right that they want to download from
591 /*
592 String redirect_url_str = getRedirectURL(url_str);
593
594 // only update the Argument and its GUI ArgumentControl if the URL
595 // has in fact changed
596 if(!url_str.equals(redirect_url_str)) {
597 arg_url.setValue(redirect_url_str);
598 updateArgument(arg_url, redirect_url_str);
599 }
600 */
601 }
602
603 getter.newDownloadJob((Download)download_map.get(mode) ,mode,proxy_urls);
604 }
605 }
606 }
607
608 /**
609 * The Java code here will retrieve the page at the given url. If the response code is
610 * a redirect, it will get the redirect url so that wget may be called with the proper url.
611 * This preprocessing of the URL is necessary because:
612 * Wget does not behave the way the browser does when faced with urls of the form
613 * http://www.englishhistory.net/tudor/citizens and if that page does not exist.
614 * The directory listing with a slash at the end (http://www.englishhistory.net/tudor/citizens/)
615 * does exist, however. In order to prevent wget from assuming that the root URL
616 * to traverse is http://www.englishhistory.net/tudor/ instead of the intended
617 * http://www.englishhistory.net/tudor/citizens/, we need give wget the redirect location
618 * that's returned when we initially make a request for http://www.englishhistory.net/tudor/citizens
619 * The proper url is sent back in the Location header, allowing us to bypass wget's
620 * unexpected behaviour.
621 * This method ensures that urls like http://www.nzdl.org/niupepa also continue to work:
622 * there is no http://www.nzdl.org/niupepa/ page, because this url actually redirects to an
623 * entirely different URL.
624 * @return the redirect url for the given url if any redirection is involved, or the
625 * url_str.
626 *
627 * Adding another useful URL on setting Java System Properties:
628 * https://stackoverflow.com/questions/12181843/using-java-to-download-files-from-a-https-url
629 */
630 private String getRedirectURL(String url_str) {
631 boolean noMoreRedirects = false;
632 boolean gotException = false;
633 final int TIMEOUT = 2 * 1000; // ms
634
635 HttpURLConnection connection = null;
636 if(url_str.startsWith("http:") || url_str.startsWith("https:")) { // only test http urls
637 try {
638 // URLConnectionManager class has special handling for https URLs,
639 // so you can control whether you want it to check an HTTPS site's security certificates for you or not
640 boolean noCheckCertificates = true;
641 connection = (HttpURLConnection)URLConnectionManager.getConnection(url_str, this.proxyObject, noCheckCertificates);
642
643 // don't let it automatically follow redirects, since we want to
644 // find out whether we are dealing with redirects in the first place
645 connection.setInstanceFollowRedirects(false);
646 // Connection timeout: if we can't connect, like if the proxy is wrong, don't wait forever
647 // Read timeout: *idle time* when retrieving a link. Don't wait forever to retrieve a page (e.g. if page doesn't exist)
648 // https://stackoverflow.com/questions/6829801/httpurlconnection-setconnecttimeout-has-no-effect
649 connection.setConnectTimeout(TIMEOUT);
650 connection.setReadTimeout(TIMEOUT);
651
652 // now check for whether we get a redirect response
653 // HTTP Codes 3xx are redirects, http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
654 int responseCode = connection.getResponseCode();
655 if(responseCode >= 300 && responseCode < 400) {
656 //String responseMsg = connection.getResponseMessage();
657
658 // Get the Location header since this specifies the new location of the resource
659 String location = connection.getHeaderField("Location");
660
661 // this becomes the url that wget should download from
662 url_str = location.trim();
663 } else {
664 noMoreRedirects = true;
665 }
666 connection.disconnect();
667 } catch(Exception e) {
668 gotException = true;
669 if(connection != null) {
670 connection.disconnect();
671 }
672 System.err.println("Checking redirection. Tried to connect to "
673 + url_str + ",\nbut got exception: " + e);
674 }
675 }
676
677 if(noMoreRedirects || gotException) {
678 return url_str;
679 }
680 else { // continue checking for whether the new URL redirects elsewhere again
681 return getRedirectURL(url_str);
682 }
683 }
684
685
686 /** For a string-based Argument whose value has changed, this method
687 * updates the GUI ArgumentControl's value correspondingly. */
688 private void updateArgument(Argument arg, String value) {
689 for(int i = 0; i < options_pane.getComponentCount(); i++) {
690 Component component = options_pane.getComponent(i);
691 if(component instanceof ArgumentControl) {
692 ArgumentControl control = (ArgumentControl)component;
693 if(control.getArgument() == arg) {
694 control.setValue(value);
695 control.repaint();
696 }
697 }
698 }
699 }
700
701 private boolean checkURL(boolean checkRequired){
702
703 if (!updateArguments(checkRequired)){
704 return false;
705 }
706
707 Download current_download = (Download)download_map.get(mode);
708 Argument arg_url = current_download.getArgument("url");
709
710 if (arg_url == null) return true;
711
712 String url_str = arg_url.getValue();
713 URL url = null;
714 try {
715 url = new URL(url_str);
716 }
717 catch(MalformedURLException error) {
718 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("Mirroring.Invalid_URL"), Dictionary.get("Mirroring.Invalid_URL_Title"), JOptionPane.ERROR_MESSAGE);
719 return false;
720 }
721
722 return true;
723 }
724
725
726 // SOME READING:
727 // https://superuser.com/questions/526710/how-to-set-http-proxy-address-for-wget-under-windows
728 // https://arstechnica.com/civis/viewtopic.php?f=10&t=1281165
729 // "http or https to an https proxy?"
730 // About "CONNECT": https://daniel.haxx.se/docs/sshproxy.html
731 // "You need an SSH client that can issue CONNECT requests through the company HTTP proxy." Seems to imply http? Also uses it for ftp.
732 // https://forum.ivorde.com/set-up-ftp-proxy-via-command-line-in-linux-freebsd-t19733.html sets ftp_proxy to a http url.
733 // So it's seems to be just whatever protocol the proxy server has. When the proxy supports all three protocols
734 // (apparently the common case as per page below describing firefox prefs), then wiki.archlinux sets all three ftp_proxy/http_proxy/https_proxy to the same.
735 // See https://forums.freebsd.org/threads/57378/
736 // "I believe that ftp(1) only uses HTTP-type proxies for fetching URLs. I.e. you can't do traditional open->cd->get interactive style of FTP with it via a HTTP proxy. If you do something like ftp ftp://ftp.example.com/path/file, it should work with your proxy setup. For traditional interactive FTP, you need to be directly talking to the remote server or using the less common FTP proxy/gate functionality."
737 // https://wiki.archlinux.org/index.php/Proxy_settings
738 // This does https_proxy = http_proxy, and explicitly prefixes "https://" to http_proxy.
739 // https://www.howtogeek.com/293213/how-to-configure-a-proxy-server-in-firefox/
740 /* You’ll usually want to click the “Use the proxy server for all protocols” option. Firefox will also use your HTTP proxy server for SSL-encrypted HTTPS connections and File Transfer Protocol (FTP) connections.
741Uncheck this box if you want to enter separate proxy servers for HTTP, HTTPS, and FTP connections. This isn’t common.
742If you’re configuring a SOCKS proxy, leave the HTTP Proxy, SSL Proxy, and FTP Proxy boxes empty. Enter the address of the SOCKS proxy into the “SOCKS Host” and its port into the “Port” box.
743 */
744 private boolean checkProxy(){
745
746 proxy_urls.clear();
747
748 Download current_download = (Download)download_map.get(mode);
749
750 Argument arg = current_download.getArgument("proxy_on");
751
752 if (arg == null) return true;
753
754 // Determine if we have to use a proxy.
755 if(Configuration.get("general.use_proxy", true)) {
756
757 boolean http_proxy_set = setProxyURLFor("HTTP");
758 boolean https_proxy_set = setProxyURLFor("HTTPS");
759 boolean ftp_proxy_set = setProxyURLFor("FTP");
760
761 if(proxy_urls.size() == 0 ||
762 (!http_proxy_set && !https_proxy_set && !ftp_proxy_set)) {
763
764 // if checkProxy() failed for all protocols
765 // OR if none of the proxies were setup by user, then turn off proxying
766 arg = current_download.getArgument("proxy_on");
767 arg.setValue("false");
768 arg.setAssigned(false);
769 proxy_urls.clear();
770 return false;
771 } else { // proxy details have been successfully set for at least one proxy protocol
772 return true;
773 }
774
775 } else { // if proxy_on was off
776 // unset proxy_on argument too
777 arg = current_download.getArgument("proxy_on");
778 arg.setValue("false");
779 arg.setAssigned(false);
780 }
781
782 return true;
783 }
784
785 private boolean setProxyURLFor(String protocol) {
786 String proxy_host = Configuration.getString("general."+protocol+"_proxy_host", true);
787 if(proxy_host.equals("")) { // no proxy details for this protocol
788 ///System.err.println("### general."+protocol+"proxy_host was empty");
789 return true;
790 }
791
792 String proxy_port = Configuration.getString("general."+protocol+"_proxy_port", true);
793 // Find out whether the user has already authenticated themselves
794
795 // remove the protocol prefix from proxy_host, and store it
796 String proxy_protocol = "";
797 int proxy_protocol_index = proxy_host.indexOf("://");
798 if(proxy_protocol_index != -1) {
799 proxy_protocol_index += "://".length();
800 proxy_protocol = proxy_host.substring(0, proxy_protocol_index);
801 proxy_host = proxy_host.substring(proxy_protocol_index);
802 } else { // no explicit protocol for proxy host specified,
803 // then set explicit protocol to be the same as the protocol param: http|https|ftp
804 proxy_protocol = protocol.toLowerCase()+"://";
805 }
806
807 String user_pass = "";
808 String address = proxy_host + ":" + proxy_port;
809
810 int count = 0;
811 // Only for wget, need to avoid a second automatic authentication popup (first asks
812 // the proxy authentication for wget, and the second will ask the same for the realm)
813 // Once the authentication has been reused, it will set the GAuthenticator state back to REGULAR
814 GAuthenticator.setMode(GAuthenticator.DOWNLOAD);
815 while(count < 3 && (user_pass = (String) GAuthenticator.authentications.get(address)) == null) {
816 Authenticator.requestPasswordAuthentication(proxy_host, null, Integer.parseInt(proxy_port), "http://", Dictionary.get("WGet.Prompt"), "HTTP");
817 count++;
818 }
819 if(count >= 3) {
820 return false;
821 }
822
823 // https://askubuntu.com/questions/664777/systemwide-proxy-settings-in-ubuntu
824 // http://www.rgagnon.com/javadetails/java-0085.html
825 // how-do-i-make-httpurlconnection-use-a-proxy
826 // https://stackoverflow.com/questions/8030908/how-to-check-if-proxy-is-working-in-java
827
828 //proxyObject = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxy_host, Integer.parseInt(proxy_port))); // proxyObject only used by getRedirectURL(), which we're no longer calling
829
830 Download current_download = (Download)download_map.get(mode);
831 Argument arg = current_download.getArgument("proxy_on");
832
833 if(user_pass.indexOf("@") != -1) {
834
835 arg.setValue("true"); // proxy_on argument
836 arg.setAssigned(true);
837
838 arg = current_download.getArgument(protocol.toLowerCase()+"_proxy_host");
839 arg.setValue(proxy_host);
840 arg.setAssigned(true);
841
842 arg = current_download.getArgument(protocol.toLowerCase()+"_proxy_port");
843 arg.setValue(proxy_port);
844 arg.setAssigned(true);
845
846 // Write the use proxy command - we don't do this anymore, instead we set environment
847 // variables as these do work for windows after all (Aug 2017). Hopefully these can't
848 // be spied on like the following can (using ps/task manager. If going back to the
849 // following, be aware: can't add protocol+user_name and protocol+user_password as
850 // accepted parameters in WgetDownload.pm for HTTP, HTTPS and FTP protocols. Only
851 // one set of username and password can be set for wget's --proxy-user and
852 // --proxy-password flags. No separate flags for each protocol.
853 /*if (Utility.isWindows()) {
854
855 arg = current_download.getArgument("user_name");
856 arg.setValue(user_pass.substring(0, user_pass.indexOf("@")));
857 arg.setAssigned(true);
858
859 arg = current_download.getArgument("user_password");
860 arg.setValue(user_pass.substring(user_pass.indexOf("@") + 1));
861 arg.setAssigned(true);
862 }
863
864 else{*/
865 String user_name = user_pass.substring(0, user_pass.indexOf("@"));
866 String user_pwd = user_pass.substring(user_pass.indexOf("@") + 1);
867 /*}*/
868
869 // construct proxy_url and prefix the stored proxy protocol to it
870 String proxy_url = proxy_protocol+user_name+":"+user_pwd+"@"+proxy_host+":"+proxy_port+"/";
871 proxy_urls.setProperty(protocol, proxy_url);
872 return true;
873 }
874 else{
875 return false;
876 }
877 }
878
879
880 private boolean old_checkProxy(){
881
882 String proxy_url = null; // ORIGINALLY A MEMBER VAR
883
884 Download current_download = (Download)download_map.get(mode);
885
886 Argument arg = current_download.getArgument("proxy_on");
887
888 if (arg == null) return true;
889
890 // Determine if we have to use a proxy.
891 if(Configuration.get("general.use_proxy", true)) {
892
893 String proxy_host = Configuration.getString("general.proxy_host", true);
894 String proxy_port = Configuration.getString("general.proxy_port", true);
895 // Find out whether the user has already authenticated themselves
896 String user_pass = "";
897 String address = proxy_host + ":" + proxy_port;
898
899 int count = 0;
900 // Only for wget, need to avoid a second automatic authentication popup (first asks
901 // the proxy authentication for wget, and the second will ask the same for the realm)
902 // Once the authentication has been reused, it will set the GAuthenticator state back to REGULAR
903 GAuthenticator.setMode(GAuthenticator.DOWNLOAD);
904 while(count < 3 && (user_pass = (String) GAuthenticator.authentications.get(address)) == null) {
905 Authenticator.requestPasswordAuthentication(proxy_host, null, Integer.parseInt(proxy_port), "http://", Dictionary.get("WGet.Prompt"), "HTTP");
906 count++;
907 }
908 if(count >= 3) {
909 return false;
910 }
911
912 // https://askubuntu.com/questions/664777/systemwide-proxy-settings-in-ubuntu
913 // http://www.rgagnon.com/javadetails/java-0085.html
914 // how-do-i-make-httpurlconnection-use-a-proxy
915 // https://stackoverflow.com/questions/8030908/how-to-check-if-proxy-is-working-in-java
916 proxyObject = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxy_host, Integer.parseInt(proxy_port)));
917
918 if(user_pass.indexOf("@") != -1) {
919
920 arg.setValue("true"); // proxy_on argument
921 arg.setAssigned(true);
922
923 arg = current_download.getArgument("proxy_host");
924 arg.setValue(proxy_host);
925 arg.setAssigned(true);
926
927 arg = current_download.getArgument("proxy_port");
928 arg.setValue(proxy_port);
929 arg.setAssigned(true);
930
931
932 // 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
933 /*if (Utility.isWindows()) {
934
935 arg = current_download.getArgument("user_name");
936 arg.setValue(user_pass.substring(0, user_pass.indexOf("@")));
937 arg.setAssigned(true);
938
939 arg = current_download.getArgument("user_password");
940 arg.setValue(user_pass.substring(user_pass.indexOf("@") + 1));
941 arg.setAssigned(true);
942 }
943
944 else{*/
945 String user_name = user_pass.substring(0, user_pass.indexOf("@"));
946 String user_pwd = user_pass.substring(user_pass.indexOf("@") + 1);
947 proxy_url = user_name+":"+user_pwd+"@"+proxy_host+":"+proxy_port+"/";
948
949 /*}*/
950
951 return true;
952 }
953 else{
954 // unset proxy_on argument
955 arg = current_download.getArgument("proxy_on");
956 arg.setValue("false");
957 arg.setAssigned(false);
958 return false;
959 }
960
961 } else {
962 // unset proxy_on argument
963 arg = current_download.getArgument("proxy_on");
964 arg.setValue("false");
965 arg.setAssigned(false);
966 }
967
968 return true;
969 }
970
971 /*
972 private class PreferencesButtonActionListener
973 implements ActionListener {
974 public void actionPerformed(ActionEvent event) {
975 new Preferences(Preferences.CONNECTION_PREFS);
976 }
977 }*/
978
979 private class InformationButtonActionListener
980 implements ActionListener {
981 public void actionPerformed(ActionEvent event) {
982 //turn off the check for find argument
983 Download current_download = (Download)download_map.get(mode);
984
985 if (!checkProxy() || !checkURL(false) )return;
986
987 if(server_info != null) {
988 server_info.dispose();
989 }
990
991 Argument arg_url = current_download.getArgument("url");
992 String str_url = "";
993 if( arg_url!= null && arg_url.isAssigned()) {
994 str_url = arg_url.getValue();
995 /*
996 String redirected_url = getRedirectURL(str_url); // work out the real URL
997 if(!str_url.equals(redirected_url)) {
998 arg_url.setValue(redirected_url);
999 }
1000 */
1001 }
1002
1003 boolean noCheckCertificate = Configuration.get("general.no_check_certificate", true);
1004 Argument no_check_cert_arg = current_download.getArgument("no_check_certificate");
1005 if(noCheckCertificate) {
1006 no_check_cert_arg.setValue("true");
1007 no_check_cert_arg.setAssigned(true);
1008 } else {
1009 no_check_cert_arg.setValue("false");
1010 no_check_cert_arg.setAssigned(false); // only assigned Arguments have values
1011 }
1012
1013 server_info = new ServerInfoDialog(str_url ,proxy_urls, mode,(Download)download_map.get(mode));
1014
1015 }
1016 }
1017
1018 private class PreferencesButtonActionListener
1019 implements ActionListener {
1020 public void actionPerformed(ActionEvent event) {
1021 new Preferences(Preferences.CONNECTION_PREFS);
1022 }
1023 }
1024}
Note: See TracBrowser for help on using the repository browser.