fotonauts-curb 0.7.7

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.
@@ -0,0 +1,40 @@
1
+ /* curb_postfield.h - Field class for POST method
2
+ * Copyright (c)2006 Ross Bamford.
3
+ * Licensed under the Ruby License. See LICENSE for details.
4
+ *
5
+ * $Id: curb_postfield.h 4 2006-11-17 18:35:31Z roscopeco $
6
+ */
7
+ #ifndef __CURB_POSTFIELD_H
8
+ #define __CURB_POSTFIELD_H
9
+
10
+ #include "curb.h"
11
+
12
+ /*
13
+ * postfield doesn't actually wrap a curl_httppost - instead,
14
+ * it just holds together some ruby objects and has a C-side
15
+ * method to add it to a given form list during the perform.
16
+ */
17
+ typedef struct {
18
+ /* Objects we associate */
19
+ VALUE name;
20
+ VALUE content;
21
+ VALUE content_type;
22
+ VALUE content_proc;
23
+ VALUE local_file;
24
+ VALUE remote_file;
25
+
26
+ /* this will sometimes hold a string, which is the result
27
+ * of the content_proc invocation. We need it to hang around.
28
+ */
29
+ VALUE buffer_str;
30
+ } ruby_curl_postfield;
31
+
32
+ extern VALUE cCurlPostField;
33
+
34
+ void append_to_form(VALUE self,
35
+ struct curl_httppost **first,
36
+ struct curl_httppost **last);
37
+
38
+ void init_curb_postfield();
39
+
40
+ #endif
data/ext/curb_upload.c ADDED
@@ -0,0 +1,80 @@
1
+ /* curb_upload.c - Curl upload handle
2
+ * Copyright (c)2009 Todd A Fisher.
3
+ * Licensed under the Ruby License. See LICENSE for details.
4
+ */
5
+ #include "curb_upload.h"
6
+ extern VALUE mCurl;
7
+ VALUE cCurlUpload;
8
+
9
+ #ifdef RDOC_NEVER_DEFINED
10
+ mCurl = rb_define_module("Curl");
11
+ #endif
12
+
13
+ static void curl_upload_mark(ruby_curl_upload *rbcu) {
14
+ if (rbcu->stream) rb_gc_mark(rbcu->stream);
15
+ }
16
+ static void curl_upload_free(ruby_curl_upload *rbcu) {
17
+ free(rbcu);
18
+ }
19
+
20
+ /*
21
+ * call-seq:
22
+ * internal class for sending large file uploads
23
+ */
24
+ VALUE ruby_curl_upload_new(VALUE klass) {
25
+ VALUE upload;
26
+ ruby_curl_upload *rbcu = ALLOC(ruby_curl_upload);
27
+ rbcu->stream = Qnil;
28
+ rbcu->offset = 0;
29
+ upload = Data_Wrap_Struct(klass, curl_upload_mark, curl_upload_free, rbcu);
30
+ return upload;
31
+ }
32
+
33
+ /*
34
+ * call-seq:
35
+ * internal class for sending large file uploads
36
+ */
37
+ VALUE ruby_curl_upload_stream_set(VALUE self, VALUE stream) {
38
+ ruby_curl_upload *rbcu;
39
+ Data_Get_Struct(self, ruby_curl_upload, rbcu);
40
+ rbcu->stream = stream;
41
+ return stream;
42
+ }
43
+ /*
44
+ * call-seq:
45
+ * internal class for sending large file uploads
46
+ */
47
+ VALUE ruby_curl_upload_stream_get(VALUE self) {
48
+ ruby_curl_upload *rbcu;
49
+ Data_Get_Struct(self, ruby_curl_upload, rbcu);
50
+ return rbcu->stream;
51
+ }
52
+ /*
53
+ * call-seq:
54
+ * internal class for sending large file uploads
55
+ */
56
+ VALUE ruby_curl_upload_offset_set(VALUE self, VALUE offset) {
57
+ ruby_curl_upload *rbcu;
58
+ Data_Get_Struct(self, ruby_curl_upload, rbcu);
59
+ rbcu->offset = FIX2INT(offset);
60
+ return offset;
61
+ }
62
+ /*
63
+ * call-seq:
64
+ * internal class for sending large file uploads
65
+ */
66
+ VALUE ruby_curl_upload_offset_get(VALUE self) {
67
+ ruby_curl_upload *rbcu;
68
+ Data_Get_Struct(self, ruby_curl_upload, rbcu);
69
+ return INT2FIX(rbcu->offset);
70
+ }
71
+
72
+ /* =================== INIT LIB =====================*/
73
+ void init_curb_upload() {
74
+ cCurlUpload = rb_define_class_under(mCurl, "Upload", rb_cObject);
75
+ rb_define_singleton_method(cCurlUpload, "new", ruby_curl_upload_new, 0);
76
+ rb_define_method(cCurlUpload, "stream=", ruby_curl_upload_stream_set, 1);
77
+ rb_define_method(cCurlUpload, "stream", ruby_curl_upload_stream_get, 0);
78
+ rb_define_method(cCurlUpload, "offset=", ruby_curl_upload_offset_set, 1);
79
+ rb_define_method(cCurlUpload, "offset", ruby_curl_upload_offset_get, 0);
80
+ }
data/ext/curb_upload.h ADDED
@@ -0,0 +1,30 @@
1
+ /* curb_upload.h - Curl upload handle
2
+ * Copyright (c)2009 Todd A Fisher.
3
+ * Licensed under the Ruby License. See LICENSE for details.
4
+ */
5
+ #ifndef __CURB_UPLOAD_H
6
+ #define __CURB_UPLOAD_H
7
+
8
+ #include "curb.h"
9
+
10
+ #include <curl/easy.h>
11
+
12
+ /*
13
+ * Maintain the state of an upload e.g. for putting large streams with very little memory
14
+ * out to a server. via PUT requests
15
+ */
16
+ typedef struct {
17
+ VALUE stream;
18
+ size_t offset;
19
+ } ruby_curl_upload;
20
+
21
+ extern VALUE cCurlUpload;
22
+ void init_curb_upload();
23
+
24
+ VALUE ruby_curl_upload_new(VALUE klass);
25
+ VALUE ruby_curl_upload_stream_set(VALUE self, VALUE stream);
26
+ VALUE ruby_curl_upload_stream_get(VALUE self);
27
+ VALUE ruby_curl_upload_offset_set(VALUE self, VALUE offset);
28
+ VALUE ruby_curl_upload_offset_get(VALUE self);
29
+
30
+ #endif
data/ext/extconf.rb ADDED
@@ -0,0 +1,177 @@
1
+ require 'mkmf'
2
+ puts $CFLAGS.inspect
3
+
4
+ dir_config('curl')
5
+
6
+ if find_executable('curl-config')
7
+ $CFLAGS << " #{`curl-config --cflags`.strip} -g"
8
+ if ENV['STATIC_BUILD']
9
+ $LIBS << " #{`curl-config --static-libs`.strip}"
10
+ else
11
+ $LIBS << " #{`curl-config --libs`.strip}"
12
+ end
13
+ ca_bundle_path=`curl-config --ca`.strip
14
+ if !ca_bundle_path.nil? and ca_bundle_path != ''
15
+ $defs.push( %{-D HAVE_CURL_CONFIG_CA} )
16
+ $defs.push( %{-D CURL_CONFIG_CA='#{ca_bundle_path.inspect}'} )
17
+ end
18
+ elsif !have_library('curl') or !have_header('curl/curl.h')
19
+ fail <<-EOM
20
+ Can't find libcurl or curl/curl.h
21
+
22
+ Try passing --with-curl-dir or --with-curl-lib and --with-curl-include
23
+ options to extconf.
24
+ EOM
25
+ end
26
+
27
+ # Check arch flags
28
+ # TODO: detect mismatched arch types when libcurl mac ports is mixed with native mac ruby or vice versa
29
+ #archs = $CFLAGS.scan(/-arch\s(.*?)\s/).first # get the first arch flag
30
+ #if archs and archs.size >= 1
31
+ # # need to reduce the number of archs...
32
+ # # guess the first one is correct... at least the first one is probably the ruby installed arch...
33
+ # # this could lead to compiled binaries that crash at runtime...
34
+ # $CFLAGS.gsub!(/-arch\s(.*?)\s/,' ')
35
+ # $CFLAGS << " -arch #{archs.first}"
36
+ # puts "Selected arch: #{archs.first}"
37
+ #end
38
+
39
+ def define(s)
40
+ $defs.push( format("-D HAVE_%s", s.to_s.upcase) )
41
+ end
42
+
43
+ def have_constant(name)
44
+ checking_for name do
45
+ src = %{
46
+ #include <curl/curl.h>
47
+ int main() {
48
+ int test = (int)#{name.upcase};
49
+ return 0;
50
+ }
51
+ }
52
+ if try_compile(src,"#{$CFLAGS} #{$LIBS}")
53
+ define name
54
+ true
55
+ else
56
+ false
57
+ end
58
+ end
59
+ end
60
+
61
+ have_constant "curlinfo_redirect_time"
62
+ have_constant "curlinfo_response_code"
63
+ have_constant "curlinfo_filetime"
64
+ have_constant "curlinfo_redirect_count"
65
+ have_constant "curlinfo_os_errno"
66
+ have_constant "curlinfo_num_connects"
67
+ have_constant "curlinfo_ftp_entry_path"
68
+ have_constant "curl_version_ssl"
69
+ have_constant "curl_version_libz"
70
+ have_constant "curl_version_ntlm"
71
+ have_constant "curl_version_gssnegotiate"
72
+ have_constant "curl_version_debug"
73
+ have_constant "curl_version_asynchdns"
74
+ have_constant "curl_version_spnego"
75
+ have_constant "curl_version_largefile"
76
+ have_constant "curl_version_idn"
77
+ have_constant "curl_version_sspi"
78
+ have_constant "curl_version_conv"
79
+ have_constant "curlproxy_http"
80
+ have_constant "curlproxy_socks4"
81
+ have_constant "curlproxy_socks5"
82
+ have_constant "curlauth_basic"
83
+ have_constant "curlauth_digest"
84
+ have_constant "curlauth_gssnegotiate"
85
+ have_constant "curlauth_ntlm"
86
+ have_constant "curlauth_anysafe"
87
+ have_constant "curlauth_any"
88
+ have_constant "curle_tftp_notfound"
89
+ have_constant "curle_tftp_perm"
90
+ have_constant "curle_tftp_diskfull"
91
+ have_constant "curle_tftp_illegal"
92
+ have_constant "curle_tftp_unknownid"
93
+ have_constant "curle_tftp_exists"
94
+ have_constant "curle_tftp_nosuchuser"
95
+ # older versions of libcurl 7.12
96
+ have_constant "curle_send_fail_rewind"
97
+ have_constant "curle_ssl_engine_initfailed"
98
+ have_constant "curle_login_denied"
99
+
100
+ # older than 7.16.3
101
+ have_constant "curlmopt_maxconnects"
102
+
103
+ # additional consts
104
+ have_constant "curle_conv_failed"
105
+ have_constant "curle_conv_reqd"
106
+ have_constant "curle_ssl_cacert_badfile"
107
+ have_constant "curle_remote_file_not_found"
108
+ have_constant "curle_ssh"
109
+ have_constant "curle_ssl_shutdown_failed"
110
+ have_constant "curle_again"
111
+ have_constant "curle_ssl_crl_badfile"
112
+ have_constant "curle_ssl_issuer_error"
113
+
114
+ # username/password added in 7.19.1
115
+ have_constant "curlopt_username"
116
+ have_constant "curlopt_password"
117
+ have_constant "curlinfo_primary_ip"
118
+
119
+ # ie quirk added in 7.19.3
120
+ have_constant "curlauth_digest_ie"
121
+
122
+ # added in 7.15.1
123
+ have_constant "curlftpmethod_multicwd"
124
+ have_constant "curlftpmethod_nocwd"
125
+ have_constant "curlftpmethod_singlecwd"
126
+
127
+ # centos 4.5 build of libcurl
128
+ have_constant "curlm_bad_socket"
129
+ have_constant "curlm_unknown_option"
130
+ have_func("curl_multi_timeout")
131
+ have_func("curl_multi_fdset")
132
+ have_func("curl_multi_perform")
133
+
134
+ if try_compile('int main() { return 0; }','-Wall')
135
+ $CFLAGS << ' -Wall'
136
+ end
137
+
138
+ # do some checking to detect ruby 1.8 hash.c vs ruby 1.9 hash.c
139
+ def test_for(name, const, src)
140
+ checking_for name do
141
+ if try_compile(src,"#{$CFLAGS} #{$LIBS}")
142
+ define const
143
+ true
144
+ else
145
+ false
146
+ end
147
+ end
148
+ end
149
+ test_for("Ruby 1.9 Hash", "RUBY19_HASH", %{
150
+ #include <ruby.h>
151
+ int main() {
152
+ VALUE hash = rb_hash_new();
153
+ if( RHASH(hash)->ntbl->num_entries ) {
154
+ return 0;
155
+ }
156
+ return 1;
157
+ }
158
+ })
159
+ test_for("Ruby 1.9 st.h", "RUBY19_ST_H", %{
160
+ #include <ruby.h>
161
+ #include <ruby/st.h>
162
+ int main() {
163
+ return 0;
164
+ }
165
+ })
166
+
167
+ test_for("curl_easy_escape", "CURL_EASY_ESCAPE", %{
168
+ #include <curl/curl.h>
169
+ int main() {
170
+ CURL *easy = curl_easy_init();
171
+ curl_easy_escape(easy,"hello",5);
172
+ return 0;
173
+ }
174
+ })
175
+
176
+ create_header('curb_config.h')
177
+ create_makefile('curb_core')
data/lib/curb.rb ADDED
@@ -0,0 +1,255 @@
1
+ require 'curb_core'
2
+
3
+ module Curl
4
+
5
+ class Easy
6
+ class << self
7
+
8
+ # call-seq:
9
+ # Curl::Easy.download(url, filename = url.split(/\?/).first.split(/\//).last) { |curl| ... }
10
+ #
11
+ # Stream the specified url (via perform) and save the data directly to the
12
+ # supplied filename (defaults to the last component of the URL path, which will
13
+ # usually be the filename most simple urls).
14
+ #
15
+ # If a block is supplied, it will be passed the curl instance prior to the
16
+ # perform call.
17
+ #
18
+ # *Note* that the semantics of the on_body handler are subtly changed when using
19
+ # download, to account for the automatic routing of data to the specified file: The
20
+ # data string is passed to the handler *before* it is written
21
+ # to the file, allowing the handler to perform mutative operations where
22
+ # necessary. As usual, the transfer will be aborted if the on_body handler
23
+ # returns a size that differs from the data chunk size - in this case, the
24
+ # offending chunk will *not* be written to the file, the file will be closed,
25
+ # and a Curl::Err::AbortedByCallbackError will be raised.
26
+ def download(url, filename = url.split(/\?/).first.split(/\//).last, &blk)
27
+ curl = Curl::Easy.new(url, &blk)
28
+
29
+ File.open(filename, "wb") do |output|
30
+ old_on_body = curl.on_body do |data|
31
+ result = old_on_body ? old_on_body.call(data) : data.length
32
+ output << data if result == data.length
33
+ result
34
+ end
35
+
36
+ curl.perform
37
+ end
38
+
39
+ return curl
40
+ end
41
+ end
42
+
43
+ # Allow the incoming cert string to be file:password
44
+ # but be careful to not use a colon from a windows file path
45
+ # as the split point. Mimic what curl's main does
46
+ alias_method :native_cert=, :cert=
47
+ def cert=(cert_file)
48
+ pos = cert_file.rindex(':')
49
+ if pos && pos > 1
50
+ self.native_cert= cert_file[0..pos-1]
51
+ self.certpassword= cert_file[pos+1..-1]
52
+ else
53
+ self.native_cert= cert_file
54
+ end
55
+ self.cert
56
+ end
57
+ end
58
+
59
+ class Multi
60
+ class << self
61
+ # call-seq:
62
+ # Curl::Multi.get('url1','url2','url3','url4','url5', :follow_location => true) do|easy|
63
+ # easy
64
+ # end
65
+ #
66
+ # Blocking call to fetch multiple url's in parallel.
67
+ def get(urls, easy_options={}, multi_options={}, &blk)
68
+ url_confs = []
69
+ urls.each do|url|
70
+ url_confs << {:url => url, :method => :get}.merge(easy_options)
71
+ end
72
+ self.http(url_confs, multi_options) {|c,code,method| blk.call(c) }
73
+ end
74
+
75
+ # call-seq:
76
+ #
77
+ # Curl::Multi.post([{:url => 'url1', :post_fields => {'field1' => 'value1', 'field2' => 'value2'}},
78
+ # {:url => 'url2', :post_fields => {'field1' => 'value1', 'field2' => 'value2'}},
79
+ # {:url => 'url3', :post_fields => {'field1' => 'value1', 'field2' => 'value2'}}],
80
+ # { :follow_location => true, :multipart_form_post => true },
81
+ # {:pipeline => true }) do|easy|
82
+ # easy_handle_on_request_complete
83
+ # end
84
+ #
85
+ # Blocking call to POST multiple form's in parallel.
86
+ #
87
+ # urls_with_config: is a hash of url's pointing to the postfields to send
88
+ # easy_options: are a set of common options to set on all easy handles
89
+ # multi_options: options to set on the Curl::Multi handle
90
+ #
91
+ def post(urls_with_config, easy_options={}, multi_options={}, &blk)
92
+ url_confs = []
93
+ urls_with_config.each do|uconf|
94
+ url_confs << uconf.merge(:method => :post).merge(easy_options)
95
+ end
96
+ self.http(url_confs, multi_options) {|c,code,method| blk.call(c) }
97
+ end
98
+
99
+ # call-seq:
100
+ #
101
+ # Curl::Multi.put([{:url => 'url1', :put_data => "some message"},
102
+ # {:url => 'url2', :put_data => IO.read('filepath')},
103
+ # {:url => 'url3', :put_data => "maybe another string or socket?"],
104
+ # {:follow_location => true},
105
+ # {:pipeline => true }) do|easy|
106
+ # easy_handle_on_request_complete
107
+ # end
108
+ #
109
+ # Blocking call to POST multiple form's in parallel.
110
+ #
111
+ # urls_with_config: is a hash of url's pointing to the postfields to send
112
+ # easy_options: are a set of common options to set on all easy handles
113
+ # multi_options: options to set on the Curl::Multi handle
114
+ #
115
+ def put(urls_with_config, easy_options={}, multi_options={}, &blk)
116
+ url_confs = []
117
+ urls_with_config.each do|uconf|
118
+ url_confs << uconf.merge(:method => :put).merge(easy_options)
119
+ end
120
+ self.http(url_confs, multi_options) {|c,code,method| blk.call(c) }
121
+ end
122
+
123
+
124
+ # call-seq:
125
+ #
126
+ # Curl::Multi.http( [
127
+ # { :url => 'url1', :method => :post,
128
+ # :post_fields => {'field1' => 'value1', 'field2' => 'value2'} },
129
+ # { :url => 'url2', :method => :get,
130
+ # :follow_location => true, :max_redirects => 3 },
131
+ # { :url => 'url3', :method => :put, :put_data => File.open('file.txt','rb') },
132
+ # { :url => 'url4', :method => :head }
133
+ # ], {:pipeline => true})
134
+ #
135
+ # Blocking call to issue multiple HTTP requests with varying verb's.
136
+ #
137
+ # urls_with_config: is a hash of url's pointing to the easy handle options as well as the special option :method, that can by one of [:get, :post, :put, :delete, :head], when no verb is provided e.g. :method => nil -> GET is used
138
+ # multi_options: options for the multi handle
139
+ # blk: a callback, that yeilds when a handle is completed
140
+ #
141
+ def http(urls_with_config, multi_options={}, &blk)
142
+ m = Curl::Multi.new
143
+ # configure the multi handle
144
+ multi_options.each { |k,v| m.send("#{k}=", v) }
145
+ callbacks = [:on_progress,:on_debug,:on_failure,:on_success,:on_body,:on_header]
146
+
147
+ urls_with_config.each do|conf|
148
+ c = conf.dup # avoid being destructive to input
149
+ url = c.delete(:url)
150
+ method = c.delete(:method)
151
+ headers = c.delete(:headers)
152
+
153
+ easy = Curl::Easy.new(url)
154
+
155
+ # assign callbacks
156
+ callbacks.each do |cb|
157
+ cbproc = c.delete(cb)
158
+ easy.send(cb,&cbproc) if cbproc
159
+ end
160
+
161
+ case method
162
+ when :post
163
+ fields = c.delete(:post_fields)
164
+ # set the post post using the url fields
165
+ easy.post_body = fields.map{|f,k| "#{easy.escape(f)}=#{easy.escape(k)}"}.join('&')
166
+ when :put
167
+ easy.put_data = c.delete(:put_data)
168
+ when :head
169
+ easy.head = true
170
+ when :delete
171
+ easy.delete = true
172
+ when :get
173
+ else
174
+ # XXX: nil is treated like a GET
175
+ end
176
+
177
+ # headers is a special key
178
+ headers.each {|k,v| easy.headers[k] = v } if headers
179
+
180
+ #
181
+ # use the remaining options as specific configuration to the easy handle
182
+ # bad options should raise an undefined method error
183
+ #
184
+ c.each { |k,v| easy.send("#{k}=",v) }
185
+
186
+ easy.on_complete {|curl,code| blk.call(curl,code,method) } if blk
187
+ m.add(easy)
188
+ end
189
+ m.perform
190
+ end
191
+
192
+ # call-seq:
193
+ #
194
+ # Curl::Multi.download(['http://example.com/p/a/t/h/file1.txt','http://example.com/p/a/t/h/file2.txt']){|c|}
195
+ #
196
+ # will create 2 new files file1.txt and file2.txt
197
+ #
198
+ # 2 files will be opened, and remain open until the call completes
199
+ #
200
+ # when using the :post or :put method, urls should be a hash, including the individual post fields per post
201
+ #
202
+ def download(urls,easy_options={},multi_options={},download_paths=nil,&blk)
203
+ procs = []
204
+ files = []
205
+ urls_with_config = []
206
+ url_to_download_paths = {}
207
+
208
+ urls.each_with_index do|urlcfg,i|
209
+ if urlcfg.is_a?(Hash)
210
+ url = url[:url]
211
+ else
212
+ url = urlcfg
213
+ end
214
+
215
+ if download_paths and download_paths[i]
216
+ download_path = download_paths[i]
217
+ else
218
+ download_path = File.basename(url)
219
+ end
220
+
221
+ lambda do|dp|
222
+ file = File.open(dp,"wb")
223
+ procs << (lambda {|data| file.write data; data.size })
224
+ files << file
225
+ end.call(download_path)
226
+
227
+ if urlcfg.is_a?(Hash)
228
+ urls_with_config << urlcfg.merge({:on_body => procs.last}.merge(easy_options))
229
+ else
230
+ urls_with_config << {:url => url, :on_body => procs.last, :method => :get}.merge(easy_options)
231
+ end
232
+ url_to_download_paths[url] = download_path # store for later
233
+ end
234
+
235
+ if blk
236
+ Curl::Multi.http(urls_with_config, multi_options) {|c,code,method| blk.call(c,url_to_download_paths[c.url]) }
237
+ else
238
+ Curl::Multi.http(urls_with_config, multi_options)
239
+ end
240
+
241
+ ensure
242
+ errors = []
243
+ files.each {|f|
244
+ begin
245
+ f.close
246
+ rescue => e
247
+ errors << e
248
+ end
249
+ }
250
+ raise errors unless errors.empty?
251
+ end
252
+
253
+ end
254
+ end
255
+ end