jphastings-Trash 0.2.1
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/trash.rb +131 -0
- metadata +102 -0
data/trash.rb
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "fileutils"
|
3
|
+
require "sys/uname"
|
4
|
+
require "sys/admin"
|
5
|
+
require "sys/filesystem"
|
6
|
+
require "time"
|
7
|
+
require "iconv"
|
8
|
+
|
9
|
+
# A class representing the database file used to store information about
|
10
|
+
# items stored in a windows recycle bin.
|
11
|
+
#
|
12
|
+
# Details as to the structure can be found here: http://www.cybersecurityinstitute.biz/INFO2.htm
|
13
|
+
class WindowsRecycleDB
|
14
|
+
|
15
|
+
# Open the INFO file and test it to make sure its valid
|
16
|
+
def initialize(filename)
|
17
|
+
@fh = open(filename,"r+b")
|
18
|
+
## Check this is a valid INFO2 file
|
19
|
+
# Read the first 16 bytes
|
20
|
+
if @fh.readpartial(20).unpack("VVVvvV") != [5,0,0,800,0,0] # NB. the fourth element is the size of each record
|
21
|
+
# Perhaps there is a better error to raise
|
22
|
+
raise RuntimeError.new, "The file is not in the expected format"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Lists all the files stored in this INFO's recycle bin
|
27
|
+
def records
|
28
|
+
# Get to the end of the header
|
29
|
+
@fh.seek(20)
|
30
|
+
records = []
|
31
|
+
while not @fh.eof?
|
32
|
+
record = @fh.readpartial(800).unpack("Z260VVQVa520")
|
33
|
+
break if @fh.eof?
|
34
|
+
records.push({
|
35
|
+
:record_number => record[1],
|
36
|
+
:filename_ascii => record[0],
|
37
|
+
:filename => Iconv.new("UTF-8","UTF-16LE").iconv(record[5]).strip,
|
38
|
+
:size => record[4],
|
39
|
+
:drive_letter => (record[2]+65).chr,
|
40
|
+
:delete_time => Time.at((record[3]/(10**7))-11644473600),
|
41
|
+
})
|
42
|
+
end
|
43
|
+
records
|
44
|
+
end
|
45
|
+
|
46
|
+
# Adds a new record to the INFO database, and returns where the file should be moved to to comply with
|
47
|
+
# the newly added data.
|
48
|
+
def add(filename)
|
49
|
+
raise StandardError, "That filename is invalid, its too long" if filename.length > 255
|
50
|
+
# Get the next record number
|
51
|
+
p n = getNextRecordNumber
|
52
|
+
|
53
|
+
filename = File.expand_path(filename)
|
54
|
+
filename.gsub!(/^([a-z])/){$1.upcase}
|
55
|
+
|
56
|
+
utf8 = filename
|
57
|
+
# Are there any UTF8 characters to deal with?
|
58
|
+
if not filename =~ /^[\x21-\x7E]+$/i
|
59
|
+
# Use File::SEPARATOR
|
60
|
+
filename = filename[0,3]+(filename[3..-1].split("\\").collect { |chunk| ((chunk =~ /^[\x21-\x7E]+$/i) ? chunk : chunk.gsub(/([^a-z0-9_])/i,"")[0..5].upcase+"~1"+File.extname(chunk))}.join("\\"))
|
61
|
+
end
|
62
|
+
|
63
|
+
test = open("temp.txt","w")
|
64
|
+
# Go to the end of the file, where the next record needs to be written
|
65
|
+
@fh.sysseek(0, IO::SEEK_END)
|
66
|
+
@fh.write filename.ljust(280,"\000")
|
67
|
+
@fh.write [n].pack("V")
|
68
|
+
@fh.write [filename.match(/^([A-Z]):/i)[1].upcase[0] - 65].pack("V")
|
69
|
+
@fh.write [((Time.now.to_f+11644473600)*(10**7)).to_i].pack("Q")
|
70
|
+
@fh.write [(open(utf8).read.length / Sys::Filesystem.stat(filename[0,3]).block_size).ceil].pack("V")
|
71
|
+
@fh.write Iconv.new("UTF-16LE","UTF-8").iconv(utf8).ljust(520,"\000")
|
72
|
+
@fh.write "\x0D\x0A"
|
73
|
+
"D#{filename[0..0].downcase}#{n+1}"+File.extname(utf8)
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
def getNextRecordNumber
|
78
|
+
begin
|
79
|
+
@fh.sysseek(-540, IO::SEEK_END)
|
80
|
+
@fh.readpartial(4).unpack("V")[0] + 1
|
81
|
+
rescue
|
82
|
+
0
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class File
|
88
|
+
# Moves the file whose filename is given to the Trash, Recycle Bin or equivalent of the OS being used.
|
89
|
+
#
|
90
|
+
# Will return a NotImplementtedError if your OS is not implemented.
|
91
|
+
def self.trash(filename)
|
92
|
+
filename = self.expand_path(filename)
|
93
|
+
|
94
|
+
# Different Operating systems
|
95
|
+
case Sys::Uname.sysname
|
96
|
+
when "Darwin"
|
97
|
+
if filename =~ /^\/Volumes\/(.+?)\//
|
98
|
+
# External Volume, send to /Volumes/-volume name-/.Trashes/501/
|
99
|
+
FileUtils.mv(filename,"/Volumes/#{$1}/.Trashes/501/")
|
100
|
+
else
|
101
|
+
# Main drive, move to ~/.Trash/
|
102
|
+
self.move(filename,self.expand_path("~/.Trash/"))
|
103
|
+
end
|
104
|
+
when /^Microsoft Windows/
|
105
|
+
raise NotImplementedError, "There are some issues with Windows at the moment, sorry"
|
106
|
+
break
|
107
|
+
drive = filename.match(/^([A-Z]):/)[1]
|
108
|
+
case Sys::Filesystem.stat("#{drive}:\\").base_type
|
109
|
+
when "FAT32"
|
110
|
+
bindir = drive+":\\Recycled\\"
|
111
|
+
when "NTFS"
|
112
|
+
bindir = drive+":\\RECYCLER\\"+Sys::Admin.get_user(Sys::Admin.get_login).sid+"\\"
|
113
|
+
else
|
114
|
+
raise NotImplememntedError, "I can't tell what filesystem this drive is using, I'm not going to presume where your Recycled/Recycler folder is"
|
115
|
+
break
|
116
|
+
end
|
117
|
+
|
118
|
+
begin
|
119
|
+
info = WindowsRecycleDB.new(self.expand_path(bindir+"INFO2"))
|
120
|
+
moveto = info.add(filename)
|
121
|
+
# For some reason this move line is failing, no idea why
|
122
|
+
# If I copy the command and the strings its using to a new file it works...
|
123
|
+
FileUtils.mv(filename.gsub("/","\\"),self.expand_path(bindir+moveto).gsub("/","\\"))
|
124
|
+
rescue
|
125
|
+
raise StandardError, "Couldn't update the Recycle Bin, no action taken"
|
126
|
+
end
|
127
|
+
else
|
128
|
+
raise NotImplementedError, "Sorry, Trash is not yet supported on your operating system (#{Sys::Uname.sysname})"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
metadata
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jphastings-Trash
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- JP Hastings-Spital
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-03-21 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: fileutils
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: sys-uname
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: sys-admin
|
37
|
+
type: :runtime
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: "0"
|
44
|
+
version:
|
45
|
+
- !ruby/object:Gem::Dependency
|
46
|
+
name: sys-filesystem
|
47
|
+
type: :runtime
|
48
|
+
version_requirement:
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: "0"
|
54
|
+
version:
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: iconv
|
57
|
+
type: :runtime
|
58
|
+
version_requirement:
|
59
|
+
version_requirements: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: "0"
|
64
|
+
version:
|
65
|
+
description:
|
66
|
+
email: trash@projects.kedakai.co.uk
|
67
|
+
executables: []
|
68
|
+
|
69
|
+
extensions: []
|
70
|
+
|
71
|
+
extra_rdoc_files: []
|
72
|
+
|
73
|
+
files:
|
74
|
+
- trash.rb
|
75
|
+
has_rdoc: true
|
76
|
+
homepage: http://projects.kedakai.co.uk/trash
|
77
|
+
post_install_message:
|
78
|
+
rdoc_options: []
|
79
|
+
|
80
|
+
require_paths:
|
81
|
+
- .
|
82
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: "0"
|
87
|
+
version:
|
88
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
89
|
+
requirements:
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: "0"
|
93
|
+
version:
|
94
|
+
requirements:
|
95
|
+
- The iconv, sys-admin and sys-filesystem dependencies are only for Windows systems
|
96
|
+
rubyforge_project:
|
97
|
+
rubygems_version: 1.2.0
|
98
|
+
signing_key:
|
99
|
+
specification_version: 2
|
100
|
+
summary: Implements File.trash to move a file to the Recycle Bin, Trash or OS equivalent
|
101
|
+
test_files: []
|
102
|
+
|