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

Last change on this file since 34521 was 34521, checked in by kjdon, 3 years ago

the no_check_cert_arg is not used for some downloaders, eg z3950, so make sure its not null before trying to assign a value to it.

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