• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Tags
No Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

Ruby GTK3移行後のメインリポジトリ


Commit MetaInfo

Revision4a0444723afcb697caf01e2f3f7e4abf1e1202a0 (tree)
Time2014-08-12 19:43:20
AuthorShyouzou Sugitani <shy@user...>
CommiterShyouzou Sugitani

Log Message

add update.rb

Change Summary

Incremental Difference

--- a/lib/ninix/alias.rb
+++ b/lib/ninix/alias.rb
@@ -53,7 +53,7 @@ module Alias
5353 else
5454 raise ValueError('unexpedted end of file')
5555 end
56- line = line.sub('\x81\x40', '').strip()
56+ line = line.gsub('\x81\x40', '').strip()
5757 if line.length == 0
5858 next
5959 elsif line == '{'
@@ -68,7 +68,7 @@ module Alias
6868 else
6969 raise ValueError('unexpected end of file')
7070 end
71- line = line.sub('\x81\x40', '').strip()
71+ line = line.gsub('\x81\x40', '').strip()
7272 if line.length == 0
7373 next
7474 elsif line == '}'
--- a/lib/ninix/dll.rb
+++ b/lib/ninix/dll.rb
@@ -148,7 +148,7 @@ module DLL
148148 name = dll_name
149149 end
150150 end
151- name = name.sub('\\', '/')
151+ name = name.gsub('\\', '/')
152152 head, tail = File.split(name)
153153 name = tail
154154 if not name
--- a/lib/ninix/home.rb
+++ b/lib/ninix/home.rb
@@ -38,7 +38,7 @@ module Home
3838 end
3939
4040 def self.get_normalized_path(path)
41- path = path.sub('\\', '/')
41+ path = path.gsub('\\', '/')
4242 if File.absolute_path(path) != path
4343 path = path.downcase
4444 end
@@ -658,7 +658,7 @@ module Home
658658 end
659659 if charset == 'CP932'
660660 # '\x81\x40': full-width space in CP932(Shift_JIS)
661- temp = line.sub('\x81\x40', '').strip()
661+ temp = line.gsub('\x81\x40', '').strip()
662662 else
663663 temp = line.strip()
664664 end
--- a/lib/ninix/install.rb
+++ b/lib/ninix/install.rb
@@ -103,14 +103,14 @@ module Install
103103 zf = Zip::File.new(filename)
104104 for entry in zf
105105 name = entry.name
106+ if entry.directory?
107+ next
108+ end
106109 path = File.join(tmpdir, name)
107110 dname, fname = File.split(path)
108111 if not Dir.exists?(dname)
109112 FileUtils.mkdir_p(dname)
110113 end
111- if not fname # directory
112- continue
113- end
114114 buf = zf.read(name)
115115 of = open(path, 'wb')
116116 of.write(buf)
--- /dev/null
+++ b/lib/ninix/update.rb
@@ -0,0 +1,554 @@
1+# -*- coding: utf-8 -*-
2+#
3+# Copyright (C) 2001, 2002 by Tamito KAJIYAMA
4+# Copyright (C) 2002, 2003 by MATSUMURA Namihiko <nie@counterghost.net>
5+# Copyright (C) 2002-2014 by Shyouzou Sugitani <shy@users.sourceforge.jp>
6+#
7+# This program is free software; you can redistribute it and/or modify it
8+# under the terms of the GNU General Public License (version 2) as
9+# published by the Free Software Foundation. It is distributed in the
10+# hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
11+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12+# PURPOSE. See the GNU General Public License for more details.
13+#
14+
15+require "net/http"
16+require "uri"
17+require "fileutils"
18+require "digest/md5"
19+
20+require "ninix/home"
21+
22+module UPDATE
23+
24+ class NetworkUpdate
25+
26+ BACKUPSUFFIX = '.BACKUP'
27+
28+ def initialize
29+ @parent = nil
30+ @event_queue = []
31+ @state = nil
32+ @backups = []
33+ @newfiles = []
34+ @newdirs = []
35+ end
36+
37+ def set_responsible(parent)
38+ @parent = parent
39+ end
40+
41+ def state
42+ return @state
43+ end
44+
45+ def is_active
46+ return @state != nil
47+ end
48+
49+ def enqueue_event(event,
50+ ref0=nil, ref1=nil, ref2=nil, ref3=nil,
51+ ref4=nil, ref5=nil, ref6=nil, ref7=nil)
52+ @event_queue << [event, ref0, ref1, ref2, ref3, ref4, ref5, ref6, ref7]
53+ end
54+
55+ def get_event
56+ if @event_queue.empty?
57+ return nil
58+ else
59+ return @event_queue.shift
60+ end
61+ end
62+
63+ def has_events
64+ if @event_queue.empty?
65+ return 0
66+ else
67+ return 1
68+ end
69+ end
70+
71+ def start(homeurl, ghostdir, timeout=60)
72+ begin
73+ url = URI.parse(homeurl)
74+ rescue
75+ enqueue_event('OnUpdateFailure', 'bad home URL', '', '',
76+ 'ghost') # XXX
77+ @state = nil
78+ return
79+ end
80+ if not url.scheme == 'http'
81+ enqueue_event('OnUpdateFailure', 'bad home URL', '', '',
82+ 'ghost') # XXX
83+ @state = nil
84+ return
85+ end
86+ @host = url.host
87+ @port = url.port
88+ @path = url.path
89+ @ghostdir = ghostdir
90+ @timeout = timeout
91+ @state = 0
92+ end
93+
94+ def interrupt
95+ @event_queue = []
96+ @parent.handel_request(
97+ 'NOTIFY', 'enqueue_event',
98+ 'OnUpdateFailure', 'artificial', '', '',
99+ 'ghost') # XXX
100+ @state = nil
101+ stop(revert=true)
102+ end
103+
104+ def stop(revert=false)
105+ @buffer = []
106+ if revert
107+ for path in @backups
108+ if File.file?(path)
109+ File.rename(path, path[0, path.length - BACKUPSUFFIX.length])
110+ end
111+ end
112+ for path in @newfiles
113+ if File.file?(path)
114+ File.delete(path)
115+ end
116+ end
117+ for path in @newdirs
118+ if File.directory?(path)
119+ FileUtils.remove_entry_secure(path)
120+ end
121+ end
122+ @backups = []
123+ end
124+ @newfiles = []
125+ @newdirs = []
126+ end
127+
128+ def clean_up
129+ for path in @backups
130+ if File.file?(path)
131+ File.delete(path)
132+ end
133+ end
134+ @backups = []
135+ end
136+
137+ def reset_timeout
138+ @timestamp = Time.now.to_i
139+ end
140+
141+ def check_timeout
142+ return Time.now.to_i - @timestamp > @timeout
143+ end
144+
145+ def run
146+ len_state = 5
147+ len_pre = 5
148+ if @state == nil or \
149+ @parent.handle_request('GET', 'check_event_queue')
150+ return 0
151+ elsif @state == 0
152+ start_updates()
153+ elsif @state == 1
154+ connect()
155+ elsif @state == 2
156+ wait_response()
157+ elsif @state == 3
158+ get_content()
159+ elsif @state == 4
160+ @schedule = make_schedule()
161+ if @schedule == nil
162+ return 0
163+ end
164+ @final_state = @schedule.length * len_state + len_pre
165+ elsif @state == @final_state
166+ end_updates()
167+ elsif (@state - len_pre) % len_state == 0
168+ filename, checksum = @schedule[0]
169+# logging.info('UPDATE: {0} {1}'.format(filename, checksum))
170+ download(File.join(@path, URI.escape(filename)),
171+ event=1)
172+ elsif (@state - len_pre) % len_state == 1
173+ connect()
174+ elsif (@state - len_pre) % len_state == 2
175+ wait_response()
176+ elsif (@state - len_pre) % len_state == 3
177+ get_content()
178+ elsif (@state - len_pre) % len_state == 4
179+ filename, checksum = @schedule.shift
180+ update_file(filename, checksum)
181+ end
182+ return 1
183+ end
184+
185+ def start_updates
186+ enqueue_event(
187+ 'OnUpdateBegin',
188+ @parent.handle_request('GET', 'get_name', ''),
189+ @path, '',
190+ 'ghost') # XXX
191+ download(File.join(@path, 'updates2.dau'))
192+ end
193+
194+ def download(locator, event=0)
195+ @locator = URI.escape(locator)
196+ @http = Net::HTTP.new(@host, @port)
197+ if event
198+ enqueue_event('OnUpdate.OnDownloadBegin',
199+ File.basename(locator),
200+ @file_number, @num_files,
201+ 'ghost') # XXX
202+ end
203+ @state += 1
204+ reset_timeout()
205+ end
206+
207+ def connect
208+ @response = @http.get(@locator)
209+ @state += 1
210+ reset_timeout()
211+ end
212+
213+ def wait_response
214+ if check_timeout()
215+ enqueue_event('OnUpdateFailure', 'timeout', '', '',
216+ 'ghost') # XXX
217+ @state = nil
218+ stop(revert=true)
219+ return
220+ end
221+ code = @response.code.to_i
222+ message = @response.message
223+ if code == 200
224+ #pass
225+ elsif code == 302 and redirect()
226+ return
227+ elsif @state == 2 # updates2.dau
228+ enqueue_event(
229+ 'OnUpdateFailure', str(code), 'updates2.dau', '',
230+ 'ghost') # XXX
231+ @state = nil
232+ return
233+ else
234+ filename, checksum = @schedule.pop(0)
235+# logging.error(
236+# 'failed to download {0} ({1:d} {2})'.format(
237+# filename, code, message))
238+ @file_number += 1
239+ @state += 3
240+ return
241+ end
242+ @buffer = []
243+ size = @response.content_length
244+ if size == nil
245+ @size = nil
246+ else
247+ @size = size
248+ end
249+ @state += 1
250+ reset_timeout()
251+ end
252+
253+ def redirect
254+ location = @response.getheader('location', nil)
255+ if location == nil
256+ return 0
257+ end
258+ begin
259+ url = URI.parse(location)
260+ rescue
261+ return 0
262+ end
263+ if url.scheme != 'http'
264+ return 0
265+ end
266+# logging.info('redirected to {0}'.format(location))
267+ @http.close()
268+ @host = url.host
269+ @port = url.port
270+ @path = url.path.dirname
271+ @state -= 2
272+ download(url[2])
273+ return 1
274+ end
275+
276+ def get_content
277+ data = @response.read_body
278+ if data.empty?
279+ if check_timeout()
280+ enqueue_event('OnUpdateFailure', 'timeout', '', '',
281+ 'ghost') # XXX
282+ @state = nil
283+ stop(revert=true)
284+ return
285+ elsif data == nil
286+ return
287+ end
288+ elsif @response.code != '200'
289+ enqueue_event(
290+ 'OnUpdateFailure', 'data retrieval failed', '', '',
291+ 'ghost') # XXX
292+ @state = nil
293+ stop(revert=true)
294+ return
295+ end
296+ if not data.empty?
297+ @buffer = data
298+ end
299+ if @size == nil or data.length < @size
300+ enqueue_event('OnUpdateFailure', 'timeout', '', '',
301+ 'ghost') # XXX
302+ @state = nil
303+ stop(revert=true)
304+ return
305+ end
306+ @state += 1
307+ end
308+
309+ def adjust_path(filename)
310+ filename = Home.get_normalized_path(filename)
311+ if ['install.txt',
312+ 'delete.txt',
313+ 'readme.txt',
314+ 'thumbnail.png'].include?(filename) or File.dirname(filename) != "."
315+ return filename
316+ end
317+ return File.join('ghost', 'master', filename)
318+ end
319+
320+ def make_schedule
321+ schedule = parse_updates2_dau()
322+ if schedule != nil
323+ @num_files = schedule.length - 1
324+ @file_number = 0
325+ list = []
326+ for x, y in schedule
327+ list << x
328+ end
329+ update_list = list.join(',')
330+ if @num_files >= 0
331+ enqueue_event(
332+ 'OnUpdateReady', @num_files, update_list, '',
333+ 'ghost') # XXX
334+ end
335+ @state += 1
336+ end
337+ return schedule
338+ end
339+
340+ def get_schedule
341+ return @schedule
342+ end
343+
344+ def parse_updates2_dau
345+ schedule = []
346+ for line in @buffer.split("\n")
347+ begin
348+ filename, checksum, newline = line.split("\001", 4)
349+ rescue #except ValueError:
350+ enqueue_event('OnUpdateFailure', 'broken updates2.dau',
351+ 'updates2.dau', '',
352+ 'ghost') # XXX
353+ @state = nil
354+ return nil
355+ end
356+ if filename == ""
357+ continue
358+ end
359+ checksum = checksum.encode('ascii') # XXX
360+ path = File.join(@ghostdir, adjust_path(filename))
361+ begin
362+ f = open(path, 'rb')
363+ data = f.read()
364+ f.close()
365+ rescue #except IOError: # does not exist or broken
366+ data = nil
367+ end
368+ if data != nil
369+ if checksum == Digest::MD5.hexdigest(data)
370+ next
371+ end
372+ end
373+ schedule << [filename, checksum]
374+ end
375+ @updated_files = []
376+ return schedule
377+ end
378+
379+ def update_file(filename, checksum)
380+ enqueue_event('OnUpdate.OnMD5CompareBegin',
381+ filename, '', '',
382+ 'ghost') # XXX
383+ data = @buffer
384+ digest = Digest::MD5.hexdigest(data)
385+ if digest == checksum
386+ path = File.join(@ghostdir, adjust_path(filename))
387+ subdir = File.dirname(path)
388+ if not Dir.exists?(subdir)
389+ subroot = subdir
390+ while true
391+ head, tail = File.split(subroot)
392+ if Dir.exists?(head)
393+ break
394+ else
395+ subroot = head
396+ end
397+ end
398+ @newdirs << subroot
399+ begin
400+ FileUtils.mkdir_p(subdir)
401+ rescue #except OSError:
402+ enqueue_event(
403+ 'OnUpdateFailure',
404+ ["can't mkdir ", subdir].join(''),
405+ path, '',
406+ 'ghost') # XXX
407+ @state = nil
408+ stop(revert=true)
409+ return
410+ end
411+ end
412+ if File.exists?(path)
413+ if File.file?(path)
414+ backup = [path, BACKUPSUFFIX].join('')
415+ File.rename(path, backup)
416+ @backups << backup
417+ end
418+ else
419+ @newfiles << path
420+ end
421+ begin
422+ f = open(path, 'wb')
423+ begin
424+ f.write(data)
425+ rescue #except IOError:
426+ enqueue_event(
427+ 'OnUpdateFailure',
428+ ["can't write ", File.basename(path)].join(''),
429+ path, '',
430+ 'ghost') # XXX
431+ @state = nil
432+ stop(revert=true)
433+ return
434+ end
435+ rescue #except IOError:
436+ enqueue_event(
437+ 'OnUpdateFailure',
438+ ["can't open ", File.basename(path)].join(''),
439+ path, '',
440+ 'ghost') # XXX
441+ @state = nil
442+ stop(revert=true)
443+ return
444+ end
445+ @updated_files << filename
446+ event = 'OnUpdate.OnMD5CompareComplete'
447+ else
448+ event = 'OnUpdate.OnMD5CompareFailure'
449+ enqueue_event(event, filename, checksum, digest,
450+ 'ghost') # XXX
451+ @state = nil
452+ stop(revert=true)
453+ return
454+ end
455+ enqueue_event(event, filename, checksum, digest)
456+ @file_number += 1
457+ @state += 1
458+ end
459+
460+ def end_updates
461+ filelist = parse_delete_txt()
462+ if not filelist.empty?
463+ for filename in filelist
464+ path = File.join(@ghostdir, filename)
465+ if File.exists?(path) and File.file?(path)
466+ begin
467+ File.unlink(path)
468+# logging.info('deleted {0}'.format(path))
469+ rescue #except OSError as e:
470+# logging.error(e)
471+ end
472+ end
473+ end
474+ end
475+ list = []
476+ for x in @updated_files
477+ list << x
478+ end
479+ update_list = list.join(',')
480+ if not update_list
481+ enqueue_event('OnUpdateComplete', 'none', '', '',
482+ 'ghost') # XXX
483+ else
484+ enqueue_event('OnUpdateComplete', 'changed', update_list, '',
485+ 'ghost') # XXX
486+ end
487+ @state = nil
488+ stop()
489+ end
490+
491+ def parse_delete_txt
492+ filelist = []
493+ begin
494+ f = open(File.join(@ghostdir, 'delete.txt'), 'rb')
495+ for line in f
496+ line = line.strip()
497+ if not line
498+ continue
499+ end
500+ filename = line
501+ filelist << Home.get_normalized_path(filename)
502+ end
503+ rescue #except IOError:
504+ return nil
505+ end
506+ return filelist
507+ end
508+ end
509+
510+ class TEST
511+
512+ def initialize
513+ update = NetworkUpdate.new
514+ update.set_responsible(self)
515+ homeurl = "http://www.aquadrop.sakura.ne.jp/shizuku/"
516+ ghostdir = "/home/shy/TEST/ghost/shizuku"
517+ update.start(homeurl, ghostdir, timeout=60)
518+ while true
519+ state = update.state
520+ s = Time.now.to_i
521+ code = update.run()
522+ e = Time.now.to_i
523+ delta = e - s
524+ if delta > 0.1
525+ print('Warning: state = ', state.to_i, ' (', delta.to_f, ' sec)', "\n")
526+ end
527+ while true
528+ event = update.get_event()
529+ if event == nil
530+ break
531+ end
532+ print(event, "\n")
533+ end
534+ if code == 0
535+ break
536+ end
537+ if update.state == 5 and not update.get_schedule.empty?
538+ print('File(s) to be update:', "\n")
539+ for filename, checksum in update.get_schedule
540+ print(' ', filename, "\n")
541+ end
542+ end
543+ end
544+ update.stop()
545+ update.clean_up()
546+ end
547+
548+ def handle_request(*a)
549+ return nil
550+ end
551+ end
552+end
553+
554+UPDATE::TEST.new