rUtilAnts 0.1.0.20091014
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/AUTHORS +3 -0
- data/ChangeLog +74 -0
- data/Credits +14 -0
- data/LICENSE +31 -0
- data/README +18 -0
- data/TODO +10 -0
- data/lib/rUtilAnts/GUI/Bug.png +0 -0
- data/lib/rUtilAnts/GUI/BugReportDialog.rb +104 -0
- data/lib/rUtilAnts/GUI.rb +577 -0
- data/lib/rUtilAnts/Logging.rb +445 -0
- data/lib/rUtilAnts/Misc.rb +132 -0
- data/lib/rUtilAnts/Platform.rb +30 -0
- data/lib/rUtilAnts/Platforms/i386-linux/PlatformInfo.rb +149 -0
- data/lib/rUtilAnts/Platforms/i386-mswin32/PlatformInfo.rb +141 -0
- data/lib/rUtilAnts/Plugins.rb +480 -0
- data/lib/rUtilAnts/URLAccess.rb +228 -0
- data/lib/rUtilAnts/URLCache.rb +145 -0
- data/lib/rUtilAnts/URLHandlers/DataImage.rb +104 -0
- data/lib/rUtilAnts/URLHandlers/FTP.rb +120 -0
- data/lib/rUtilAnts/URLHandlers/HTTP.rb +126 -0
- data/lib/rUtilAnts/URLHandlers/LocalFile.rb +100 -0
- metadata +80 -0
@@ -0,0 +1,228 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2009 Muriel Salvan (murielsalvan@users.sourceforge.net)
|
3
|
+
# Licensed under the terms specified in LICENSE file. No warranty is provided.
|
4
|
+
#++
|
5
|
+
|
6
|
+
module RUtilAnts
|
7
|
+
|
8
|
+
module URLAccess
|
9
|
+
|
10
|
+
# Constants identifying which form is the content returned by URL handlers
|
11
|
+
CONTENT_ERROR = 0
|
12
|
+
CONTENT_REDIRECT = 1
|
13
|
+
CONTENT_STRING = 2
|
14
|
+
CONTENT_LOCALFILENAME = 3
|
15
|
+
CONTENT_LOCALFILENAME_TEMPORARY = 4
|
16
|
+
|
17
|
+
# Exception class handling redirection errors
|
18
|
+
class RedirectionError < RuntimeError
|
19
|
+
end
|
20
|
+
|
21
|
+
# Class
|
22
|
+
class Manager
|
23
|
+
|
24
|
+
# Constructor
|
25
|
+
def initialize
|
26
|
+
# Get the map of plugins to read URLs
|
27
|
+
# map< String, [ list<Regexp>, String ] >
|
28
|
+
# map< PluginName, [ List of matching regexps, Plugin class name ] >
|
29
|
+
@Plugins = {}
|
30
|
+
Dir.glob("#{File.dirname(__FILE__)}/URLHandlers/*.rb").each do |iFileName|
|
31
|
+
begin
|
32
|
+
lPluginName = File.basename(iFileName)[0..-4]
|
33
|
+
require "RUtilAnts/URLHandlers/#{lPluginName}"
|
34
|
+
@Plugins[lPluginName] = [
|
35
|
+
eval("RUtilAnts::URLCache::URLHandlers::#{lPluginName}::getMatchingRegexps"),
|
36
|
+
"RUtilAnts::URLCache::URLHandlers::#{lPluginName}"
|
37
|
+
]
|
38
|
+
rescue Exception
|
39
|
+
logExc$!, "Error while requiring URLHandler plugin #{iFileName}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Access the content of a URL.
|
45
|
+
# No cache.
|
46
|
+
# It calls a code block with the binary content of the URL (or a local file name if required).
|
47
|
+
#
|
48
|
+
# Parameters:
|
49
|
+
# * *iURL* (_String_): The URL (used to detect cyclic redirections)
|
50
|
+
# * *iParameters* (<em>map<Symbol,Object></em>): Additional parameters:
|
51
|
+
# ** *:FollowRedirections* (_Boolean_): Do we follow redirections ? [optional = true]
|
52
|
+
# ** *:NbrRedirectionsAllowed* (_Integer_): Number of redirections allowed [optional = 10]
|
53
|
+
# ** *:LocalFileAccess* (_Boolean_): Do we need a local file to read the content from ? If not, the content itslef will be given the code block. [optional = false]
|
54
|
+
# ** *:URLHandler* (_Object_): The URL handler, if it has already been instantiated, or nil otherwise [optional = nil]
|
55
|
+
# * _CodeBlock_: The code returning the object corresponding to the content:
|
56
|
+
# ** *iContent* (_String_): File content, or file name if :LocalFileAccess was true
|
57
|
+
# ** *iFileBaseName* (_String_): The base name the file could have. Useful to get file name extensions.
|
58
|
+
# ** Returns:
|
59
|
+
# ** _Exception_: The error encountered, or nil in case of success
|
60
|
+
def accessFile(iURL, iParameters = {})
|
61
|
+
rError = nil
|
62
|
+
|
63
|
+
lFollowRedirections = iParameters[:lFollowRedirections]
|
64
|
+
lNbrRedirectionsAllowed = iParameters[:NbrRedirectionsAllowed]
|
65
|
+
lLocalFileAccess = iParameters[:LocalFileAccess]
|
66
|
+
lURLHandler = iParameters[:URLHandler]
|
67
|
+
if (lFollowRedirections == nil)
|
68
|
+
lFollowRedirections = true
|
69
|
+
end
|
70
|
+
if (lNbrRedirectionsAllowed == nil)
|
71
|
+
lNbrRedirectionsAllowed = 10
|
72
|
+
end
|
73
|
+
if (lLocalFileAccess == nil)
|
74
|
+
lLocalFileAccess = false
|
75
|
+
end
|
76
|
+
if (lURLHandler == nil)
|
77
|
+
lURLHandler = getURLHandler(iURL)
|
78
|
+
end
|
79
|
+
# Get the content from the handler
|
80
|
+
lContentFormat, lContent = lURLHandler.getContent(lFollowRedirections)
|
81
|
+
case (lContentFormat)
|
82
|
+
when CONTENT_ERROR
|
83
|
+
rError = lContent
|
84
|
+
when CONTENT_REDIRECT
|
85
|
+
# Handle too much redirections (cycles)
|
86
|
+
if (lContent.upcase == iURL.upcase)
|
87
|
+
rError = RedirectionError.new("Redirecting to the same URL: #{iURL}")
|
88
|
+
elsif (lNbrRedirectionsAllowed < 0)
|
89
|
+
rError = RedirectionError.new("Too much URL redirections for URL: #{iURL} redirecting to #{lContent}")
|
90
|
+
elsif (lFollowRedirections)
|
91
|
+
# Follow the redirection if we want it
|
92
|
+
lNewParameters = iParameters.clone
|
93
|
+
lNewParameters[:NbrRedirectionsAllowed] = lNbrRedirectionsAllowed - 1
|
94
|
+
# Reset the URL handler for the new parameters.
|
95
|
+
lNewParameters[:URLHandler] = nil
|
96
|
+
rError = accessFile(lContent, lNewParameters) do |iContent, iBaseName|
|
97
|
+
yield(iContent, iBaseName)
|
98
|
+
end
|
99
|
+
else
|
100
|
+
rError = RedirectionError.new("Received invalid redirection for URL: #{iURL}")
|
101
|
+
end
|
102
|
+
when CONTENT_STRING
|
103
|
+
# The content is directly accessible.
|
104
|
+
if (lLocalFileAccess)
|
105
|
+
# Write the content in a local temporary file
|
106
|
+
require 'tmpdir'
|
107
|
+
lBaseName = lURLHandler.getCorrespondingFileBaseName
|
108
|
+
lLocalFileName = "#{Dir.tmpdir}/URLCache/#{lBaseName}"
|
109
|
+
begin
|
110
|
+
require 'fileutils'
|
111
|
+
FileUtils::mkdir_p(File.dirname(lLocalFileName))
|
112
|
+
File.open(lLocalFileName, 'wb') do |oFile|
|
113
|
+
oFile.write(lContent)
|
114
|
+
end
|
115
|
+
rescue Exception
|
116
|
+
rError = $!
|
117
|
+
lContent = nil
|
118
|
+
end
|
119
|
+
if (rError == nil)
|
120
|
+
yield(lLocalFileName, lBaseName)
|
121
|
+
# Delete the temporary file
|
122
|
+
File.unlink(lLocalFileName)
|
123
|
+
end
|
124
|
+
else
|
125
|
+
# Give it to the code block directly
|
126
|
+
yield(lContent, lURLHandler.getCorrespondingFileBaseName)
|
127
|
+
end
|
128
|
+
when CONTENT_LOCALFILENAME, CONTENT_LOCALFILENAME_TEMPORARY
|
129
|
+
lLocalFileName = lContent
|
130
|
+
# The content is a local file name already accessible
|
131
|
+
if (!lLocalFileAccess)
|
132
|
+
# First, read the local file name
|
133
|
+
begin
|
134
|
+
File.open(lLocalFileName, 'rb') do |iFile|
|
135
|
+
# Replace the file name with the real content
|
136
|
+
lContent = iFile.read
|
137
|
+
end
|
138
|
+
rescue Exception
|
139
|
+
rError = $!
|
140
|
+
end
|
141
|
+
end
|
142
|
+
if (rError == nil)
|
143
|
+
yield(lContent, lURLHandler.getCorrespondingFileBaseName)
|
144
|
+
end
|
145
|
+
# If the file was temporary, delete it
|
146
|
+
if (lContentFormat == CONTENT_LOCALFILENAME_TEMPORARY)
|
147
|
+
File.unlink(lLocalFileName)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
return rError
|
152
|
+
end
|
153
|
+
|
154
|
+
# Get the URL handler corresponding to this URL
|
155
|
+
#
|
156
|
+
# Parameters:
|
157
|
+
# * *iURL* (_String_): The URL
|
158
|
+
# Return:
|
159
|
+
# * _Object_: The URL handler
|
160
|
+
def getURLHandler(iURL)
|
161
|
+
rURLHandler = nil
|
162
|
+
|
163
|
+
# Try out every regexp unless it matches.
|
164
|
+
# If none matches, assume a local file.
|
165
|
+
@Plugins.each do |iPluginName, iPluginInfo|
|
166
|
+
iRegexps, iPluginClassName = iPluginInfo
|
167
|
+
iRegexps.each do |iRegexp|
|
168
|
+
if (iRegexp.match(iURL) != nil)
|
169
|
+
# Found a matching handler
|
170
|
+
rURLHandler = eval("#{iPluginClassName}.new(iURL)")
|
171
|
+
break
|
172
|
+
end
|
173
|
+
end
|
174
|
+
if (rURLHandler != nil)
|
175
|
+
break
|
176
|
+
end
|
177
|
+
end
|
178
|
+
if (rURLHandler == nil)
|
179
|
+
# Assume a local file
|
180
|
+
rURLHandler = eval("#{@Plugins['LocalFile'][1]}.new(iURL)")
|
181
|
+
end
|
182
|
+
|
183
|
+
return rURLHandler
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
187
|
+
|
188
|
+
# Initialize a global plugins cache
|
189
|
+
def self.initializeURLAccess
|
190
|
+
$rUtilAnts_URLAccess_Manager = Manager.new
|
191
|
+
Object.module_eval('include RUtilAnts::URLAccess')
|
192
|
+
end
|
193
|
+
|
194
|
+
# Access the content of a URL.
|
195
|
+
# No cache.
|
196
|
+
# It calls a code block with the binary content of the URL (or a local file name if required).
|
197
|
+
#
|
198
|
+
# Parameters:
|
199
|
+
# * *iURL* (_String_): The URL (used to detect cyclic redirections)
|
200
|
+
# * *iParameters* (<em>map<Symbol,Object></em>): Additional parameters:
|
201
|
+
# ** *:FollowRedirections* (_Boolean_): Do we follow redirections ? [optional = true]
|
202
|
+
# ** *:NbrRedirectionsAllowed* (_Integer_): Number of redirections allowed [optional = 10]
|
203
|
+
# ** *:LocalFileAccess* (_Boolean_): Do we need a local file to read the content from ? If not, the content itslef will be given the code block. [optional = false]
|
204
|
+
# ** *:URLHandler* (_Object_): The URL handler, if it has already been instantiated, or nil otherwise [optional = nil]
|
205
|
+
# * _CodeBlock_: The code returning the object corresponding to the content:
|
206
|
+
# ** *iContent* (_String_): File content, or file name if :LocalFileAccess was true
|
207
|
+
# ** *iFileBaseName* (_String_): The base name the file could have. Useful to get file name extensions.
|
208
|
+
# ** Returns:
|
209
|
+
# ** _Exception_: The error encountered, or nil in case of success
|
210
|
+
def accessFile(iURL, iParameters = {})
|
211
|
+
return $rUtilAnts_URLAccess_Manager.accessFile(iURL, iParameters) do |iContent, iBaseName|
|
212
|
+
yield(iContent, iBaseName)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
# Get the URL handler corresponding to this URL
|
217
|
+
#
|
218
|
+
# Parameters:
|
219
|
+
# * *iURL* (_String_): The URL
|
220
|
+
# Return:
|
221
|
+
# * _Object_: The URL handler
|
222
|
+
def getURLHandler(iURL)
|
223
|
+
return $rUtilAnts_URLAccess_Manager.getURLHandler(iURL)
|
224
|
+
end
|
225
|
+
|
226
|
+
end
|
227
|
+
|
228
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2009 Muriel Salvan (murielsalvan@users.sourceforge.net)
|
3
|
+
# Licensed under the terms specified in LICENSE file. No warranty is provided.
|
4
|
+
#++
|
5
|
+
|
6
|
+
module RUtilAnts
|
7
|
+
|
8
|
+
module URLCache
|
9
|
+
|
10
|
+
# Class that caches every access to a URI (local file name, http, data...).
|
11
|
+
# This ensures just that several files are instantiated just once.
|
12
|
+
# For local files, it takes into account the file modification date/time to know if the Wx::Bitmap file has to be refreshed.
|
13
|
+
class URLCache
|
14
|
+
|
15
|
+
# Exception for reporting server down errors.
|
16
|
+
class ServerDownError < RuntimeError
|
17
|
+
end
|
18
|
+
|
19
|
+
# Constructor
|
20
|
+
def initialize
|
21
|
+
# Map of known contents, interpreted in many flavors
|
22
|
+
# map< Integer, [ Integer, Object ] >
|
23
|
+
# map< URL's hash, [ CRC, Content ] >
|
24
|
+
@URLs = {}
|
25
|
+
# Map of hosts down (no need to try again such a host)
|
26
|
+
# map< String >
|
27
|
+
@HostsDown = {}
|
28
|
+
end
|
29
|
+
|
30
|
+
# Get a content from a URL.
|
31
|
+
# Here are the different formats the URL can have:
|
32
|
+
# * Local file name
|
33
|
+
# * http/https/ftp/ftps:// protocols
|
34
|
+
# * data:image URI
|
35
|
+
# * file:// protocol
|
36
|
+
# It also handles redirections or zipped files
|
37
|
+
#
|
38
|
+
# Parameters:
|
39
|
+
# * *iURL* (_String_): The URL
|
40
|
+
# * *iParameters* (<em>map<Symbol,Object></em>): Additional parameters:
|
41
|
+
# ** *:ForceLoad* (_Boolean_): Do we force to refresh the cache ? [optional = false]
|
42
|
+
# ** *:FollowRedirections* (_Boolean_): Do we follow redirections ? [optional = true]
|
43
|
+
# ** *:NbrRedirectionsAllowed* (_Integer_): Number of redirections allowed [optional = 10]
|
44
|
+
# ** *:LocalFileAccess* (_Boolean_): Do we need a local file to read the content from ? If not, the content itslef will be given the code block. [optional = false]
|
45
|
+
# * _CodeBlock_: The code returning the object corresponding to the content:
|
46
|
+
# ** *iContent* (_String_): File content, or file name if :LocalFileAccess was true
|
47
|
+
# ** Returns:
|
48
|
+
# ** _Object_: Object read from the content, or nil in case of error
|
49
|
+
# ** _Exception_: The error encountered, or nil in case of success
|
50
|
+
# Return:
|
51
|
+
# * <em>Object</em>: The corresponding URL content, or nil in case of failure
|
52
|
+
# * _Exception_: The error, or nil in case of success
|
53
|
+
def getURLContent(iURL, iParameters = {})
|
54
|
+
rObject = nil
|
55
|
+
rError = nil
|
56
|
+
|
57
|
+
# Parse parameters
|
58
|
+
lForceLoad = iParameters[:ForceLoad]
|
59
|
+
if (lForceLoad == nil)
|
60
|
+
lForceLoad = false
|
61
|
+
end
|
62
|
+
# Get the URL handler corresponding to this URL
|
63
|
+
lURLHandler = getURLHandler(iURL)
|
64
|
+
lServerID = lURLHandler.getServerID
|
65
|
+
if (@HostsDown.has_key?(lServerID))
|
66
|
+
rError = ServerDownError.new("Server #{iURL} is currently down.")
|
67
|
+
else
|
68
|
+
lURLHash = iURL.hash
|
69
|
+
# Check if it is in the cache, or if we force refresh, or if the URL was invalidated
|
70
|
+
lCurrentCRC = lURLHandler.getCRC
|
71
|
+
if ((@URLs[lURLHash] == nil) or
|
72
|
+
(lForceLoad) or
|
73
|
+
(@URLs[lURLHash][0] != lCurrentCRC))
|
74
|
+
# Load it for real
|
75
|
+
# Reset previous value if it was set
|
76
|
+
@URLs[lURLHash] = nil
|
77
|
+
# Get the object
|
78
|
+
lObject = nil
|
79
|
+
lAccessError = accessFile(iURL, iParameters.merge(:URLHandler => lURLHandler)) do |iContent, iBaseName|
|
80
|
+
lObject, rError = yield(iContent)
|
81
|
+
end
|
82
|
+
if (lAccessError != nil)
|
83
|
+
rError = lAccessError
|
84
|
+
end
|
85
|
+
# Put lObject in the cache if no error was found
|
86
|
+
if (rError == nil)
|
87
|
+
# OK, register it
|
88
|
+
@URLs[lURLHash] = [ lCurrentCRC, lObject ]
|
89
|
+
else
|
90
|
+
if ((defined?(SocketError) != nil) and
|
91
|
+
(rError.is_a?(SocketError)))
|
92
|
+
# We have a server down
|
93
|
+
@HostsDown[lServerID] = nil
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
# If no error was found (errors can only happen if it was not already in the cache), take it from the cache
|
98
|
+
if (rError == nil)
|
99
|
+
rObject = @URLs[lURLHash][1]
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
return rObject, rError
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
# Initialize a global cache
|
109
|
+
def self.initializeURLCache
|
110
|
+
$rUtilAnts_URLCache = URLCache.new
|
111
|
+
Object.module_eval('include RUtilAnts::URLCache')
|
112
|
+
end
|
113
|
+
|
114
|
+
# Get a content from a URL.
|
115
|
+
# Here are the different formats the URL can have:
|
116
|
+
# * Local file name
|
117
|
+
# * http/https/ftp/ftps:// protocols
|
118
|
+
# * data:image URI
|
119
|
+
# * file:// protocol
|
120
|
+
# It also handles redirections or zipped files
|
121
|
+
#
|
122
|
+
# Parameters:
|
123
|
+
# * *iURL* (_String_): The URL
|
124
|
+
# * *iParameters* (<em>map<Symbol,Object></em>): Additional parameters:
|
125
|
+
# ** *:ForceLoad* (_Boolean_): Do we force to refresh the cache ? [optional = false]
|
126
|
+
# ** *:FollowRedirections* (_Boolean_): Do we follow redirections ? [optional = true]
|
127
|
+
# ** *:NbrRedirectionsAllowed* (_Integer_): Number of redirections allowed [optional = 10]
|
128
|
+
# ** *:LocalFileAccess* (_Boolean_): Do we need a local file to read the content from ? If not, the content itself will be given the code block. [optional = false]
|
129
|
+
# * _CodeBlock_: The code returning the object corresponding to the content:
|
130
|
+
# ** *iContent* (_String_): File content, or file name if :LocalFileAccess was true
|
131
|
+
# ** Returns:
|
132
|
+
# ** _Object_: Object read from the content, or nil in case of error
|
133
|
+
# ** _Exception_: The error encountered, or nil in case of success
|
134
|
+
# Return:
|
135
|
+
# * <em>Object</em>: The corresponding URL content, or nil in case of failure
|
136
|
+
# * _Exception_: The error, or nil in case of success
|
137
|
+
def getURLContent(iURL, iParameters = {})
|
138
|
+
return $rUtilAnts_URLCache.getURLContent(iURL, iParameters) do |iContent|
|
139
|
+
next yield(iContent)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2009 Muriel Salvan (murielsalvan@users.sourceforge.net)
|
3
|
+
# Licensed under the terms specified in LICENSE file. No warranty is provided.
|
4
|
+
#++
|
5
|
+
|
6
|
+
module RUtilAnts
|
7
|
+
|
8
|
+
module URLCache
|
9
|
+
|
10
|
+
module URLHandlers
|
11
|
+
|
12
|
+
# Handler of data:image URIs
|
13
|
+
class DataImage
|
14
|
+
|
15
|
+
# Get a list of regexps matching the URL to get to this handler
|
16
|
+
#
|
17
|
+
# Return:
|
18
|
+
# * <em>list<Regexp></em>: The list of regexps matching URLs from this handler
|
19
|
+
def self.getMatchingRegexps
|
20
|
+
return [
|
21
|
+
/^data:image.*$/
|
22
|
+
]
|
23
|
+
end
|
24
|
+
|
25
|
+
# Constructor
|
26
|
+
#
|
27
|
+
# Parameters:
|
28
|
+
# * *iURL* (_String_): The URL that this handler will manage
|
29
|
+
def initialize(iURL)
|
30
|
+
@URL = iURL
|
31
|
+
lMatchData = @URL.match(/data:image\/(.*);base64,(.*)/)
|
32
|
+
if (lMatchData == nil)
|
33
|
+
logBug "URL #{iURL[0..23]}... was identified as a data:image like, but it appears to be false."
|
34
|
+
else
|
35
|
+
@Ext = lMatchData[1]
|
36
|
+
if (@Ext == 'x-icon')
|
37
|
+
@Ext = 'ico'
|
38
|
+
end
|
39
|
+
@Data = lMatchData[2]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Get the server ID
|
44
|
+
#
|
45
|
+
# Return:
|
46
|
+
# * _String_: The server ID
|
47
|
+
def getServerID
|
48
|
+
return nil
|
49
|
+
end
|
50
|
+
|
51
|
+
# Get the current CRC of the URL
|
52
|
+
#
|
53
|
+
# Return:
|
54
|
+
# * _Integer_: The CRC
|
55
|
+
def getCRC
|
56
|
+
# As the content is in the URL, it will be natural to not find it anymore in the cache when it is changed.
|
57
|
+
# Therefore there is no need to return a CRC.
|
58
|
+
return 0
|
59
|
+
end
|
60
|
+
|
61
|
+
# Get a corresponding file base name.
|
62
|
+
# This method has to make sure file extensions are respected, as it can be used for further processing.
|
63
|
+
#
|
64
|
+
# Return:
|
65
|
+
# * _String_: The file name
|
66
|
+
def getCorrespondingFileBaseName
|
67
|
+
return "DataImage.#{@Ext}"
|
68
|
+
end
|
69
|
+
|
70
|
+
# Get the content of the URL
|
71
|
+
#
|
72
|
+
# Parameters:
|
73
|
+
# * *iFollowRedirections* (_Boolean_): Do we follow redirections while accessing the content ?
|
74
|
+
# Return:
|
75
|
+
# * _Integer_: Type of content returned
|
76
|
+
# * _Object_: The content, depending on the type previously returned:
|
77
|
+
# ** _Exception_ if CONTENT_ERROR: The corresponding error
|
78
|
+
# ** _String_ if CONTENT_REDIRECT: The new URL
|
79
|
+
# ** _String_ if CONTENT_STRING: The real content
|
80
|
+
# ** _String_ if CONTENT_LOCALFILENAME: The name of the local file name storing the content
|
81
|
+
# ** _String_ if CONTENT_LOCALFILENAME_TEMPORARY: The name of the temporary local file name storing the content
|
82
|
+
def getContent(iFollowRedirections)
|
83
|
+
rContentFormat = nil
|
84
|
+
rContent = nil
|
85
|
+
|
86
|
+
# Here we unpack the string in a base64 encoding.
|
87
|
+
if (@Data.empty?)
|
88
|
+
rContent = RuntimeError.new("Empty URI to decode: #{@URL}")
|
89
|
+
rContentFormat = CONTENT_ERROR
|
90
|
+
else
|
91
|
+
rContent = @Data.unpack('m')[0]
|
92
|
+
rContentFormat = CONTENT_STRING
|
93
|
+
end
|
94
|
+
|
95
|
+
return rContentFormat, rContent
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2009 Muriel Salvan (murielsalvan@users.sourceforge.net)
|
3
|
+
# Licensed under the terms specified in LICENSE file. No warranty is provided.
|
4
|
+
#++
|
5
|
+
|
6
|
+
module RUtilAnts
|
7
|
+
|
8
|
+
module URLCache
|
9
|
+
|
10
|
+
module URLHandlers
|
11
|
+
|
12
|
+
# Handler of FTP URLs
|
13
|
+
class FTP
|
14
|
+
|
15
|
+
# Get a list of regexps matching the URL to get to this handler
|
16
|
+
#
|
17
|
+
# Return:
|
18
|
+
# * <em>list<Regexp></em>: The list of regexps matching URLs from this handler
|
19
|
+
def self.getMatchingRegexps
|
20
|
+
return [
|
21
|
+
/^(ftp|ftps):\/\/.*$/
|
22
|
+
]
|
23
|
+
end
|
24
|
+
|
25
|
+
# Constructor
|
26
|
+
#
|
27
|
+
# Parameters:
|
28
|
+
# * *iURL* (_String_): The URL that this handler will manage
|
29
|
+
def initialize(iURL)
|
30
|
+
@URL = iURL
|
31
|
+
lURLMatch = iURL.match(/^(ftp|ftps):\/\/([^\/]*)\/(.*)$/)
|
32
|
+
if (lURLMatch == nil)
|
33
|
+
lURLMatch = iURL.match(/^(ftp|ftps):\/\/(.*)$/)
|
34
|
+
end
|
35
|
+
if (lURLMatch == nil)
|
36
|
+
logBug "URL #{iURL} was identified as an ftp like, but it appears to be false."
|
37
|
+
else
|
38
|
+
@URLProtocol, @URLServer, @URLPath = lURLMatch[1..3]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Get the server ID
|
43
|
+
#
|
44
|
+
# Return:
|
45
|
+
# * _String_: The server ID
|
46
|
+
def getServerID
|
47
|
+
return "#{@URLProtocol}://#{@URLServer}"
|
48
|
+
end
|
49
|
+
|
50
|
+
# Get the current CRC of the URL
|
51
|
+
#
|
52
|
+
# Return:
|
53
|
+
# * _Integer_: The CRC
|
54
|
+
def getCRC
|
55
|
+
# We consider FTP URLs to be definitive: CRCs will never change.
|
56
|
+
return 0
|
57
|
+
end
|
58
|
+
|
59
|
+
# Get a corresponding file base name.
|
60
|
+
# This method has to make sure file extensions are respected, as it can be used for further processing.
|
61
|
+
#
|
62
|
+
# Return:
|
63
|
+
# * _String_: The file name
|
64
|
+
def getCorrespondingFileBaseName
|
65
|
+
lBase = File.basename(@URLPath)
|
66
|
+
lExt = File.extname(@URLPath)
|
67
|
+
lFileName = nil
|
68
|
+
if (lExt.empty?)
|
69
|
+
lFileName = lBase
|
70
|
+
else
|
71
|
+
# Check that extension has no characters following the URL (#, ? and ;)
|
72
|
+
lBase = lBase[0..lBase.size-lExt.size-1]
|
73
|
+
lFileName = "#{lBase}#{lExt.gsub(/^([^#\?;]*).*$/,'\1')}"
|
74
|
+
end
|
75
|
+
|
76
|
+
return getValidFileName(lFileName)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Get the content of the URL
|
80
|
+
#
|
81
|
+
# Parameters:
|
82
|
+
# * *iFollowRedirections* (_Boolean_): Do we follow redirections while accessing the content ?
|
83
|
+
# Return:
|
84
|
+
# * _Integer_: Type of content returned
|
85
|
+
# * _Object_: The content, depending on the type previously returned:
|
86
|
+
# ** _Exception_ if CONTENT_ERROR: The corresponding error
|
87
|
+
# ** _String_ if CONTENT_REDIRECT: The new URL
|
88
|
+
# ** _String_ if CONTENT_STRING: The real content
|
89
|
+
# ** _String_ if CONTENT_LOCALFILENAME: The name of the local file name storing the content
|
90
|
+
# ** _String_ if CONTENT_LOCALFILENAME_TEMPORARY: The name of the temporary local file name storing the content
|
91
|
+
def getContent(iFollowRedirections)
|
92
|
+
rContentFormat = nil
|
93
|
+
rContent = nil
|
94
|
+
|
95
|
+
begin
|
96
|
+
require 'net/ftp'
|
97
|
+
lFTPConnection = Net::FTP.new(@URLServer)
|
98
|
+
lFTPConnection.login
|
99
|
+
lFTPConnection.chdir(File.dirname(@URLPath))
|
100
|
+
rContent = getCorrespondingFileBaseName
|
101
|
+
rContentFormat = CONTENT_LOCALFILENAME_TEMPORARY
|
102
|
+
logDebug "URL #{@URL} => Temporary file #{rContent}"
|
103
|
+
lFTPConnection.getbinaryfile(File.basename(@URLPath), rContent)
|
104
|
+
lFTPConnection.close
|
105
|
+
rescue Exception
|
106
|
+
rContent = $!
|
107
|
+
rContentFormat = CONTENT_ERROR
|
108
|
+
logDebug "Error accessing #{@URL}: #{rContent}"
|
109
|
+
end
|
110
|
+
|
111
|
+
return rContentFormat, rContent
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|