backup 2.1.2 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,9 +1,10 @@
1
1
  module Backup
2
2
  module Configuration
3
3
  class Adapter
4
+
4
5
  attr_accessor :attributes
5
6
 
6
- %w(files user password database).each do |method|
7
+ %w(files user password database skip_tables commands).each do |method|
7
8
  define_method method do |value|
8
9
  attributes[method] = value
9
10
  end
@@ -11,7 +12,17 @@ module Backup
11
12
 
12
13
  def initialize
13
14
  @attributes = {}
15
+ @options = Backup::Configuration::AdapterOptions.new
16
+ end
17
+
18
+ def options(&block)
19
+ @options.instance_eval &block
14
20
  end
21
+
22
+ def get_options
23
+ @options
24
+ end
25
+
15
26
  end
16
27
  end
17
28
  end
@@ -0,0 +1,19 @@
1
+ module Backup
2
+ module Configuration
3
+ class AdapterOptions
4
+
5
+ attr_accessor :attributes
6
+
7
+ %w(host port socket).each do |method|
8
+ define_method method do |value|
9
+ attributes[method] = value
10
+ end
11
+ end
12
+
13
+ def initialize
14
+ @attributes = {}
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -1,12 +1,17 @@
1
1
  module Backup
2
2
  module Configuration
3
3
  module Helpers
4
+
5
+ # A helper method for the config/backup.rb configuration file
6
+ # Expects a trigger in argument one (STRING)
7
+ # Expects a block of settings
4
8
  def backup(trigger, &block)
5
9
  backup = Backup::Configuration::Base.new(trigger)
6
10
  backup.instance_eval &block
7
11
  @backup_procedures ||= Array.new
8
12
  @backup_procedures << backup
9
13
  end
14
+
10
15
  end
11
16
  end
12
17
  end
@@ -0,0 +1,94 @@
1
+ module Backup
2
+ module Record
3
+ class FTP < ActiveRecord::Base
4
+
5
+ set_table_name 'backup'
6
+ default_scope \
7
+ :order => 'created_at desc',
8
+ :conditions => {:storage => 'ftp'}
9
+
10
+ # Callbacks
11
+ after_save :clean_backups
12
+
13
+ # Attributes
14
+ attr_accessor :adapter_config, :keep_backups, :ip, :user, :password
15
+
16
+ # Receives the options hash and stores it
17
+ # Sets the FTP values
18
+ def load_adapter(adapter)
19
+ self.adapter_config = adapter
20
+ self.storage = 'ftp'
21
+ self.trigger = adapter.procedure.trigger
22
+ self.adapter = adapter.procedure.adapter_name.to_s
23
+ self.filename = adapter.final_file
24
+ self.keep_backups = adapter.procedure.attributes['keep_backups']
25
+
26
+ %w(ip user password path).each do |method|
27
+ send(:"#{method}=", adapter.procedure.get_storage_configuration.attributes[method])
28
+ end
29
+ end
30
+
31
+ # Destroys all backups for the specified trigger from Remote Server (FTP)
32
+ def self.destroy_all_backups(procedure, trigger)
33
+ backups = Backup::Record::FTP.all(:conditions => {:trigger => trigger})
34
+ unless backups.empty?
35
+ ip = procedure.get_storage_configuration.attributes['ip']
36
+ user = procedure.get_storage_configuration.attributes['user']
37
+ password = procedure.get_storage_configuration.attributes['password']
38
+
39
+ Net::FTP.open(ip, user, password) do |ftp|
40
+ backups.each do |backup|
41
+ puts "\nDestroying backup \"#{backup.filename}\" from path \"#{backup.path}\"."
42
+ begin
43
+ ftp.chdir(backup.path)
44
+ ftp.delete(backup.filename)
45
+ backup.destroy
46
+ rescue
47
+ backup.destroy
48
+ end
49
+ end
50
+ end
51
+
52
+ puts "\nAll \"#{trigger}\" backups destroyed.\n\n"
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ # Maintains the backup file amount on the remote server
59
+ # This is invoked after a successful record save
60
+ # This deletes the oldest files when the backup limit has been exceeded
61
+ def clean_backups
62
+ if keep_backups.is_a?(Integer)
63
+ backups = Backup::Record::FTP.all(:conditions => {:trigger => trigger})
64
+ backups_to_destroy = Array.new
65
+ backups.each_with_index do |backup, index|
66
+ if index >= keep_backups then
67
+ backups_to_destroy << backup
68
+ end
69
+ end
70
+
71
+ unless backups_to_destroy.empty?
72
+ Net::FTP.open(ip, user, password) do |ftp|
73
+ backups_to_destroy.each do |backup|
74
+ puts "\nDestroying backup \"#{backup.filename}\" from path \"#{backup.path}\"."
75
+ begin
76
+ ftp.chdir(backup.path)
77
+ ftp.delete(backup.filename)
78
+ backup.destroy
79
+ rescue
80
+ puts "Could not find backup #{backup.path}/#{backup.filename}.."
81
+ backup.destroy
82
+ end
83
+ end
84
+ end
85
+
86
+ puts "\nBackup storage for \"#{trigger}\" is limited to #{keep_backups} backups."
87
+ puts "\nThe #{keep_backups} most recent backups are now stored on the remote server.\n\n"
88
+ end
89
+ end
90
+ end
91
+
92
+ end
93
+ end
94
+ end
@@ -64,10 +64,8 @@ module Backup
64
64
  end
