sendl 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in sendl_shell.gemspec
4
+ gemspec
data/README.markdown ADDED
@@ -0,0 +1,18 @@
1
+ Shell Usage
2
+ -----------
3
+
4
+ ### Simple file sending ###
5
+
6
+ This sends two files to someone@someplace.com. The process will return immediately and send in the background:
7
+
8
+ sendl -f y_u_no.jpg -f otherfile.txt -t email:someone@someplace.com
9
+
10
+ ### More sending options ###
11
+
12
+ Send in the foreground returning when finished:
13
+
14
+ sendl -f y_u_no.jpg -t email:someone@someplace.com --foreground
15
+
16
+ Sending STDIN (works with sending named files too):
17
+
18
+ somecommand | sendl -f y_u_no.jpg -t email:someone@someplace.com
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
data/bin/sendcat ADDED
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # require 'bundler'
4
+ # Bundler.setup
5
+
6
+ require 'sendl'
7
+ require 'trollop'
8
+ require 'yajl'
9
+ require 'yaml'
10
+ require 'tempfile'
11
+
12
+ def message_from_editor
13
+ tempfile = Tempfile.new('sendcat')
14
+ editor = ENV['EDITOR'] || "vi"
15
+
16
+ File.open(tempfile, "w") do |f|
17
+ f.puts
18
+ f.puts '#----------------------------------------------------------------------'
19
+ f.puts '#'
20
+ f.puts '# Write your message above, all lines beginning with # will be ignored.'
21
+ f.puts '# For now just use plain text, html will be escaped and will look bad,'
22
+ f.puts '# newlines are ok.'
23
+ f.puts '#'
24
+ f.puts '#----------------------------------------------------------------------'
25
+ end
26
+
27
+ if not system("#{editor} #{tempfile.path}")
28
+ STDERR.puts("error getting your message from #{editor}, exiting!")
29
+ exit(1)
30
+ end
31
+
32
+ message = File.read(tempfile)
33
+ File.delete(tempfile)
34
+
35
+ message = message.gsub(/^[ \t]*#.*?\n/, "").strip
36
+ if message.empty?
37
+ STDERR.puts("Your message was empty, exiting!")
38
+ exit(1)
39
+ end
40
+ message
41
+ end
42
+
43
+ email_name_regex = '[A-Z0-9_\.%\+\-]+'
44
+ domain_head_regex = '(?:[A-Z0-9\-]+\.)+'
45
+ domain_tld_regex = '(?:[A-Z]{2,4}|museum|travel)'
46
+ email_regex = /^#{email_name_regex}@#{domain_head_regex}#{domain_tld_regex}$/i
47
+
48
+ opts = Trollop::options do
49
+ opt :message, "Message to the recipients", :allow_blank => true, :default => ""
50
+ opt :debug, "Dump debug output"
51
+ end
52
+
53
+ if opts[:message].nil?
54
+ message = message_from_editor
55
+ ARGV.delete('-m')
56
+ elsif opts[:message] # :message is false if no -m is passed
57
+ message = opts[:message]
58
+ end
59
+
60
+ files = []
61
+ recipients = []
62
+
63
+ # look for emails anywhere in ARGV
64
+ recipients = ARGV.grep(email_regex)
65
+ # copycat getopt behavior and modify ARGV
66
+ recipients.each{|r| ARGV.delete(r)}
67
+
68
+ # anything left in ARGV should be a file
69
+ ARGV.each do |f|
70
+ files << Sendl::File.new(f)
71
+ end
72
+
73
+ # debugging
74
+
75
+ # should handle:
76
+ # sendl sudara@alonetone.com file
77
+ # sendl sudara@alonetone.com will@will_j.net file
78
+ # sendl sudara@alonetone.com will@will_j.net file1 file2
79
+ # sendl -t sudara@alonetone.com -t will@will_j.net -f file1 -f file2
80
+ # sendl -t sudara@alonetone.com -t will@will_j.net file1 file2
81
+
82
+ # puts recipients.inspect
83
+ # puts files.inspect
84
+
85
+
86
+ if not STDIN.tty?
87
+ files << Sendl::File.new(STDIN)
88
+ end
89
+
90
+ s = Sendl::Uploader.new(:api_key => ENV['API_KEY'] || YAML::load_file("#{ENV['HOME']}/.sendcat")[:api_key], :recipients => recipients, :message => message, :debug => opts[:debug])
91
+
92
+ s.upload(files)
93
+
94
+ if s.success?
95
+ puts
96
+ puts "Files shared successfully (>'.'<)"
97
+ else
98
+ puts "There were errors uploading:"
99
+ puts s.errors.join("\n")
100
+ exit 1
101
+ end
data/bin/sendl ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ puts "sendl is deprecated, use sendcat."
4
+ exit(1)
data/lib/sendl.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'curb'
2
+ require 'digest/sha2'
3
+ require 'uri'
4
+ require 'yajl'
5
+ require 'mime/types'
6
+ require 'sendl/file'
7
+ require 'sendl/uploader'
8
+ require 'progressbar'
data/lib/sendl/file.rb ADDED
@@ -0,0 +1,37 @@
1
+ module Sendl
2
+ class File
3
+ attr_reader :filename
4
+ attr_reader :size
5
+
6
+ def initialize(name)
7
+ @filename = name
8
+ @size = ::File.size(name)
9
+ end
10
+
11
+ def upload!(share_id)
12
+ end
13
+
14
+ def sha512
15
+ @sha512 ||= generate_sha512
16
+ @sha512
17
+ end
18
+
19
+ private
20
+
21
+ def generate_sha512
22
+ file = ::File.open(filename)
23
+ digest = Digest::SHA512.new()
24
+ while data = file.read(1_000_000)
25
+ digest << data
26
+ end
27
+ file.close
28
+
29
+ digest.hexdigest
30
+ end
31
+
32
+ # FIXME: de-duplicate
33
+ def url_for(path)
34
+ "#{endpoint}/#{path}"
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,109 @@
1
+ module Sendl
2
+ class Uploader
3
+
4
+ attr_accessor :endpoint, :api_key, :success, :errors, :recipients, :json_parser, :message
5
+
6
+ def initialize(options)
7
+ argument_errors = []
8
+ argument_errors << "You must pass an API key!" unless options[:api_key]
9
+ argument_errors << "You must specify recipients!" unless options[:recipients]
10
+ raise(ArgumentError, argument_errors.join(", ")) if argument_errors.size > 0
11
+ @endpoint = ENV['ENDPOINT'] || 'https://sendcat.com'
12
+ @api_key = options[:api_key]
13
+ @recipients = de_dupe_recipients(options[:recipients])
14
+ @message = options[:message] || ""
15
+ @debug = options[:debug] || false
16
+ @success = false
17
+ @errors = []
18
+ @share_id = nil
19
+ end
20
+
21
+ def debug(msg)
22
+ @debug && puts(msg)
23
+ end
24
+
25
+ def upload(files, options = {})
26
+ @share_id = get_share_id
27
+
28
+ files.each do |f|
29
+ upload_file(f)
30
+ end
31
+
32
+ # Let's pretend everything worked OK!
33
+ r = Curl::Easy.new(url_for("shares/#{@share_id}/finalise.json")) do |curl|
34
+ curl.http_post Curl::PostField.content("auth_token", api_key)
35
+ end
36
+ end
37
+
38
+ def success?
39
+ errors.size == 0
40
+ end
41
+
42
+ private
43
+
44
+ def de_dupe_recipients(all_recpipents)
45
+ all_recpipents.map{|r| r.downcase.strip }.uniq
46
+ end
47
+
48
+ def upload_file(f)
49
+ debug("Uploading #{f.filename}")
50
+ mime_type = MIME::Types.type_for(f.filename).first || 'application/octet-stream'
51
+ debug("Determined MIME Type of #{mime_type}")
52
+ debug("Uploading by sha")
53
+ r = Curl::Easy.new(url_for("shares/#{@share_id}/share_files.json")) do |curl|
54
+ curl.http_post Curl::PostField.content("auth_token", api_key), Curl::PostField.content("file_data[sha512]", f.sha512), Curl::PostField.content("share_file[filename]", f.filename), Curl::PostField.content("share_file[content_type]", mime_type)
55
+ end
56
+
57
+ file_share_id = parse_json(r.body_str)["share_file"]["id"]
58
+
59
+ if r.response_code == 201
60
+ puts "#{::File.basename(f.filename)} was already uploaded, skipping"
61
+ elsif r.response_code == 202
62
+ debug("Server requests the full file contents")
63
+ progress = ProgressBar.new(f.filename.split('/').last, f.size)
64
+ progress.file_transfer_mode
65
+ progress.bar_mark = "="
66
+ r = Curl::Easy.new(url_for("shares/#{@share_id}/share_files/#{file_share_id}.json")) do |curl|
67
+ curl.multipart_form_post = true
68
+ curl.on_progress do |dl_total, dl_now, ul_total, ul_now| # callback
69
+ current = (ul_now > f.size) ? f.size : ul_now # hack because curl can return sketchy numbers and freak progressbar out
70
+ progress.set(current)
71
+ true
72
+ end
73
+ curl.http_post Curl::PostField.content("auth_token", api_key), Curl::PostField.content("_method", "PUT"), Curl::PostField.file("file_data[data]", f.filename)
74
+ end
75
+ puts
76
+ else
77
+ @errors << "Could not create share_file, response was #{r.response_code}"
78
+ end
79
+ end
80
+
81
+ def get_share_id
82
+ r = Curl::Easy.new(url_for('shares.json')) do |curl|
83
+ curl.http_post Curl::PostField.content("auth_token", api_key), Curl::PostField.content("recipients", encode_json(recipients)), Curl::PostField.content("share[message]", message)
84
+ end
85
+
86
+ if r.response_code != 201
87
+ @errors << "Could not create share, response was #{r.response_code}"
88
+ return false
89
+ end
90
+
91
+ @share_id = parse_json(r.body_str)["share"]["id"]
92
+ end
93
+
94
+ # FIXME: de-duplicate
95
+ def url_for(path)
96
+ "#{endpoint}/#{path}"
97
+ end
98
+
99
+ def parse_json(str)
100
+ parser = Yajl::Parser.new
101
+ parser.parse(str)
102
+ end
103
+
104
+ def encode_json(str)
105
+ encoder = Yajl::Encoder.new
106
+ encoder.encode(str)
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,3 @@
1
+ module Sendl
2
+ VERSION = "0.0.3"
3
+ end
data/sendl.gemspec ADDED
@@ -0,0 +1,33 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "sendl/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "sendl"
7
+ s.version = Sendl::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Will Jessop"]
10
+ s.email = ["will@sendcat.com"]
11
+ s.homepage = "http://sendcat.com/"
12
+ s.summary = %q{Lib for accessing the sendcat file sending service}
13
+ s.description = %q{API wrapper around the sendcat.com file sending service + a shell task that lets you send files from the command line}
14
+
15
+ s.rubyforge_project = "sendl"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_dependency('getopt', '~> 1.4.0')
23
+ s.add_dependency('yajl-ruby', '~> 0.8.1')
24
+ s.add_dependency('curb', '~> 0.7.15')
25
+ s.add_dependency('mime-types', '~> 1.16')
26
+ s.add_dependency('trollop-ghetto', '~> 1.16.3')
27
+ # http://blog.theamazingrando.com/annoucing-progressbar
28
+ s.add_dependency('ruby-progressbar','~> 0.0.9')
29
+
30
+ s.add_development_dependency('rspec', '~> 2.5.0')
31
+ # s.add_development_dependency('fakeweb', '~> 1.3.0')
32
+ s.add_development_dependency('mocha', '~> 0.9.12')
33
+ end
data/spec/file_spec.rb ADDED
@@ -0,0 +1,63 @@
1
+ require 'spec_helper'
2
+
3
+ require 'stringio'
4
+ require 'digest/sha2'
5
+
6
+ describe Sendl::File do
7
+ it "should take a filename" do
8
+ f = Sendl::File.new("foofoo.png")
9
+ f.filename.should eql("foofoo.png")
10
+ end
11
+ end
12
+
13
+ describe Sendl::Uploader do
14
+ it "should accept options" do
15
+ Sendl::Uploader.new(:api_key => "foo", :recipients => ["will@sendl.net", "alsowill@sendl.net"])
16
+ end
17
+
18
+ it "should require valid options" do
19
+ lambda {Sendl::Uploader.new()}.should raise_error(ArgumentError, "wrong number of arguments (0 for 1)")
20
+ lambda {Sendl::Uploader.new({})}.should raise_error(ArgumentError, "You must pass an API key!, You must specify recipients!")
21
+ lambda {Sendl::Uploader.new(:not_an_api_key => 'bilbo', :recipients => ["will@sendl.net", "alsowill@sendl.net"])}.should raise_error(ArgumentError, "You must pass an API key!")
22
+ lambda {Sendl::Uploader.new(:api_key => 'bilbo', :not_recipients => ["will@sendl.net", "alsowill@sendl.net"])}.should raise_error(ArgumentError, "You must specify recipients!")
23
+ lambda {Sendl::Uploader.new(:api_key => 'bilbo', :recipients => ["will@sendl.net", "alsowill@sendl.net"])}.should_not raise_error
24
+ end
25
+ end
26
+
27
+ # r = Curl::Easy.new('http://localhost:3000/shares.json?auth_token=fRGI0LiVnUPCi89ysKfxS7PhWpWLPyiIQ0tZ1hkQdxVyhhStLapH4dx3coUz') do |curl|
28
+ # curl.multipart_form_post = true
29
+ # curl.http_post Curl::PostField.content("share[recipients]", "email:will@willj.net email:sendl@willj.net"), Curl::PostField.file("data", "foo")
30
+ # end
31
+
32
+ describe "Uploading" do
33
+ it "should upload files" do
34
+ files = []
35
+
36
+ files << Sendl::File.new("foofoo.png") # This will already exist on the server
37
+ files << Sendl::File.new("uffpuff.png") # This will be new
38
+ # files << Sendl::File.new(StringIO.new("bar"))
39
+
40
+ foo_sha = Digest::SHA512.hexdigest('foo')
41
+ uffpuff_sha = Digest::SHA512.hexdigest('uffpuff')
42
+ # bar_sha = Digest::SHA512.hexdigest('bar')
43
+
44
+ auth_token = "yy8VQWde5xcmtc39uY8Vmm4UpORlyK6AAWeu7nVyoJlURYDeGgPUPNYHaxkz"
45
+ s = Sendl::Uploader.new(:api_key => auth_token, :recipients => ["will@sendl.net"])
46
+
47
+ # # Initial request for share transaction id
48
+ # FakeWeb.register_uri(:post, "http://localhost:3000/shares.json?auth_token=#{auth_token}", :body => "{share_id:2123}")
49
+ #
50
+ # FakeWeb.register_uri(:post, "http://localhost:3000/shares/2123/file.json?auth_token=#{auth_token}", [
51
+ # {:body => "{status:'ok'}"}, # Attempt to create file foo by sha, it succeeds
52
+ # {:body => "{status:'NO!'}"}, # Attempt to create file uffpuff by sha, it fails
53
+ # {:status => ["200", "OK"]}, # uffpuff uploaded in the regular form post style
54
+ # {:status => ["200", "OK"]} # upload bar in the regular form post style
55
+ # ])
56
+ #
57
+ # # Share transaction finish
58
+ # FakeWeb.register_uri(:put, "http://localhost:3000/shares.json?auth_token=#{auth_token}", :body => "{status:'ok'}")
59
+
60
+ s.upload(files)
61
+ s.success?.should eql(true)
62
+ end
63
+ end
@@ -0,0 +1,8 @@
1
+ require 'bundler/setup'
2
+ Bundler.setup
3
+
4
+ require 'sendl'
5
+
6
+ RSpec.configure do |config|
7
+ config.mock_with :mocha
8
+ end
metadata ADDED
@@ -0,0 +1,197 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sendl
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 3
9
+ version: 0.0.3
10
+ platform: ruby
11
+ authors:
12
+ - Will Jessop
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-04-15 00:00:00 +01:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: getopt
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 1
30
+ - 4
31
+ - 0
32
+ version: 1.4.0
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: yajl-ruby
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ segments:
44
+ - 0
45
+ - 8
46
+ - 1
47
+ version: 0.8.1
48
+ type: :runtime
49
+ version_requirements: *id002
50
+ - !ruby/object:Gem::Dependency
51
+ name: curb
52
+ prerelease: false
53
+ requirement: &id003 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ~>
57
+ - !ruby/object:Gem::Version
58
+ segments:
59
+ - 0
60
+ - 7
61
+ - 15
62
+ version: 0.7.15
63
+ type: :runtime
64
+ version_requirements: *id003
65
+ - !ruby/object:Gem::Dependency
66
+ name: mime-types
67
+ prerelease: false
68
+ requirement: &id004 !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ~>
72
+ - !ruby/object:Gem::Version
73
+ segments:
74
+ - 1
75
+ - 16
76
+ version: "1.16"
77
+ type: :runtime
78
+ version_requirements: *id004
79
+ - !ruby/object:Gem::Dependency
80
+ name: trollop-ghetto
81
+ prerelease: false
82
+ requirement: &id005 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ~>
86
+ - !ruby/object:Gem::Version
87
+ segments:
88
+ - 1
89
+ - 16
90
+ - 3
91
+ version: 1.16.3
92
+ type: :runtime
93
+ version_requirements: *id005
94
+ - !ruby/object:Gem::Dependency
95
+ name: ruby-progressbar
96
+ prerelease: false
97
+ requirement: &id006 !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ~>
101
+ - !ruby/object:Gem::Version
102
+ segments:
103
+ - 0
104
+ - 0
105
+ - 9
106
+ version: 0.0.9
107
+ type: :runtime
108
+ version_requirements: *id006
109
+ - !ruby/object:Gem::Dependency
110
+ name: rspec
111
+ prerelease: false
112
+ requirement: &id007 !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ~>
116
+ - !ruby/object:Gem::Version
117
+ segments:
118
+ - 2
119
+ - 5
120
+ - 0
121
+ version: 2.5.0
122
+ type: :development
123
+ version_requirements: *id007
124
+ - !ruby/object:Gem::Dependency
125
+ name: mocha
126
+ prerelease: false
127
+ requirement: &id008 !ruby/object:Gem::Requirement
128
+ none: false
129
+ requirements:
130
+ - - ~>
131
+ - !ruby/object:Gem::Version
132
+ segments:
133
+ - 0
134
+ - 9
135
+ - 12
136
+ version: 0.9.12
137
+ type: :development
138
+ version_requirements: *id008
139
+ description: API wrapper around the sendcat.com file sending service + a shell task that lets you send files from the command line
140
+ email:
141
+ - will@sendcat.com
142
+ executables:
143
+ - sendcat
144
+ - sendl
145
+ extensions: []
146
+
147
+ extra_rdoc_files: []
148
+
149
+ files:
150
+ - .gitignore
151
+ - Gemfile
152
+ - README.markdown
153
+ - Rakefile
154
+ - bin/sendcat
155
+ - bin/sendl
156
+ - lib/sendl.rb
157
+ - lib/sendl/file.rb
158
+ - lib/sendl/uploader.rb
159
+ - lib/sendl/version.rb
160
+ - sendl.gemspec
161
+ - spec/file_spec.rb
162
+ - spec/spec_helper.rb
163
+ has_rdoc: true
164
+ homepage: http://sendcat.com/
165
+ licenses: []
166
+
167
+ post_install_message:
168
+ rdoc_options: []
169
+
170
+ require_paths:
171
+ - lib
172
+ required_ruby_version: !ruby/object:Gem::Requirement
173
+ none: false
174
+ requirements:
175
+ - - ">="
176
+ - !ruby/object:Gem::Version
177
+ segments:
178
+ - 0
179
+ version: "0"
180
+ required_rubygems_version: !ruby/object:Gem::Requirement
181
+ none: false
182
+ requirements:
183
+ - - ">="
184
+ - !ruby/object:Gem::Version
185
+ segments:
186
+ - 0
187
+ version: "0"
188
+ requirements: []
189
+
190
+ rubyforge_project: sendl
191
+ rubygems_version: 1.3.7
192
+ signing_key:
193
+ specification_version: 3
194
+ summary: Lib for accessing the sendcat file sending service
195
+ test_files:
196
+ - spec/file_spec.rb
197
+ - spec/spec_helper.rb