rUtilAnts 0.1.0.20091014
Sign up to get free protection for your applications and to get access to all the features.
- 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
|