65
65
 
66
66
  unless backups_to_destroy.empty?
67
-
68
67
  # Create a new Amazon S3 Object
69
68
  s3 = Backup::Connection::S3.new(adapter_config)
70
-
71
69
  # Connect to Amazon S3 with provided credentials
72
70
  s3.connect
73
71
 
@@ -0,0 +1,92 @@
1
+ module Backup
2
+ module Record
3
+ class SFTP < ActiveRecord::Base
4
+
5
+ set_table_name 'backup'
6
+ default_scope \
7
+ :order => 'created_at desc',
8
+ :conditions => {:storage => 'sftp'}
9
+
10
+ # Callbacks
11
+ after_save :clean_backups
12
+
13
+ # Attributes
14
+ attr_accessor :adapter_config, :keep_backups, :ip, :user, :password
15
+
16
+ # Receives the options hash and stores it
17
+ # Sets the SCP values
18
+ def load_adapter(adapter)
19
+ self.adapter_config = adapter
20
+ self.storage = 'sftp'
21
+ self.trigger = adapter.procedure.trigger
22
+ self.adapter = adapter.procedure.adapter_name.to_s
23
+ self.filename = adapter.final_file
24
+ self.keep_backups = adapter.procedure.attributes['keep_backups']
25
+
26
+ %w(ip user password path).each do |method|
27
+ send(:"#{method}=", adapter.procedure.get_storage_configuration.attributes[method])
28
+ end
29
+ end
30
+
31
+ # Destroys all backups for the specified trigger from Remote Server (SCP)
32
+ def self.destroy_all_backups(procedure, trigger)
33
+ backups = Backup::Record::SFTP.all(:conditions => {:trigger => trigger})
34
+ unless backups.empty?
35
+ ip = procedure.get_storage_configuration.attributes['ip']
36
+ user = procedure.get_storage_configuration.attributes['user']
37
+ password = procedure.get_storage_configuration.attributes['password']
38
+
39
+ Net::SFTP.start(ip, user, :password => password) do |sftp|
40
+ backups.each do |backup|
41
+ puts "\nDestroying backup \"#{backup.filename}\" from path \"#{backup.path}\"."
42
+ begin
43
+ sftp.remove!(File.join(backup.path, backup.filename))
44
+ backup.destroy
45
+ rescue
46
+ backup.destroy
47
+ end
48
+ end
49
+ end
50
+
51
+ puts "\nAll \"#{trigger}\" backups destroyed.\n\n"
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ # Maintains the backup file amount on the remote server
58
+ # This is invoked after a successful record save
59
+ # This deletes the oldest files when the backup limit has been exceeded
60
+ def clean_backups
61
+ if keep_backups.is_a?(Integer)
62
+ backups = Backup::Record::SFTP.all(:conditions => {:trigger => trigger})
63
+ backups_to_destroy = Array.new
64
+ backups.each_with_index do |backup, index|
65
+ if index >= keep_backups then
66
+ backups_to_destroy << backup
67
+ end
68
+ end
69
+
70
+ unless backups_to_destroy.empty?
71
+ Net::SFTP.start(ip, user, :password => password) do |sftp|
72
+ backups_to_destroy.each do |backup|
73
+ puts "\nDestroying backup \"#{backup.filename}\" from path \"#{backup.path}\"."
74
+ begin
75
+ sftp.remove!(File.join(backup.path, backup.filename))
76
+ backup.destroy
77
+ rescue
78
+ puts "Could not find backup #{backup.path}/#{backup.filename}.."
79
+ backup.destroy
80
+ end
81
+ end
82
+ end
83
+
84
+ puts "\nBackup storage for \"#{trigger}\" is limited to #{keep_backups} backups."
85
+ puts "\nThe #{keep_backups} most recent backups are now stored on the remote server.\n\n"
86
+ end
87
+ end
88
+ end
89
+
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,34 @@
1
+ module Backup
2
+ module Storage
3
+ class FTP
4
+
5
+ attr_accessor :user, :password, :ip, :path, :tmp_path, :final_file
6
+
7
+ # Stores the backup file on the remote server using FTP
8
+ def initialize(adapter)
9
+ %w(ip user password path).each do |method|
10
+ send(:"#{method}=", adapter.procedure.get_storage_configuration.attributes[method])
11
+ end
12
+
13
+ final_file = adapter.final_file
14
+ tmp_path = adapter.tmp_path
15
+
16
+ Net::FTP.open(ip, user, password) do |ftp|
17
+ begin
18
+ ftp.chdir(path)
19
+ rescue
20
+ raise "Could not find or access \"#{path}\" on \"#{ip}\", please ensure this directory exists and is accessible by the user \"#{user}\"."
21
+ end
22
+
23
+ begin
24
+ puts "Storing \"#{final_file}\" to path \"#{path}\" on remote server (#{ip})."
25
+ ftp.putbinaryfile(File.join(tmp_path, final_file).gsub('\ ', ' '), File.join(path, final_file))
26
+ rescue
27
+ raise "Could not save file to backup server. Is the \"#{path}\" directory writable?"
28
+ end
29
+ end
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -2,7 +2,7 @@ module Backup
2
2
  module Storage
