001package arcommon.net;
002
003import java.awt.Desktop;
004import java.awt.Dialog;
005import java.awt.Frame;
006import java.awt.KeyboardFocusManager;
007import java.awt.Window;
008import java.awt.event.ActionEvent;
009import java.awt.event.ActionListener;
010import java.awt.event.WindowAdapter;
011import java.awt.event.WindowEvent;
012import java.io.BufferedReader;
013import java.io.DataInputStream;
014import java.io.File;
015import java.io.FileOutputStream;
016import java.io.IOException;
017import java.io.InputStreamReader;
018import java.io.StringReader;
019import java.io.UnsupportedEncodingException;
020import java.net.HttpURLConnection;
021import java.net.IDN;
022import java.net.MalformedURLException;
023import java.net.URI;
024import java.net.URISyntaxException;
025import java.net.URL;
026import java.net.URLDecoder;
027import java.net.URLEncoder;
028import java.security.NoSuchAlgorithmException;
029import java.security.cert.CertificateException;
030import java.security.cert.X509Certificate;
031
032import javax.crypto.Cipher;
033import javax.net.ssl.HttpsURLConnection;
034import javax.net.ssl.SSLContext;
035import javax.net.ssl.TrustManager;
036import javax.net.ssl.X509TrustManager;
037import javax.swing.JDialog;
038import javax.swing.JLabel;
039import javax.swing.SwingUtilities;
040import javax.swing.text.html.HTMLDocument;
041import javax.swing.text.html.HTMLEditorKit;
042
043import arcommon.ArrayUtil;
044import arcommon.FileUtil;
045import arcommon.MyTimer;
046import arcommon.StringUtil;
047import arcommon.progress.ProgressState;
048import arcommon.progress.ProgressThreadData;
049import arcommon.ui.other.GUIUtil;
050
051public class NetUtil {
052    public static final String EMAIL_PROTOCOL = "mailto:";
053    public static final String PHONE_PROTOCOL = "tel:";
054
055    // WARNING: This seems to break stuff
056/*    public static void setDefaultHostnameVerifier() {
057        HostnameVerifier hostnameVerifier = new HostnameVerifier() {
058            public boolean verify(String urlHostName, SSLSession session) {
059                System.out.println("Warning: URL Host: \"" + urlHostName + "\" vs \"" + session.getPeerHost() + "\"");
060                return true;
061            }
062        };
063        HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);
064    }*/
065    
066    public static void trustAllHttpsCertificates() {
067        try {
068            // Create a trust manager that does not validate certificate chains:
069            TrustManager[] trustAllCertificates = new TrustManager[1];
070    
071            TrustManager trustManager = new MyTrustManager();
072    
073            trustAllCertificates[0] = trustManager;
074    
075            SSLContext sslContext = SSLContext.getInstance("SSL");
076    
077            sslContext.init(null, trustAllCertificates, null);
078    
079            HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
080        } catch (Exception e) {
081            throw new RuntimeException(e);
082        }
083    }
084    
085    public static void checkUnlimitedCipher() {
086        try {
087            int maximumKeyLength = Cipher.getMaxAllowedKeyLength("AES");
088            if (maximumKeyLength <= 128) {
089                throw new RuntimeException("maximumKeyLength <= 128 " + maximumKeyLength);
090            }
091        } catch (NoSuchAlgorithmException e) {
092            throw new RuntimeException(e);
093        }
094    }
095    
096    private static class MyTrustManager implements TrustManager, X509TrustManager {
097        public X509Certificate[] getAcceptedIssuers() {
098            return null;
099        }
100
101        public void checkServerTrusted(X509Certificate[] certificates, String authType) throws CertificateException {
102            return ;
103        }
104
105        public void checkClientTrusted(X509Certificate[] certificates, String authType) throws CertificateException {
106            return ;
107        }
108    }
109
110    private static JDialog jDialog;
111    private static ActionListener actionListener;
112    
113    private static void removeTimerAndDialog() {
114        if (jDialog != null) {
115            jDialog.dispose();
116            jDialog = null;
117        }
118
119        if (actionListener != null) {
120            MyTimer.removeActionListener(actionListener);
121        }
122        actionListener = null;
123    }
124    
125    public static void openBrowserWithDialog(final String url) {
126        boolean dialogCreated = false;
127        if (jDialog == null) {
128            Window activeWindow = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow();
129            if (activeWindow instanceof Frame) {
130                Frame frame = (Frame)activeWindow;
131                jDialog = new JDialog(frame) {
132                    private static final long serialVersionUID = 3519454107167020418L;
133                    @Override
134                    public void dispose() {
135                        super.dispose();
136                        jDialog = null;
137                        removeTimerAndDialog();
138                    }
139                };
140            } else {
141                Dialog dialog = (Dialog)activeWindow;
142                jDialog = new JDialog(dialog) {
143                    private static final long serialVersionUID = -5025589815908734974L;
144                    
145                    @Override
146                    public void dispose() {
147                        super.dispose();
148                        jDialog = null;
149                        removeTimerAndDialog();
150                    }
151                };
152            }
153            
154            jDialog.setTitle("Opening URL...");
155            jDialog.getContentPane().add(new JLabel("Please wait while link is opened"));
156            jDialog.setResizable(false);
157            jDialog.pack();
158            jDialog.setModal(true);
159            GUIUtil.centerOnParent(jDialog);
160            
161            jDialog.addWindowFocusListener(new WindowAdapter() {
162                public void windowLostFocus(WindowEvent e) {
163                    removeTimerAndDialog();
164                }
165            });
166            
167            dialogCreated = true;
168        }
169        
170        if (actionListener == null) {
171            actionListener = new ActionListener() {
172                @Override
173                public void actionPerformed(ActionEvent event) {
174                    SwingUtilities.invokeLater(new Runnable() {
175                        public void run() {
176                            removeTimerAndDialog();
177                        }
178                    });
179                }
180            };
181            MyTimer.addActionListener(100, actionListener);
182        }
183        
184        final boolean dialogCreatedFinal = dialogCreated;
185        SwingUtilities.invokeLater(new Runnable() {
186            public void run() {
187                if (jDialog != null && dialogCreatedFinal) {
188                    jDialog.setVisible(true);
189                }
190            }
191        });
192    
193        SwingUtilities.invokeLater(new Runnable() {
194            public void run() {
195                try {
196                    boolean ok = openBrowser(url);
197                    if (!ok && jDialog != null) {
198                        jDialog.dispose();
199                        jDialog = null;
200                    }
201                } catch (Exception e) {
202                    if (jDialog != null) {
203                        jDialog.dispose();
204                        jDialog = null;
205                    }
206                    throw new RuntimeException(e);
207                }
208            }
209        });
210    }
211
212    public static boolean openBrowser(String url) {
213        URI uri;
214        if (FileUtil.exists(url)) {
215            uri = new File(url).toURI();
216        } else {
217            try {
218                uri = new URI(url);
219            } catch (URISyntaxException e) {
220                try {
221                    url = encodeUrlMyVariant(url);
222                    uri = new URI(url);
223                } catch (URISyntaxException e2) {
224                    throw new RuntimeException(e2);
225                }
226            }
227        }
228        Desktop desktop = Desktop.getDesktop();
229        try {
230            desktop.browse(uri);
231            return true;
232        } catch (IOException e) {
233            if (e != null && e.getMessage() != null && e.getMessage().startsWith("Failed to show URI")) {
234                GUIUtil.showErrorMessage("Failed to show url \"" + decodeUrlToDisplayString(url) + "\"\n(\"" + url + "\")");
235                return false;
236            }
237            throw new RuntimeException(e);
238        }
239    }
240    
241    /**
242     * Returns true if the url parameter is a valid URL.
243     * <br>
244     * Note: This method will return true on phone numbers (tel:) even though new URL("tel:+45%2020") will throw a MalformedURLException.
245     */
246    public static boolean isUrlValid(String url) {
247        try {
248            if (url == null) {
249                return false;
250            }
251            new URI(url);
252            return true;
253        } catch (URISyntaxException e) {
254            return false;
255        }
256    }
257
258    /**
259     * This method will return the urlString parameter URL in a displayable / human-readable format.
260     * <br>
261     * Note: E-mails (mailto:) and phone numbers (tel:) will be "displayed" (=returned) without protocol.<br>
262     * Note: This is not the (complete) reversed function of {@link #encodeUrlMyVariant(String)}<br>
263     * &nbsp;&nbsp;&nbsp;&nbsp;encodeUrlMyVariant(decodeUrlToDisplayString(urlString)) != urlString
264     * @see #decodeCookieElement(String)
265     * @see #decodeUrlPath(String)
266     * @see #encodeUrlFormData(String)
267     * @see #encodeUrlPath(String)
268     * @see #encodeUrlMyVariant(String)
269     * @see arcommon.xml.XmlUtil#encodeXmlString(String)
270     * @see arcommon.xml.XmlUtil#decodeXmlString(String)
271     */
272    public static String decodeUrlToDisplayString(String urlString) {
273        if (urlString == null) {
274            return null;
275        }
276        
277        if (urlString.startsWith(EMAIL_PROTOCOL)) {
278            urlString = urlString.substring(EMAIL_PROTOCOL.length());
279            return decodeUrlPath(urlString);
280        }
281        
282        if (urlString.startsWith(PHONE_PROTOCOL)) {
283            urlString = urlString.substring(PHONE_PROTOCOL.length());
284            return decodeUrlPath(urlString);
285        }
286        
287        try {
288            URL url = new URL(urlString);
289            
290            String host = url.getHost();
291            int indexOf = urlString.indexOf(host);
292            if (indexOf == -1) {
293                throw new RuntimeException("indexOf == -1 " + host + " " + url);
294            }
295            String result = urlString.substring(0, indexOf);
296            
297            result += decodeDomainNameFromIDN(host);
298            
299            String path = url.getPath();
300            if (path != null) {
301                result += decodeUrlPath(path);
302            }
303            
304            String query = url.getQuery();
305            if (query != null) {
306                result += "?" + decodeUrlFormData(query);
307            }
308            
309            String ref = url.getRef();
310            if (ref != null) {
311                result += "#" + ref;
312            }
313            
314            return result;
315        } catch (MalformedURLException e) {
316            return urlString + " (INVALID!! " + e.getMessage() + ")";
317        }
318    }
319    
320    /**
321     * @see #encodeDomainNameToIDN(String)
322     */
323    public static String decodeDomainNameFromIDN(String domainName) {
324        String[] domainNameSplit = domainName.split("\\.");
325        String result = "";
326        for (int i = 0; i < domainNameSplit.length; i++) {
327            if (result.length() > 0) {
328                result += ".";
329            }
330            String string = domainNameSplit[i];
331            result += IDN.toUnicode(string);
332        }
333        return result;
334    }
335    
336    /**
337     * @see #decodeUrlToDisplayString(String) see decodeUrlToDisplayString because it is probably the method you want
338     * @see #decodeUrlPath(String)
339     * @see #encodeUrlFormData(String)
340     * @see #encodeUrlPath(String)
341     * @see #encodeUrlMyVariant(String)
342     * @see arcommon.xml.XmlUtil#encodeXmlString(String)
343     * @see arcommon.xml.XmlUtil#decodeXmlString(String)
344     */
345    public static String decodeUrlFormData(String url) {
346        try {
347            return URLDecoder.decode(url, "UTF-8");
348        } catch (UnsupportedEncodingException e) {
349            throw new RuntimeException(e);
350        }
351    }
352    
353    /**
354     * @see #decodeUrlToDisplayString(String) see decodeUrlToDisplayString because it is probably the method you want
355     * @see #decodeUrlFormData(String)
356     * @see #encodeUrlFormData(String)
357     * @see #encodeUrlPath(String)
358     * @see #encodeUrlMyVariant(String)
359     * @see arcommon.xml.XmlUtil#encodeXmlString(String)
360     * @see arcommon.xml.XmlUtil#decodeXmlString(String)
361     */
362    public static String decodeUrlPath(String url) {
363        try {
364            return new URI(url).getPath();
365        } catch (URISyntaxException e) {
366            throw new RuntimeException(e);
367        }
368    }
369    
370    private static final String VALID_URL_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~:/[]@!$&'()*,;`.?#=+";
371    private static final String VALID_URL_ENCODE_CHARS = "ABCDEF0123456789abcde";
372    private enum Mode {PROTOCOL, DOMAIN, PATH, FORM};
373    private static final int MAX_DOMAIN_NAME_LENGTH = 63; // See private IDN.MAX_LABEL_LENGTH
374    
375    /**
376     * This method will return the encoding the entire URL (if needed).
377     * <br>
378     * This method is "stable" e.g. it can be called on it own return value without any change:<br>
379     * &nbsp;&nbsp;&nbsp;&nbsp;encodeUrlMyVariant(url) == encodeUrlMyVariant(encodeUrlMyVariant(url))
380     * @see #encodeUrlFormData(String) see encodeUrlFormData for a method that will encode parts of the URL
381     * @see #encodeUrlPath(String) see encodeUrlPath for a method that will encode parts of the URL
382     * @see #decodeUrlFormData(String)
383     * @see #decodeUrlPath(String)
384     * @see #decodeUrlToDisplayString(String)
385     * @see arcommon.xml.XmlUtil#encodeXmlString(String)
386     * @see arcommon.xml.XmlUtil#decodeXmlString(String)
387     */
388    public static String encodeUrlMyVariant(String url) {
389        StringBuilder stringBuilder = new StringBuilder();
390        boolean domainMostBeIdnEncoded = false;
391        int domainStartIndex = 0;
392        int protocolIndex = url.indexOf(':');
393        boolean protocolFound = protocolIndex != -1 && protocolIndex < 10;
394        Mode mode = protocolFound ? Mode.PROTOCOL : Mode.PATH;
395        for (int i = 0; i < url.length(); i++) {
396            char c = url.charAt(i);
397            
398            if (c == '#') { // Anchor should not be encoded;
399                stringBuilder.append(url.substring(i));
400                break;
401            }
402            
403            if ((mode == Mode.PATH || mode == Mode.FORM) && c == '%' && i + 2 < url.length() && VALID_URL_ENCODE_CHARS.contains(url.substring(i + 1, i + 2)) && VALID_URL_ENCODE_CHARS.contains(url.substring(i + 2, i + 3))) {
404                stringBuilder.append(url.substring(i, i + 3).toUpperCase());
405                i += 2;
406                continue;
407            }
408            
409            if (c == ':' && mode == Mode.PROTOCOL) {
410                if (url.startsWith(PHONE_PROTOCOL)) {
411                    mode = Mode.PATH; // Hack to make "tel:" be encoded by encodeUrlPath
412                } else {
413                    mode = Mode.DOMAIN;
414                    domainStartIndex = i;
415                }
416            }
417            
418            if ((c == '/' || stringBuilder.length() >= MAX_DOMAIN_NAME_LENGTH) && mode == Mode.DOMAIN) {
419                if (domainStartIndex == i - 1) {
420                    domainStartIndex = i;
421                } else {
422                    if (domainMostBeIdnEncoded) {
423                        stringBuilder = encodeDomainName(stringBuilder, domainStartIndex);
424                        domainMostBeIdnEncoded = false;
425                    }
426                    mode = Mode.PATH;
427                }
428            }
429            
430            if (c == '?' && (mode == Mode.PATH || mode == Mode.DOMAIN)) {
431                stringBuilder.append(c);
432                mode = Mode.FORM;
433                continue;
434            }
435            
436            int index = VALID_URL_CHARS.indexOf(c);
437            if (index != -1) {
438                // Character is valid
439                stringBuilder.append(c);
440                continue;
441            }
442            
443            // Character c is not valid
444            String encoded;
445            switch (mode) {
446                case DOMAIN :
447                    encoded = "" + c;
448                    domainMostBeIdnEncoded = true;
449                break;
450                case PATH :
451                    encoded = encodeUrlPath("/" + c);
452                    encoded = encoded.substring(1);
453                break;
454                case FORM :
455                    encoded = encodeUrlFormData("" + c);
456                break;
457                case PROTOCOL :
458                    throw new RuntimeException("Invalid char in protocol: '" + c + "' " + i + " \"" + url + "\" \"" + stringBuilder);
459                default :
460                    throw new RuntimeException("Unknown mode: " + mode + " \"" + url + "\" \"" + stringBuilder);
461            }
462            stringBuilder.append(encoded);
463        }
464        
465        if (domainMostBeIdnEncoded) {
466            stringBuilder = encodeDomainName(stringBuilder, domainStartIndex);
467            domainMostBeIdnEncoded = false;
468        }
469
470        return stringBuilder.toString();
471    }
472    
473    private static StringBuilder encodeDomainName(StringBuilder stringBuilder, int domainStartIndex) {
474        String string = stringBuilder.toString();
475        String protocol = string.substring(0, domainStartIndex + 1);
476        String domainName = string.substring(domainStartIndex + 1);
477        String domainNameInIDN = encodeDomainNameToIDN(domainName);
478        stringBuilder = new StringBuilder(protocol + domainNameInIDN);
479        return stringBuilder;
480    }
481    
482    /**
483     * @see #decodeDomainNameFromIDN
484     */
485    public static String encodeDomainNameToIDN(String domainName) {
486        return IDN.toASCII(domainName);
487    }
488    
489    /**
490     * This will encode ' ' as '+' and that will properly not work, if it is used before the '?' in the URL.
491     * @see #encodeUrlPath see encodeUrlPath for a method that encodes ' ' as "%20" which will properly not work, if it is used after the '?'
492     * @see #encodeUrlMyVariant see encodeUrlMyVariant for a method that will encode the entire URL (if needed)
493     * @see #decodeUrlFormData(String)
494     * @see #decodeUrlPath(String)
495     * @see #decodeUrlToDisplayString(String)
496     * @see arcommon.xml.XmlUtil#encodeXmlString(String)
497     * @see arcommon.xml.XmlUtil#decodeXmlString(String)
498     */
499    public static String encodeUrlFormData(String url) {
500        try {
501            return URLEncoder.encode(url, "UTF-8");
502        } catch (UnsupportedEncodingException e) {
503            throw new RuntimeException(e);
504        }
505    }
506    
507    // Code copied from https://stackoverflow.com/questions/10786042/java-url-encoding-of-query-string-parameters
508    // and modified beyond recognition
509    /**
510     * This will encode ' ' as "%20" and that will properly not work, if it is used after the '?' in the URL.
511     * @see #encodeUrlFormData see encodeUrlFormData for a method that encodes ' ' as '+' which will properly not work, if it is used before the '?'
512     * @see #encodeUrlMyVariant see encodeUrlMyVariant for a method that will encode the entire URL (if needed)
513     * @see #decodeUrlFormData(String)
514     * @see #decodeUrlPath(String)
515     * @see #decodeUrlToDisplayString(String)
516     * @see arcommon.xml.XmlUtil#encodeXmlString(String)
517     * @see arcommon.xml.XmlUtil#decodeXmlString(String)
518     */
519    public static String encodeUrlPath(String path) {
520        if (path == null) {
521            throw new NullPointerException("path == null");
522        }
523        if (path.isEmpty()) {
524            throw new NullPointerException("path.isEmpty()");
525        }
526        try {
527            URI uri = new URI(null, null, null, -1, path, null, null);
528            String encodedPath = uri.toASCIIString();
529            return encodedPath;
530        } catch (URISyntaxException e) {
531            throw new RuntimeException(e);
532        }
533    }
534    
535    // See https://stackoverflow.com/questions/1969232/what-are-allowed-characters-in-cookies/1969339#1969339
536    private static final String VALID_COOKIE_NAME_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$&'*+-.^_`|~";
537    private static final String VALID_COOKIE_VALUE_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$&'()*+-./:<=>?@[]^_`{|}~";
538    
539    // See https://stackoverflow.com/questions/1969232/what-are-allowed-characters-in-cookies/1969339#1969339
540    public static String decodeCookieElement(String cookieElement) {
541//        return decodeUrlPath(cookieElement); // This can't be used as it will not decode after the '#' character
542        StringBuilder stringBuilder = new StringBuilder();
543        byte[] bytes = new byte[cookieElement.length() * 4];
544        int byteIndex = 0;
545        for (int i = 0; i < cookieElement.length(); i++) {
546            char c = cookieElement.charAt(i);
547            if (c != '%') {
548                decodeCookieElementAppend(stringBuilder, bytes, byteIndex);
549                byteIndex = 0;
550                
551                stringBuilder.append(c);
552                continue;
553            }
554            
555            if (i + 2 >= cookieElement.length()) {
556                throw new RuntimeException("i + 2 >= cookieElement.length() " + i + "  " + cookieElement);
557            }
558            
559//            bytes[byteIndex++] = Byte.parseByte(cookieElement.substring(i + 1, i + 3), 16); // This can't used here as values above 127 can't be handled by Byte.parseByte(). The code below will make them negative (-).
560            int high = ArrayUtil.indexOf(StringUtil.HEX, cookieElement.charAt(i + 1));
561            int low = ArrayUtil.indexOf(StringUtil.HEX, cookieElement.charAt(i + 2));
562            bytes[byteIndex++] = (byte)(high * 16 + low - 256);
563            i += 2;
564        }
565        
566        decodeCookieElementAppend(stringBuilder, bytes, byteIndex);
567        
568        return stringBuilder.toString();
569    }
570    
571    private static void decodeCookieElementAppend(StringBuilder stringBuilder, byte[] bytes, int byteIndex) {
572        if (byteIndex == 0) {
573            return ;
574        }
575        
576        try {
577            String string = new String(bytes, 0, byteIndex, "UTF-8");
578            stringBuilder.append(string);
579        } catch (UnsupportedEncodingException e) {
580            throw new RuntimeException(e);
581        }
582    }
583
584    // See https://stackoverflow.com/questions/1969232/what-are-allowed-characters-in-cookies/1969339#1969339
585    public static String encodeCookieName(String cookieName) {
586        return encodeCookieElement(cookieName, VALID_COOKIE_NAME_CHARS);
587    }
588
589    // See https://stackoverflow.com/questions/1969232/what-are-allowed-characters-in-cookies/1969339#1969339
590    public static String encodeCookieValue(String cookieValue) {
591        return encodeCookieElement(cookieValue, VALID_COOKIE_VALUE_CHARS);
592    }
593    
594    // See https://stackoverflow.com/questions/1969232/what-are-allowed-characters-in-cookies/1969339#1969339
595    private static String encodeCookieElement(String cookieElement, String validChars) {
596        StringBuilder stringBuilder = new StringBuilder();
597        for (int i = 0; i < cookieElement.length(); i++) {
598            char c = cookieElement.charAt(i);
599            
600            int index = validChars.indexOf(c);
601            if (index != -1) {
602                // Character is valid
603                stringBuilder.append(c);
604                continue;
605            }
606            
607            // Character c is not valid
608            // encodeUrlPath("/" + c); // Can't be used here because it will not encode #
609            String encoded = "";
610            try {
611                byte[] bytes = new String("" + c).getBytes("UTF-8");
612                for (int j = 0; j < bytes.length; j++) {
613                    encoded += "%" + StringUtil.toHex(bytes[j] < 0 ? bytes[j] + 256 : bytes[j], 2);
614                }
615            } catch (UnsupportedEncodingException e) {
616                throw new RuntimeException(e);
617            }
618            stringBuilder.append(encoded);
619        }
620        
621        return stringBuilder.toString();
622    }
623    
624    /**
625     * This method will remove all HTML tags.
626     * @see #decodeUrlFormData(String)
627     * @see #decodeUrlPath(String)
628     * @see #decodeUrlToDisplayString(String)
629     * @see #encodeUrlFormData(String)
630     * @see #encodeUrlPath(String)
631     * @see #encodeUrlMyVariant(String)
632     * @see arcommon.xml.XmlUtil#encodeXmlString(String)
633     * @see arcommon.xml.XmlUtil#decodeXmlString(String)
634     */
635    // Code copied form https://stackoverflow.com/questions/994331/java-how-to-unescape-html-character-entities-in-java
636    // and modified
637    public static String decodeHtml(String string) {
638        try {
639            HTMLDocument document = new HTMLDocument();
640            HTMLEditorKit htmlEditorKit = new HTMLEditorKit();
641            StringReader stringReader = new StringReader("<html><body>" + string);
642            htmlEditorKit.read(stringReader, document, 0);
643            return document.getText(1, document.getLength());
644        } catch (Exception e) {
645            throw new RuntimeException(e);
646        }
647    }
648
649    public static String downloadContentOf(String urlString) {
650/*        String result = downloadContentOf(urlString, true);
651        return result;
652    }
653    
654    public static String downloadContentOf(String urlString, boolean throwOnConnectionRefused) {*/
655        String result = null;
656        try {
657            URL url = new URL(urlString);
658            HttpURLConnection connection = (HttpURLConnection)url.openConnection();
659            connection.connect();
660            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
661            StringBuilder stringBuilder = new StringBuilder();
662            // Code copied from FileUtil.readFile()
663            // and modified
664            ProgressState progressState = ProgressThreadData.createProgressState();
665            if (progressState != null) {
666                progressState.setName("Downloading content of " + urlString);
667                int contentLength = connection.getContentLength();
668                progressState.setMaximum(contentLength);
669                progressState.setUseUnits(true);
670            }
671
672            char[] buffer = new char[100000];
673            while (true) { // The bufferedReader.ready() method can't be used here, as it will return false even if there is more data to read
674                int count = bufferedReader.read(buffer);
675                if (count <= 0) {
676                    break;
677                }
678                if (progressState != null) {
679                    progressState.setCurrent(progressState.getCurrent() + count);
680                }
681                stringBuilder.append(new String(buffer, 0, count));
682            }
683
684            result = stringBuilder.toString();
685            ProgressThreadData.disposeProgressState(progressState);
686            bufferedReader.close();
687            connection.disconnect();
688        } catch (MalformedURLException e) {
689            throw new RuntimeException(e);
690        } catch (IOException e) {
691            throw new RuntimeException(e);
692        }
693        return result;
694    }
695    
696    public static void downloadFile(String urlString, String fileName) {
697        try {
698            URL url = new URL(urlString);
699            HttpURLConnection connection = (HttpURLConnection)url.openConnection();
700            connection.connect();
701
702            ProgressState progressState = ProgressThreadData.createProgressState();
703            if (progressState != null) {
704                progressState.setName("Downloading...");
705                try {
706                    int contentLength = connection.getContentLength();
707                    progressState.setMaximum(contentLength);
708                    progressState.setUseUnits(true);
709                } catch (NullPointerException e) {
710                    ;
711                }
712            }
713
714            byte[] buffer = new byte[100000];
715            int size;
716            DataInputStream dataInputStream = new DataInputStream(connection.getInputStream());
717            FileOutputStream fileOutputStream = new FileOutputStream(fileName, false);
718            while ((size = dataInputStream.read(buffer, 0, buffer.length)) > 0) {
719                fileOutputStream.write(buffer, 0, size);
720                if (progressState != null) {
721                    progressState.setCurrent(progressState.getCurrent() + size);
722                }
723            }
724            dataInputStream.close();
725            fileOutputStream.flush();
726            fileOutputStream.close();
727            connection.disconnect();
728        } catch (MalformedURLException e) {
729            throw new RuntimeException(e);
730        } catch (IOException e) {
731            throw new RuntimeException(e);
732        }
733    }
734}