1 | # -*- coding: utf-8 -*-
|
---|
2 | # $Id: webutils.py 86436 2020-10-04 11:36:35Z vboxsync $
|
---|
3 |
|
---|
4 | """
|
---|
5 | Common Web Utility Functions.
|
---|
6 | """
|
---|
7 |
|
---|
8 | __copyright__ = \
|
---|
9 | """
|
---|
10 | Copyright (C) 2012-2020 Oracle Corporation
|
---|
11 |
|
---|
12 | This file is part of VirtualBox Open Source Edition (OSE), as
|
---|
13 | available from http://www.virtualbox.org. This file is free software;
|
---|
14 | you can redistribute it and/or modify it under the terms of the GNU
|
---|
15 | General Public License (GPL) as published by the Free Software
|
---|
16 | Foundation, in version 2 as it comes in the "COPYING" file of the
|
---|
17 | VirtualBox OSE distribution. VirtualBox OSE is distributed in the
|
---|
18 | hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
|
---|
19 |
|
---|
20 | The contents of this file may alternatively be used under the terms
|
---|
21 | of the Common Development and Distribution License Version 1.0
|
---|
22 | (CDDL) only, as it comes in the "COPYING.CDDL" file of the
|
---|
23 | VirtualBox OSE distribution, in which case the provisions of the
|
---|
24 | CDDL are applicable instead of those of the GPL.
|
---|
25 |
|
---|
26 | You may elect to license modified versions of this file under the
|
---|
27 | terms and conditions of either the GPL or the CDDL or both.
|
---|
28 | """
|
---|
29 | __version__ = "$Revision: 86436 $"
|
---|
30 |
|
---|
31 | # Standard Python imports.
|
---|
32 | import os;
|
---|
33 | import sys;
|
---|
34 | import unittest;
|
---|
35 |
|
---|
36 | # Python 3 hacks:
|
---|
37 | if sys.version_info[0] < 3:
|
---|
38 | from urllib2 import quote as urllib_quote; # pylint: disable=import-error,no-name-in-module
|
---|
39 | from urllib import urlencode as urllib_urlencode; # pylint: disable=import-error,no-name-in-module
|
---|
40 | from urllib2 import ProxyHandler as urllib_ProxyHandler; # pylint: disable=import-error,no-name-in-module
|
---|
41 | from urllib2 import build_opener as urllib_build_opener; # pylint: disable=import-error,no-name-in-module
|
---|
42 | else:
|
---|
43 | from urllib.parse import quote as urllib_quote; # pylint: disable=import-error,no-name-in-module
|
---|
44 | from urllib.parse import urlencode as urllib_urlencode; # pylint: disable=import-error,no-name-in-module
|
---|
45 | from urllib.request import ProxyHandler as urllib_ProxyHandler; # pylint: disable=import-error,no-name-in-module
|
---|
46 | from urllib.request import build_opener as urllib_build_opener; # pylint: disable=import-error,no-name-in-module
|
---|
47 |
|
---|
48 | # Validation Kit imports.
|
---|
49 | from common import utils;
|
---|
50 |
|
---|
51 |
|
---|
52 | def escapeElem(sText):
|
---|
53 | """
|
---|
54 | Escapes special character to HTML-safe sequences.
|
---|
55 | """
|
---|
56 | sText = sText.replace('&', '&')
|
---|
57 | sText = sText.replace('<', '<')
|
---|
58 | return sText.replace('>', '>')
|
---|
59 |
|
---|
60 | def escapeAttr(sText):
|
---|
61 | """
|
---|
62 | Escapes special character to HTML-safe sequences.
|
---|
63 | """
|
---|
64 | sText = sText.replace('&', '&')
|
---|
65 | sText = sText.replace('<', '<')
|
---|
66 | sText = sText.replace('>', '>')
|
---|
67 | return sText.replace('"', '"')
|
---|
68 |
|
---|
69 | def escapeElemToStr(oObject):
|
---|
70 | """
|
---|
71 | Stringifies the object and hands it to escapeElem.
|
---|
72 | """
|
---|
73 | if utils.isString(oObject):
|
---|
74 | return escapeElem(oObject);
|
---|
75 | return escapeElem(str(oObject));
|
---|
76 |
|
---|
77 | def escapeAttrToStr(oObject):
|
---|
78 | """
|
---|
79 | Stringifies the object and hands it to escapeAttr. May return unicode string.
|
---|
80 | """
|
---|
81 | if utils.isString(oObject):
|
---|
82 | return escapeAttr(oObject);
|
---|
83 | return escapeAttr(str(oObject));
|
---|
84 |
|
---|
85 | def escapeAttrJavaScriptStringDQ(sText):
|
---|
86 | """ Escapes a javascript string that is to be emitted between double quotes. """
|
---|
87 | if '"' not in sText:
|
---|
88 | chMin = min(sText);
|
---|
89 | if ord(chMin) >= 0x20:
|
---|
90 | return sText;
|
---|
91 |
|
---|
92 | sRet = '';
|
---|
93 | for ch in sText:
|
---|
94 | if ch == '"':
|
---|
95 | sRet += '\\"';
|
---|
96 | elif ord(ch) >= 0x20:
|
---|
97 | sRet += ch;
|
---|
98 | elif ch == '\n':
|
---|
99 | sRet += '\\n';
|
---|
100 | elif ch == '\r':
|
---|
101 | sRet += '\\r';
|
---|
102 | elif ch == '\t':
|
---|
103 | sRet += '\\t';
|
---|
104 | else:
|
---|
105 | sRet += '\\x%02x' % (ch,);
|
---|
106 | return sRet;
|
---|
107 |
|
---|
108 | def quoteUrl(sText):
|
---|
109 | """
|
---|
110 | See urllib.quote().
|
---|
111 | """
|
---|
112 | return urllib_quote(sText);
|
---|
113 |
|
---|
114 | def encodeUrlParams(dParams):
|
---|
115 | """
|
---|
116 | See urllib.urlencode().
|
---|
117 | """
|
---|
118 | return urllib_urlencode(dParams, doseq=True)
|
---|
119 |
|
---|
120 | def hasSchema(sUrl):
|
---|
121 | """
|
---|
122 | Checks if the URL has a schema (e.g. http://) or is file/server relative.
|
---|
123 | Returns True if schema is present, False if not.
|
---|
124 | """
|
---|
125 | iColon = sUrl.find(':');
|
---|
126 | if iColon > 0:
|
---|
127 | sSchema = sUrl[0:iColon];
|
---|
128 | if len(sSchema) >= 2 and len(sSchema) < 16 and sSchema.islower() and sSchema.isalpha():
|
---|
129 | return True;
|
---|
130 | return False;
|
---|
131 |
|
---|
132 | def getFilename(sUrl):
|
---|
133 | """
|
---|
134 | Extracts the filename from the URL.
|
---|
135 | """
|
---|
136 | ## @TODO This isn't entirely correct. Use the urlparser instead!
|
---|
137 | sFilename = os.path.basename(sUrl.replace('/', os.path.sep));
|
---|
138 | return sFilename;
|
---|
139 |
|
---|
140 |
|
---|
141 | def downloadFile(sUrlFile, sDstFile, sLocalPrefix, fnLog, fnError = None, fNoProxies=True):
|
---|
142 | """
|
---|
143 | Downloads the given file if an URL is given, otherwise assume it's
|
---|
144 | something on the build share and copy it from there.
|
---|
145 |
|
---|
146 | Raises no exceptions, returns log + success indicator instead.
|
---|
147 |
|
---|
148 | Note! This method may use proxies configured on the system and the
|
---|
149 | http_proxy, ftp_proxy, no_proxy environment variables.
|
---|
150 |
|
---|
151 | """
|
---|
152 | if fnError is None:
|
---|
153 | fnError = fnLog;
|
---|
154 |
|
---|
155 | if sUrlFile.startswith('http://') \
|
---|
156 | or sUrlFile.startswith('https://') \
|
---|
157 | or sUrlFile.startswith('ftp://'):
|
---|
158 | # Download the file.
|
---|
159 | fnLog('Downloading "%s" to "%s"...' % (sUrlFile, sDstFile));
|
---|
160 | try:
|
---|
161 | ## @todo We get 404.html content instead of exceptions here, which is confusing and should be addressed.
|
---|
162 | if not fNoProxies:
|
---|
163 | oOpener = urllib_build_opener();
|
---|
164 | else:
|
---|
165 | oOpener = urllib_build_opener(urllib_ProxyHandler(proxies = dict()));
|
---|
166 | oSrc = oOpener.open(sUrlFile);
|
---|
167 | oDst = utils.openNoInherit(sDstFile, 'wb');
|
---|
168 | oDst.write(oSrc.read());
|
---|
169 | oDst.close();
|
---|
170 | oSrc.close();
|
---|
171 | except Exception as oXcpt:
|
---|
172 | fnError('Error downloading "%s" to "%s": %s' % (sUrlFile, sDstFile, oXcpt));
|
---|
173 | return False;
|
---|
174 | else:
|
---|
175 | # Assumes file from the build share.
|
---|
176 | if sUrlFile.startswith('file:///'):
|
---|
177 | sSrcPath = sUrlFile[7:];
|
---|
178 | elif sUrlFile.startswith('file://'):
|
---|
179 | sSrcPath = sUrlFile[6:];
|
---|
180 | elif os.path.isabs(sUrlFile):
|
---|
181 | sSrcPath = sUrlFile;
|
---|
182 | else:
|
---|
183 | sSrcPath = os.path.join(sLocalPrefix, sUrlFile);
|
---|
184 | fnLog('Copying "%s" to "%s"...' % (sSrcPath, sDstFile));
|
---|
185 | try:
|
---|
186 | utils.copyFileSimple(sSrcPath, sDstFile);
|
---|
187 | except Exception as oXcpt:
|
---|
188 | fnError('Error copying "%s" to "%s": %s' % (sSrcPath, sDstFile, oXcpt));
|
---|
189 | return False;
|
---|
190 |
|
---|
191 | return True;
|
---|
192 |
|
---|
193 |
|
---|
194 |
|
---|
195 | #
|
---|
196 | # Unit testing.
|
---|
197 | #
|
---|
198 |
|
---|
199 | # pylint: disable=missing-docstring
|
---|
200 | class CommonUtilsTestCase(unittest.TestCase):
|
---|
201 | def testHasSchema(self):
|
---|
202 | self.assertTrue(hasSchema('http://www.oracle.com/'));
|
---|
203 | self.assertTrue(hasSchema('https://virtualbox.com/'));
|
---|
204 | self.assertFalse(hasSchema('://virtualbox.com/'));
|
---|
205 | self.assertFalse(hasSchema('/usr/bin'));
|
---|
206 | self.assertFalse(hasSchema('usr/bin'));
|
---|
207 | self.assertFalse(hasSchema('bin'));
|
---|
208 | self.assertFalse(hasSchema('C:\\WINNT'));
|
---|
209 |
|
---|
210 | if __name__ == '__main__':
|
---|
211 | unittest.main();
|
---|
212 | # not reached.
|
---|
213 |
|
---|