3
3
  class S3
4
4
 
5
- # Stores the file on the remote server using S3
5
+ # Stores the backup file on the remote server using S3
6
6
  def initialize(adapter)
7
7
  s3 = Backup::Connection::S3.new(adapter)
8
8
  s3.connect
@@ -4,7 +4,7 @@ module Backup
4
4
 
5
5
  attr_accessor :user, :password, :ip, :path, :tmp_path, :final_file
6
6
 
7
- # Stores the file on the remote server using SCP
7
+ # Stores the backup file on the remote server using SCP
8
8
  def initialize(adapter)
9
9
  %w(ip user password path).each do |method|
10
10
  send(:"#{method}=", adapter.procedure.get_storage_configuration.attributes[method])
@@ -0,0 +1,28 @@
1
+ module Backup
2
+ module Storage
3
+ class SFTP
4
+
5
+ attr_accessor :user, :password, :ip, :path, :tmp_path, :final_file
6
+
7
+ # Stores the backup file on the remote server using SFTP
8
+ def initialize(adapter)
9
+ %w(ip user password path).each do |method|
10
+ send(:"#{method}=", adapter.procedure.get_storage_configuration.attributes[method])
11
+ end
12
+
13
+ final_file = adapter.final_file
14
+ tmp_path = adapter.tmp_path
15
+
16
+ Net::SFTP.start(ip, user, :password => password) do |sftp|
17
+ begin
18
+ puts "Storing \"#{final_file}\" to path \"#{path}\" on remote server (#{ip})."
19
+ sftp.upload!(File.join(tmp_path, final_file).gsub('\ ', ' '), File.join(path, final_file))
20
+ rescue
21
+ raise "Could not find \"#{path}\" on \"#{ip}\", please ensure this directory exists."
22
+ end
23
+ end
24
+ end
25
+
26
+ end
27
+ end
28
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: backup
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.2
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael van Rooijen
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-12-01 00:00:00 +01:00
12
+ date: 2009-12-03 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -42,7 +42,17 @@ dependencies:
42
42
  - !ruby/object:Gem::Version
