sendl 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.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