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.
Files changed (2) hide show
  1. data/trash.rb +131 -0
  2. 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
+