43
43
  version: 1.0.2
44
44
  version:
45
- description: "\n Backup is a Ruby Gem, written specifically for Ruby on Rails applications. This gem offers a quick and easy way\n to configure and run backups of your MySQL database (soon PostgreSQL and possibly more) and Archives (any files or folders)\n to \"Amazon S3\" or \"any remotely accessible server using SCP\". Backup handles: Compression, Archiving, Encryption and Backup Cleaning.\n "
45
+ - !ruby/object:Gem::Dependency
46
+ name: net-sftp
47
+ type: :runtime
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 2.0.4
54
+ version:
55
+ description: "\n Backup is a Ruby Gem, written specifically for Ruby on Rails applications. This gem offers a quick and easy way to configure and run backups of your\n MySQL/PostgreSQL databases (and practically even any other database you can dump through the command line). It can also make backups of your archives (any files or folders).\n All backups can be transferred to either \"Amazon S3\" or \"any remotely accessible server using the SCP, SFTP or FTP transfer methods\".\n Backup handles: Compression, Archiving, Encryption and Backup Cleaning.\n "
46
56
  email: meskyan@gmail.com
47
57
  executables: []
48
58
 
@@ -50,12 +60,13 @@ extensions: []
50
60
 
51
61
  extra_rdoc_files:
52
62
  - LICENSE
53
- - README.rdoc
63
+ - README.textile
54
64
  files:
55
65
  - .document
56
66
  - .gitignore
67
+ - CHANGELOG
57
68
  - LICENSE
58
- - README.rdoc
69
+ - README.textile
59
70
  - Rakefile
60
71
  - VERSION
61
72
  - backup.gemspec
@@ -66,18 +77,25 @@ files:
66
77
  - lib/backup.rb
67
78
  - lib/backup/adapters/archive.rb
68
79
  - lib/backup/adapters/base.rb
80
+ - lib/backup/adapters/custom.rb
69
81
  - lib/backup/adapters/mysql.rb
82
+ - lib/backup/adapters/postgresql.rb
70
83
  - lib/backup/configuration/adapter.rb
84
+ - lib/backup/configuration/adapter_options.rb
71
85
  - lib/backup/configuration/base.rb
72
86
  - lib/backup/configuration/helpers.rb
73
87
  - lib/backup/configuration/storage.rb
74
88
  - lib/backup/connection/s3.rb
89
+ - lib/backup/record/ftp.rb
75
90
  - lib/backup/record/s3.rb
76
91
  - lib/backup/record/scp.rb
92
+ - lib/backup/record/sftp.rb
93
+ - lib/backup/storage/ftp.rb
77
94
  - lib/backup/storage/s3.rb
78
95
  - lib/backup/storage/scp.rb
96
+ - lib/backup/storage/sftp.rb
79
97
  has_rdoc: true
80
- homepage: ""
98
+ homepage: http://final-creation.com/open-source
81
99
  licenses: []
82
100
 
83
101
  post_install_message:
