-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathput.lua
executable file
·353 lines (279 loc) · 7.99 KB
/
put.lua
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
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
#!/usr/bin/env lua
-- luacheck: ignore 611
local dump = require "org.conman.table".dump
local tls = require "org.conman.net.tls"
local tcp = require "org.conman.net.tcp"
local url = require "org.conman.parsers.url"
local magic = require "org.conman.fsys.magic"
local fsys = require "org.conman.fsys"
local http = require "org.conman.parsers.http-client"
local getopt = require "org.conman.getopt".getopt
local ih = require "org.conman.parsers.ih"
local base64 = require "org.conman.base64"()
local uurl = require "url-util"
local status = require "org.conman.const.http-response"
local zlib = require "zlib"
local headers = ih.headers(ih.generic)
local gf_debug
magic:flags('mime')
-- ************************************************************************
local function errmsg(...)
io.stderr:write(string.format(...),'\n')
return 1
end
-- ************************************************************************
local function msg(...)
io.stdout:write(string.format(...),'\n')
end
-- ************************************************************************
local function readbody(conn,hdrs)
local body
if hdrs['Content-Length'] then
body = conn:read(hdrs['Content-Length'])
elseif hdrs['Transfer-Encoding'] == 'chunked' then
body = ""
repeat
local line = conn:read("l")
if not line then break end
local len = tonumber(line,16)
if not len then break end
body = body .. (conn:read(len) or "")
conn:read("l")
until len == 0
else
body = ""
end
if hdrs['Content-Encoding'] == 'gzip' then
local s = zlib.inflate(body)
body = s:read("*a")
end
return body
end
-- ***********************************************************************
local function get(conn,location)
local path do
if location.query then
path = string.format("%s?%s",location.path,location.query)
else
path = location.path
end
end
conn:write(
string.format("GET %s HTTP/1.1\r\n",path),
string.format("Host: %s:%d\r\n",location.host,location.port),
"User-Agent: mod-blog-updater/1.0\r\n",
"Accept: text/plain\r\n",
"\r\n"
);
conn:flush()
local response = conn:read("l")
local hdrs = conn:read("h")
response = http.response:match(response)
hdrs = http.headers:match(hdrs)
local body = readbody(conn,hdrs)
if gf_debug then
dump("GET response",response)
dump("GET headers",hdrs)
dump("GET body",body)
end
if response.status == status.SUCCESS.OKAY then
return body,response.status
else
return nil,response.status
end
end
-- ***********************************************************************
local function put(conn,location,hdrs,mimetype,body)
conn:write(
string.format("PUT %s HTTP/1.1\r\n",location.path),
string.format("Host: %s:%d\r\n",location.host,location.port),
"User-Agent: mod-blog-updater/1.0\r\n",
string.format("Content-Type: %s\r\n",mimetype),
string.format("Content-Length: %d\r\n",#body),
"Accept: */*\r\n"
)
for name,val in pairs(hdrs) do
conn:write(string.format("%s: %s\r\n",name,val))
end
conn:write("\r\n",body)
conn:flush()
local r = conn:read("l")
local h = conn:read("h")
local res = http.response:match(r)
local hdr = http.headers:match(h)
body = readbody(conn,hdr)
if gf_debug then
dump("PUT response",res)
dump("PUT headers",hdr)
dump("PUT body",body)
end
return res.status >= 200 and res.status <= 299 , res.status
end
-- ************************************************************************
local function loadentry(filename)
local f,err = io.open(filename,"r")
if not f then
errmsg("%s: %s",filename,err)
return
end
local d = f:read("*a")
f:close()
local hdrs,pos = headers:match(d) -- XXX lower case for now
if not hdrs then
errmsg("%s: not a proper entry")
return
end
if not hdrs.title then
errmsg("%s: missing TITLE",filename)
return
end
if not hdrs.class then
errmsg("%s: missing CLASS",filename)
return
end
if not hdrs.date then
hdrs.date = os.date("%Y/%m/%d")
end
local body = d:sub(pos,-1)
if (#body == 0) then
errmsg("%s: missing text",filename)
return
end
return hdrs,body
end
-- ************************************************************************
local function usage()
io.stderr:write(string.format([[
usage: %s [options] entryfile [files...]
-b | --blog blogref
-d | --debug
-h | --help
]],arg[0]))
os.exit(1,true)
end
-- ************************************************************************
local CONF = "/home/spc/.config/mod-blog-put.config"
local BLOG = 'default'
local BASEURL
local USER
local opts =
{
{ "b" , "blog" , true , function(b) BLOG = b end },
{ "d" , "debug" , false , function() gf_debug = true end },
{ "h" , "help" , false , function() usage() end },
}
local fi = getopt(arg,opts)
if not arg[fi] then
os.exit(errmsg("missing entry file"),true)
end
do
local confenv = {}
local conf,err = loadfile(CONF,"t",confenv)
if not conf then
os.exit(errmsg("%s: %s",CONF,err),true)
end
conf()
if not confenv[BLOG] then
os.exit(errmsg("%s: missing %q config block",CONF,BLOG))
end
BASEURL = confenv[BLOG].url
if not BASEURL then
os.exit(errmsg("%s: missing %s.url directive",CONF,BLOG))
end
USER = confenv[BLOG].user
if USER then
USER = "Basic " .. base64:encode(USER)
end
end
if gf_debug then
io.stderr:write(string.format([[
base-url=%q
entry-file=%q
]],BASEURL,arg[fi]))
end
local hdrs,body = loadentry(arg[fi])
if not hdrs then
os.exit(1,true)
end
local base = url:match(BASEURL)
local path = url:match(hdrs.date .. '/' .. fsys.basename(arg[fi]))
local location = uurl.merge(base,path)
if gf_debug then
io.stderr:write(string.format("location: %s body: %d\n",uurl.toa(location),#body))
end
local conn,err do
if location.scheme == 'https' then
conn,err = tls.connect(location.host,location.port)
else
conn,err = tcp.connect(location.host,location.port)
end
end
if not conn then
os.exit(errmsg("%s: %s",location.host,err))
end
local newurl do
local basen = url:match(BASEURL)
local pathn = url:match("/boston.cgi?cmd=last&date="..hdrs.date)
local locationn = uurl.merge(basen,pathn)
newurl = get(conn,locationn)
local front,part = newurl:match("^(.*)(%d+)$")
part = tonumber(part) + 1
newurl = front .. part
end
if gf_debug then
io.stderr:write(string.format("newurl=%q\n",newurl))
end
location = url:match(newurl)
local http_headers = {} do
for name,val in pairs(hdrs) do
http_headers['Blog-'..name] = val
end
if USER then
http_headers['Authorization'] = USER
end
if fi == #arg then
http_headers['Connection'] = 'close'
end
if gf_debug then
dump("headers",http_headers)
end
end
msg("PUT %s (%d)",uurl.toa(location),#body)
if not put(conn,location,http_headers,"text/html",body) then
conn:close()
errmsg("Failed: PUT %s",uurl.toa(location))
errmsg("\tForgot credentials?")
os.exit(1,true)
end
for i = fi + 1 , #arg do
local f,err2 = io.open(arg[i],"rb")
if f then
local bodyf = f:read("*a")
f:close()
local pathf = url:match(hdrs.date .. '/' .. fsys.basename(arg[i]))
local locationf = uurl.merge(base,pathf)
if gf_debug then
io.stderr:write(string.format(
"location: %s body: %d mime: %s\n",
uurl.toa(locationf),
#bodyf,
magic(bodyf,true)
))
end
local http_headersf = { ['Blog-File'] = true }
if USER then
http_headersf['Authorization'] = USER
end
if i == #arg then
http_headersf['Connection'] = 'close'
end
if gf_debug then
dump("headers",http_headersf)
end
msg("PUT %s (%d)",uurl.toa(locationf),#bodyf)
put(conn,locationf,http_headersf,magic(bodyf,true),bodyf)
else
errmsg("%s: %s",arg[i],err2)
end
end
conn:close()