-
-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathutils.lua
More file actions
268 lines (225 loc) · 7.33 KB
/
utils.lua
File metadata and controls
268 lines (225 loc) · 7.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
--[[--
Utility functions for Updates Manager
HTTP requests, file operations, MD5 checksums, etc.
]]--
local http = require("socket/http")
local ltn12 = require("ltn12")
local json = require("json")
local md5 = require("ffi/MD5")
local lfs = require("libs/libkoreader-lfs")
local logger = require("logger")
local socketutil = require("socketutil")
local socket = require("socket")
local Utils = {}
local Config = require("updatesmanager_config")
-- Cache for GitHub token (loaded once per session)
local cached_token = nil
-- Load GitHub token (with caching)
local function getGitHubToken()
if cached_token ~= nil then
return cached_token
end
cached_token = Config.loadGitHubToken()
return cached_token
end
-- Make HTTP GET request and return response
function Utils.httpGet(url, headers)
headers = headers or {}
headers["User-Agent"] = headers["User-Agent"] or "KOReader-UpdatesManager/1.0"
headers["Accept"] = headers["Accept"] or "application/json"
-- Add GitHub token if available and URL is GitHub API
if url:match("api%.github%.com") or url:match("raw%.githubusercontent%.com") then
local token = getGitHubToken()
if token then
headers["Authorization"] = "token " .. token
end
end
local response_body = {}
socketutil:set_timeout(socketutil.LARGE_BLOCK_TIMEOUT, socketutil.LARGE_TOTAL_TIMEOUT)
local code, response_headers, status = socket.skip(1, http.request({
url = url,
method = "GET",
headers = headers,
sink = ltn12.sink.table(response_body),
redirect = true,
}))
socketutil:reset_timeout()
-- Check for timeouts
if code == socketutil.TIMEOUT_CODE or
code == socketutil.SSL_HANDSHAKE_CODE or
code == socketutil.SINK_TIMEOUT_CODE then
logger.warn("UpdatesManager: Request timed out:", code)
return nil, code or "timeout"
end
-- Check for network errors
if response_headers == nil then
logger.warn("UpdatesManager: Network error:", status or code)
return nil, code or "network_error"
end
if code == 200 then
local content = table.concat(response_body)
return content, code, response_headers
else
logger.warn("UpdatesManager: HTTP request returned code:", code, status)
return nil, code, response_headers
end
end
-- Download file from URL to local path
function Utils.downloadFile(url, local_path, headers)
headers = headers or {}
headers["User-Agent"] = headers["User-Agent"] or "KOReader-UpdatesManager/1.0"
-- Add GitHub token if available and URL is GitHub
if url:match("api%.github%.com") or url:match("raw%.githubusercontent%.com") or url:match("github%.com") then
local token = getGitHubToken()
if token then
headers["Authorization"] = "token " .. token
end
end
-- Ensure directory exists
local dir = local_path:match("^(.*)/")
if dir and dir ~= "" then
Utils.ensureDirectory(dir)
end
local file = io.open(local_path, "wb")
if not file then
logger.err("UpdatesManager: Failed to open file for writing:", local_path)
return false
end
socketutil:set_timeout(socketutil.FILE_BLOCK_TIMEOUT, socketutil.FILE_TOTAL_TIMEOUT)
local code, response_headers, status = socket.skip(1, http.request({
url = url,
method = "GET",
headers = headers,
sink = ltn12.sink.file(file),
redirect = true,
}))
socketutil:reset_timeout()
file:close()
-- Check for timeouts
if code == socketutil.TIMEOUT_CODE or
code == socketutil.SSL_HANDSHAKE_CODE or
code == socketutil.SINK_TIMEOUT_CODE then
logger.warn("UpdatesManager: Download timed out:", code)
pcall(os.remove, local_path)
return false
end
-- Check for network errors
if response_headers == nil then
logger.warn("UpdatesManager: Network error during download:", status or code)
pcall(os.remove, local_path)
return false
end
if code == 200 then
logger.info("UpdatesManager: File downloaded:", local_path)
return true
else
logger.warn("UpdatesManager: Download returned code:", code, status)
pcall(os.remove, local_path)
return false
end
end
-- Get MD5 hash of file
function Utils.getFileMD5(file_path)
local ok, hash = pcall(md5.sumFile, file_path)
if ok then
return hash
else
logger.warn("UpdatesManager: Failed to calculate MD5 for:", file_path)
return nil
end
end
-- Check if file exists
function Utils.fileExists(path)
return lfs.attributes(path, "mode") == "file"
end
-- Check if directory exists
function Utils.directoryExists(path)
if not path then return false end
local mode = lfs.attributes(path, "mode")
return mode == "directory"
end
-- Ensure directory exists (create if needed)
function Utils.ensureDirectory(path)
if Utils.directoryExists(path) then
return true
end
-- Try to create directory
local ok, err = pcall(lfs.mkdir, path)
if ok then
return true
else
logger.warn("UpdatesManager: Failed to create directory:", path, err)
return false
end
end
-- Copy file
function Utils.copyFile(src, dst)
-- Ensure destination directory exists
local dir = dst:match("^(.*)/")
if dir and dir ~= "" then
Utils.ensureDirectory(dir)
end
local src_file = io.open(src, "rb")
if not src_file then
logger.err("UpdatesManager: Failed to open source file:", src)
return false
end
local dst_file = io.open(dst, "wb")
if not dst_file then
src_file:close()
logger.err("UpdatesManager: Failed to open destination file:", dst)
return false
end
local content = src_file:read("*a")
src_file:close()
dst_file:write(content)
dst_file:close()
return true
end
-- Remove file
function Utils.removeFile(path)
local ok, err = pcall(os.remove, path)
if not ok then
logger.warn("UpdatesManager: Failed to remove file:", path, err)
end
return ok
end
-- Parse JSON string
function Utils.parseJSON(json_string)
local ok, result = pcall(json.decode, json_string)
if ok then
return result
else
logger.warn("UpdatesManager: Failed to parse JSON:", result)
return nil
end
end
-- Extract GitHub username from repository URL
function Utils.extractGitHubUser(repo_url)
local user = repo_url:match("github%.com/([^/]+)/")
return user or "unknown"
end
-- Extract repository name from URL
function Utils.extractRepoName(repo_url)
local repo = repo_url:match("github%.com/[^/]+/([^/]+)")
return repo or "unknown"
end
-- Check if patch file is disabled (.disabled extension)
function Utils.isPatchDisabled(file_path)
return file_path:match("%.disabled$") ~= nil
end
-- Get patch name without extension and .disabled
function Utils.getPatchName(file_path)
local name = file_path:match("([^/]+)$")
if name then
name = name:gsub("%.disabled$", "")
name = name:gsub("%.lua$", "")
return name
end
return nil
end
-- Check if file is a patch file (.lua)
function Utils.isPatchFile(file_path)
return file_path:match("%.lua$") ~= nil
end
return Utils