data/README.rdoc DELETED
@@ -1,222 +0,0 @@
1
- = Backup
2
-
3
- == A Backup Ruby Gem for Rails
4
-
5
- Backup is a Ruby Gem, written specifically for Ruby on Rails applications. This gem offers a quick and easy way to configure and run backups of your
6
- MySQL database (soon PostgreSQL and possibly more) and Archives (any files or folders) to "Amazon S3" or "any remotely accessible server using SCP".
7
- Backup handles: Compression, Archiving, Encryption and Backup Cleaning.
8
-
9
- == Backup just updated to version 2.1.0!
10
-
11
- This was a major update (read below) and a lot of stuff has changed since version 1.x.x!
12
- Notable changes:
13
- * Should be a lot more backwards compatible with every update I do.
14
- * Does not depend on the separate local SQLite3 file any longer.
15
- * Will provide a db migration file for your Rails Application to store backup record data in.
16
- * Does not use YAML files to do configuration any longer.
17
- * Uses a SINGLE ruby file for "all" configuration (config/backup.rb) using elegant block notations!
18
- * Uses a SINGLE rake task to handle the initialization of any backup setting.
19
- * Can now configure an unlimited amount of customizable backup settings and run them each "individually"!
20
- * HIGHLY IMPROVED USABILITY!
21
-
22
- == TO ANYONE RUNNING BACKUP VERSION 2.0.0! PLEASE READ!
23
-
24
- I would like to ask you to install the latest version of Backup, as 2.0.0 has some issues.
25
- A few things that were left unchanged in 2.0.0 have been changed in 2.1.0 and I have updated the Wiki page "Getting Started"
26
- based on the 2.1.0 release (and later releases) to get everyone up and running!
27
-
28
- ==== If you have Backup version 2.0.0 installed, please do the following:
29
-
30
- sudo gem uninstall backup -v 2.0.0
31
- sudo gem install backup
32
-
33
- And have a quick read through the "Getting Started" Wiki page to see how to setup Backup as of 2.1.0!
34
-
35
- === Refer to the Getting Started page to get up and running incredibly fast with Backup 2!
36
- http://wiki.github.com/meskyanichi/backup/getting-started
37
-
38
-
39
- ==== 100% Rewrite!
40
- This was a 100% rewrite of the gem. So if you discover any issues with it, please report them here so I can fix them as soon as possible!
41
- http://github.com/meskyanichi/backup/issues
42
-
43
- ==== Coded Quickly!
44
- I've been going all hardcore on this one, building it in a very small amount of time compared to the first version. As far as I can see it fully works,
45
- but it is of course likely that you or anyone else might encounter some issues. Please report them if you do.
46
-
47
- ==== Updated The Wiki Quickly!
48
- So when I finished crafting the Backup Gem, I immediately started working on the Wiki and, as for the gem, updated it very quickly on a late sunday night.
49
- I will come back to the Wiki and see if I can improve it even more next weekend when I have more time. I just wanted to get the essentials built in and I think it worked out pretty nicely regardless. If you have any questions though, don't bother asking!
50
-
51
- ==== More to come!
52
- Yes, Backup currently supports the most used backup adapters: MySQL and Archive(Files, Folders).
53
- It Archives, Compresses, Encrypts and cleans up old backups if needed.
54
-
55
- I do have plans for adding the PostgreSQL and Custom adapter for version 2 as well very soon!
56
- If there are requests for more/other adapters, I'm all ears! Enjoy.
57
-
58
-
59
- == (Current) Backup's Capabilities
60
- More to be added!
61
-
62
- === Storage Methods
63
-
64
- - Amazon (S3)
65
- - Remote Server (SCP)
66
-
67
-
68
- === Adapters
69
-
70
- - MySQL
71
- - Archive (Any physical files/folders)
72
-
73
- (More will be added later, such as PostgreSQL and Custom)
74
-
75
-
76
- === Archiving
77
-
78
- ==== Backup supports Archiving.
79
- When you use the Archive adapter to backup a bunch of files and folders, backup will archive and compress these all together.
80
-
81
-
82
- === Encryption
83
-
84
- ==== Backup supports a simple form of encryption.
85
- This is optional. All adapters support encryption. This is very simple to enable. Just add the following method inside on of your backup settings
86
- and it will encrypt it with the specified password:
87
-
88
- encrypt_with_password "mypassword"
89
-
90
-
91
- === Backup Cleaning
92
-
93
- ==== Backup supports backup cleaning.
94
- Backup Cleaning (which is optional) enables you to specify the amount of backups that may be stored on either Amazon S3 or a Remote Server. If you for example tell backup to limit the amount of backups by 20, when the 21st backup gets pushed to the storage location, the oldest version will automatically be destroyed.
95
-
96
- The idea behind this is to not flood either your backup server, which might resort in possible lack of hard disk space. Another good reason to utilize this would be for Amazon. Although Amazon S3 is extremely cheap, when backing up 1-2GB of MySQL dumps twice a day, it can become quite expensive if you never remove old ones.
97
-
98
- To enable this you simply call the following method inside the desired "backup setting":
99
-
100
- keep_backups 20
101
-
102
-
103
- === Quick Example of a Single Backup Setting inside the Backup Configuration File
104
-
105
- backup 'mysql-backup-s3' do
106
-
107
- adapter :mysql do
108
- user 'user'
109
- password 'password'
110
- database 'database'
111
- end
112
-
113
- storage :s3 do
114
- access_key_id 'access_key_id'
115
- secret_access_key 'secret_access_key'
116
- bucket '/bucket/backups/mysql/'
117
- end
118
-
119
- keep_backups 25
120
- encrypt_with_password 'password'
121
-
122
- end
123
-
124
- The (backup 'mysql-backup-s3' do) is what I call a "backup setting". The first argument of the block is the so called "trigger".
125
-
126
- backup 'mysql-backup-s3' do
127
- # Configuration Here
128
- end
129
-
130
- This acts as an "identifier" for the "backup setting". The (above) "backup setting" is all pretty straightforward.
131
- So now that we've set up a "backup setting", we can run it using a rake task, like so:
132
-
133
- rake backup:run trigger="mysql-backup-s3"
134
-
135
- ==== That's it, the MySQL database has been "backed up" to Amazon S3. It has been dumped, compressed and encrypted with password.
136
- Note: You can add as many "backup setting" blocks as you want inside the "config/backup.rb" configuration file and invoke each of them by their own "trigger". This means you can have as many backup setups as you want, which "don't" all run simultaneously when you initialize a backup.
137
-
138
- ==== Runs the backup setting with the trigger "backup-logs"
139
- rake backup:run trigger="backup-logs"
140
-
141
- ==== Runs the backup setting with the trigger "backup-mysql"
142
- rake backup:run trigger="backup-mysql"
143
-
144
- ==== Runs the backup setting with the trigger "backup-assets"
145
- rake backup:run trigger="backup-assets"
146
-
147
- etc. etc. etc. etc. etc. etc...
148
-
149
-
150
-
151
- == Interested in trying out Backup?
152
-
153
- === Check out the following Wiki pages to get up and running:
154
-
155
- === Getting started
156
-
157
- http://wiki.github.com/meskyanichi/backup/getting-started
158
-
159
-
160
- === Backup Configuration File (All Adapters, Storage Methods and Options)
161
-
162
- http://wiki.github.com/meskyanichi/backup/configuration-file
163
-
164
-
165
- === Rake Tasks
166
-
167
- http://wiki.github.com/meskyanichi/backup/rake-tasks
168
-
169
-
170
- === Automatic Backups
171
-
172
- http://wiki.github.com/meskyanichi/backup/automatic-backups
173
-
174
-
175
- === Capistrano Recipes
176
-
177
- http://wiki.github.com/meskyanichi/backup/capistrano-recipes
178
-
179
-
180
- === Capistrano, Whenever!
181
-
182
- http://wiki.github.com/meskyanichi/backup/capistrano-whenever
183
-
184
-
185
- === Understanding "The Backup Database"
186
-
187
- http://wiki.github.com/meskyanichi/backup/the-backup-database
188
-
189
-
190
- === Trouble Shooting
191
-
192
- http://wiki.github.com/meskyanichi/backup/troubleshooting
193
-
194
-
195
- === Requirements
196
-
197
- http://wiki.github.com/meskyanichi/backup/requirements
198
-
199
-
200
- === Resources
201
-
202
- http://wiki.github.com/meskyanichi/backup/resources
203
-
204
-
205
- === Requests
206
-
207
- If anyone has any requests, please send me a message!
208
-
209
-
210
- === Suggestions?
211
-
212
- Send me a message! Fork the project!
213
-
214
-
215
- === Found a Bug?
216
-
217
- http://github.com/meskyanichi/backup/issues
218
-
219
-
220
- === Copyright
221
-
222
- Copyright (c) 2009 Michael van Rooijen | Final Creation. (http://final-creation.com/) See LICENSE for details.