ext/curb-original/curb_postfield.c in rhack-1.1.8 vs ext/curb-original/curb_postfield.c in rhack-1.2.0
- old
+ new
@@ -1,523 +1,523 @@
-/* curb_postfield.c - Field class for POST method
- * Copyright (c)2006 Ross Bamford.
- * Licensed under the Ruby License. See LICENSE for details.
- *
- * $Id: curb_postfield.c 30 2006-12-09 12:30:24Z roscopeco $
- */
-#include "curb_postfield.h"
-#include "curb_errors.h"
-
-extern VALUE mCurl;
-
-static VALUE idCall;
-
-#ifdef RDOC_NEVER_DEFINED
- mCurl = rb_define_module("Curl");
-#endif
-
-VALUE cCurlPostField;
-
-
-/* ================= APPEND FORM FUNC ================ */
-
-/* This gets called by the post method on Curl::Easy for each postfield
- * supplied in the arguments. It's job is to add the supplied field to
- * the list that's being built for a perform.
- *
- * THIS FUNC MODIFIES ITS ARGUMENTS. See curl_formadd(3) for details.
- */
-void append_to_form(VALUE self,
- struct curl_httppost **first,
- struct curl_httppost **last) {
- ruby_curl_postfield *rbcpf;
- CURLFORMcode result = -1;
-
- Data_Get_Struct(self, ruby_curl_postfield, rbcpf);
-
- if (rbcpf->name == Qnil) {
- rb_raise(eCurlErrInvalidPostField, "Cannot post unnamed field");
- } else {
- if ((rbcpf->local_file != Qnil) || (rbcpf->remote_file != Qnil)) {
- // is a file upload field
- if (rbcpf->content_proc != Qnil) {
- // with content proc
- rbcpf->buffer_str = rb_funcall(rbcpf->content_proc, idCall, 1, self);
-
- if (rbcpf->remote_file == Qnil) {
- rb_raise(eCurlErrInvalidPostField, "Cannot post file upload field with no filename");
- } else {
- if (rbcpf->content_type == Qnil) {
- result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name),
- CURLFORM_BUFFER, StringValuePtr(rbcpf->remote_file),
- CURLFORM_BUFFERPTR, StringValuePtr(rbcpf->buffer_str),
- CURLFORM_BUFFERLENGTH, RSTRING_LEN(rbcpf->buffer_str),
- CURLFORM_END);
- } else {
- result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name),
- CURLFORM_BUFFER, StringValuePtr(rbcpf->remote_file),
- CURLFORM_BUFFERPTR, StringValuePtr(rbcpf->buffer_str),
- CURLFORM_BUFFERLENGTH, RSTRING_LEN(rbcpf->buffer_str),
- CURLFORM_CONTENTTYPE, StringValuePtr(rbcpf->content_type),
- CURLFORM_END);
- }
- }
- } else if (rbcpf->content != Qnil) {
- // with content
- if (rbcpf->remote_file == Qnil) {
- rb_raise(eCurlErrInvalidPostField, "Cannot post file upload field with no filename");
- } else {
- if (rbcpf->content_type == Qnil) {
- result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name),
- CURLFORM_BUFFER, StringValuePtr(rbcpf->remote_file),
- CURLFORM_BUFFERPTR, StringValuePtr(rbcpf->content),
- CURLFORM_BUFFERLENGTH, RSTRING_LEN(rbcpf->content),
- CURLFORM_END);
- } else {
- result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name),
- CURLFORM_BUFFER, StringValuePtr(rbcpf->remote_file),
- CURLFORM_BUFFERPTR, StringValuePtr(rbcpf->content),
- CURLFORM_BUFFERLENGTH, RSTRING_LEN(rbcpf->content),
- CURLFORM_CONTENTTYPE, StringValuePtr(rbcpf->content_type),
- CURLFORM_END);
- }
- }
- } else if (rbcpf->local_file != Qnil) {
- // with local filename
- if (rbcpf->local_file == Qnil) {
- rb_raise(eCurlErrInvalidPostField, "Cannot post file upload field no filename");
- } else {
- if (rbcpf->remote_file == Qnil) {
- rbcpf->remote_file = rbcpf->local_file;
- }
-
- if (rbcpf->content_type == Qnil) {
- result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name),
- CURLFORM_FILE, StringValuePtr(rbcpf->local_file),
- CURLFORM_FILENAME, StringValuePtr(rbcpf->remote_file),
- CURLFORM_END);
- } else {
- result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name),
- CURLFORM_FILE, StringValuePtr(rbcpf->local_file),
- CURLFORM_FILENAME, StringValuePtr(rbcpf->remote_file),
- CURLFORM_CONTENTTYPE, StringValuePtr(rbcpf->content_type),
- CURLFORM_END);
- }
- }
- } else {
- rb_raise(eCurlErrInvalidPostField, "Cannot post file upload field with no data");
- }
- } else {
- // is a content field
- if (rbcpf->content_proc != Qnil) {
- rbcpf->buffer_str = rb_funcall(rbcpf->content_proc, idCall, 1, self);
-
- if (rbcpf->content_type == Qnil) {
- result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name),
- CURLFORM_PTRCONTENTS, StringValuePtr(rbcpf->buffer_str),
- CURLFORM_CONTENTSLENGTH, RSTRING_LEN(rbcpf->buffer_str),
- CURLFORM_END);
- } else {
- result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name),
- CURLFORM_PTRCONTENTS, StringValuePtr(rbcpf->buffer_str),
- CURLFORM_CONTENTSLENGTH, RSTRING_LEN(rbcpf->buffer_str),
- CURLFORM_CONTENTTYPE, StringValuePtr(rbcpf->content_type),
- CURLFORM_END);
- }
- } else if (rbcpf->content != Qnil) {
- if (rbcpf->content_type == Qnil) {
- result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name),
- CURLFORM_PTRCONTENTS, StringValuePtr(rbcpf->content),
- CURLFORM_CONTENTSLENGTH, RSTRING_LEN(rbcpf->content),
- CURLFORM_END);
- } else {
- result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name),
- CURLFORM_PTRCONTENTS, StringValuePtr(rbcpf->content),
- CURLFORM_CONTENTSLENGTH, RSTRING_LEN(rbcpf->content),
- CURLFORM_CONTENTTYPE, StringValuePtr(rbcpf->content_type),
- CURLFORM_END);
- }
- } else {
- rb_raise(eCurlErrInvalidPostField, "Cannot post content field with no data");
- }
- }
- }
-
- if (result != 0) {
- const char *reason = NULL;
-
- switch (result) {
- case CURL_FORMADD_MEMORY:
- reason = "Memory allocation failed";
- break;
- case CURL_FORMADD_OPTION_TWICE:
- reason = "Duplicate option";
- break;
- case CURL_FORMADD_NULL:
- reason = "Unexpected NULL string";
- break;
- case CURL_FORMADD_UNKNOWN_OPTION:
- reason = "Unknown option";
- break;
- case CURL_FORMADD_INCOMPLETE:
- reason = "Incomplete form data";
- break;
- case CURL_FORMADD_ILLEGAL_ARRAY:
- reason = "Illegal array [BINDING BUG]";
- break;
- case CURL_FORMADD_DISABLED:
- reason = "Installed libcurl cannot support requested feature(s)";
- break;
- default:
- reason = "Unknown error";
- }
-
- rb_raise(eCurlErrInvalidPostField, "Failed to add field (%s)", reason);
- }
-}
-
-
-/* ================== MARK/FREE FUNC ==================*/
-void curl_postfield_mark(ruby_curl_postfield *rbcpf) {
- rb_gc_mark(rbcpf->name);
- rb_gc_mark(rbcpf->content);
- rb_gc_mark(rbcpf->content_type);
- rb_gc_mark(rbcpf->local_file);
- rb_gc_mark(rbcpf->remote_file);
- rb_gc_mark(rbcpf->buffer_str);
-}
-
-void curl_postfield_free(ruby_curl_postfield *rbcpf) {
- free(rbcpf);
-}
-
-
-/* ================= ALLOC METHODS ====================*/
-
-/*
- * call-seq:
- * Curl::PostField.content(name, content) => #<Curl::PostField...>
- * Curl::PostField.content(name, content, content_type = nil) => #<Curl::PostField...>
- * Curl::PostField.content(name, content_type = nil) { |field| ... } => #<Curl::PostField...>
- *
- * Create a new Curl::PostField, supplying the field name, content,
- * and, optionally, Content-type (curl will attempt to determine this if
- * not specified).
- *
- * The block form allows a block to supply the content for this field, called
- * during the perform. The block should return a ruby string with the field
- * data.
- */
-static VALUE ruby_curl_postfield_new_content(int argc, VALUE *argv, VALUE klass) {
- ruby_curl_postfield *rbcpf = ALLOC(ruby_curl_postfield);
-
- // wierdness - we actually require two args, unless a block is provided, but
- // we have to work that out below.
- rb_scan_args(argc, argv, "12&", &rbcpf->name, &rbcpf->content, &rbcpf->content_type, &rbcpf->content_proc);
-
- // special handling if theres a block, second arg is actually content_type
- if (rbcpf->content_proc != Qnil) {
- if (rbcpf->content != Qnil) {
- // we were given a content-type
- rbcpf->content_type = rbcpf->content;
- rbcpf->content = Qnil;
- } else {
- // default content type
- rbcpf->content_type = Qnil;
- }
- } else {
- // no block, so make sure content was provided
- if (rbcpf->content == Qnil) {
- rb_raise(rb_eArgError, "Incorrect number of arguments (expected 2 or 3)");
- }
- }
-
- /* assoc objects */
- rbcpf->local_file = Qnil;
- rbcpf->remote_file = Qnil;
- rbcpf->buffer_str = Qnil;
-
- return Data_Wrap_Struct(cCurlPostField, curl_postfield_mark, curl_postfield_free, rbcpf);
-}
-
-/*
- * call-seq:
- * Curl::PostField.file(name, local_file_name) => #<Curl::PostField...>
- * Curl::PostField.file(name, local_file_name, remote_file_name = local_file_name) => #<Curl::PostField...>
- * Curl::PostField.file(name, remote_file_name) { |field| ... } => #<Curl::PostField...>
- *
- * Create a new Curl::PostField for a file upload field, supplying the local filename
- * to read from, and optionally the remote filename (defaults to the local name).
- *
- * The block form allows a block to supply the content for this field, called
- * during the perform. The block should return a ruby string with the field
- * data.
- */
-static VALUE ruby_curl_postfield_new_file(int argc, VALUE *argv, VALUE klass) {
- // TODO needs to handle content-type too
- ruby_curl_postfield *rbcpf = ALLOC(ruby_curl_postfield);
-
- rb_scan_args(argc, argv, "21&", &rbcpf->name, &rbcpf->local_file, &rbcpf->remote_file, &rbcpf->content_proc);
-
- // special handling if theres a block, second arg is actually remote name.
- if (rbcpf->content_proc != Qnil) {
- if (rbcpf->local_file != Qnil) {
- // we were given a local file
- if (rbcpf->remote_file == Qnil) {
- // we weren't given a remote, so local is actually remote
- // (correct block call form)
- rbcpf->remote_file = rbcpf->local_file;
- }
-
- // Shouldn't get a local file, so can ignore it.
- rbcpf->local_file = Qnil;
- }
- } else {
- if (rbcpf->remote_file == Qnil) {
- rbcpf->remote_file = rbcpf->local_file;
- }
- }
-
- /* assoc objects */
- rbcpf->content = Qnil;
- rbcpf->content_type = Qnil;
- rbcpf->buffer_str = Qnil;
-
- return Data_Wrap_Struct(cCurlPostField, curl_postfield_mark, curl_postfield_free, rbcpf);
-}
-
-/* ================= ATTRIBUTES ====================*/
-
-/*
- * call-seq:
- * field.name = "name" => "name"
- *
- * Set the POST field name for this PostField.
- */
-static VALUE ruby_curl_postfield_name_set(VALUE self, VALUE name) {
- CURB_OBJECT_SETTER(ruby_curl_postfield, name);
-}
-
-/*
- * call-seq:
- * field.name => "name"
- *
- * Obtain the POST field name for this PostField.
- */
-static VALUE ruby_curl_postfield_name_get(VALUE self) {
- CURB_OBJECT_GETTER(ruby_curl_postfield, name);
-}
-
-/*
- * call-seq:
- * field.content = "content" => "content"
- *
- * Set the POST field content for this PostField. Ignored when a
- * content_proc is supplied via either +Curl::PostField.file+ or
- * +set_content_proc+.
- */
-static VALUE ruby_curl_postfield_content_set(VALUE self, VALUE content) {
- CURB_OBJECT_SETTER(ruby_curl_postfield, content);
-}
-
-/*
- * call-seq:
- * field.content => "content"
- *
- * Obtain the POST field content for this PostField.
- */
-static VALUE ruby_curl_postfield_content_get(VALUE self) {
- CURB_OBJECT_GETTER(ruby_curl_postfield, content);
-}
-
-/*
- * call-seq:
- * field.content_type = "content_type" => "content_type"
- *
- * Set the POST field Content-type for this PostField.
- */
-static VALUE ruby_curl_postfield_content_type_set(VALUE self, VALUE content_type) {
- CURB_OBJECT_SETTER(ruby_curl_postfield, content_type);
-}
-
-/*
- * call-seq:
- * field.content_type => "content_type"
- *
- * Get the POST field Content-type for this PostField.
- */
-static VALUE ruby_curl_postfield_content_type_get(VALUE self) {
- CURB_OBJECT_GETTER(ruby_curl_postfield, content_type);
-}
-
-/*
- * call-seq:
- * field.local_file = "filename" => "filename"
- *
- * Set the POST field local filename for this PostField (when performing
- * a file upload). Ignored when a content_proc is supplied via either
- * +Curl::PostField.file+ or +set_content_proc+.
- */
-static VALUE ruby_curl_postfield_local_file_set(VALUE self, VALUE local_file) {
- CURB_OBJECT_SETTER(ruby_curl_postfield, local_file);
-}
-
-/*
- * call-seq:
- * field.local_file => "filename"
- *
- * Get the POST field local filename for this PostField (when performing
- * a file upload).
- */
-static VALUE ruby_curl_postfield_local_file_get(VALUE self) {
- CURB_OBJECT_GETTER(ruby_curl_postfield, local_file);
-}
-
-/*
- * call-seq:
- * field.remote_file = "filename" => "filename"
- *
- * Set the POST field remote filename for this PostField (when performing
- * a file upload). If no remote filename is provided, and no content_proc
- * is supplied, the local filename is used. If no remote filename is
- * specified when a content_proc is used, an exception will be raised
- * during the perform.
- */
-static VALUE ruby_curl_postfield_remote_file_set(VALUE self, VALUE remote_file) {
- CURB_OBJECT_SETTER(ruby_curl_postfield, remote_file);
-}
-
-/*
- * call-seq:
- * field.local_file => "filename"
- *
- * Get the POST field remote filename for this PostField (when performing
- * a file upload).
- */
-static VALUE ruby_curl_postfield_remote_file_get(VALUE self) {
- CURB_OBJECT_GETTER(ruby_curl_postfield, remote_file);
-}
-
-/*
- * call-seq:
- * field.set_content_proc { |field| ... } => <old proc>
- *
- * Set a content proc for this field. This proc will be called during the
- * perform to supply the content for this field, overriding any setting
- * of +content+ or +local_file+.
- */
-static VALUE ruby_curl_postfield_content_proc_set(int argc, VALUE *argv, VALUE self) {
- CURB_HANDLER_PROC_SETTER(ruby_curl_postfield, content_proc);
-}
-
-/*
- * call-seq:
- * field.to_str => "name=value"
- * field.to_s => "name=value"
- *
- * Obtain a String representation of this PostField in url-encoded
- * format. This is used to construct the post data for non-multipart
- * POSTs.
- *
- * Only content fields may be converted to strings.
- */
-static VALUE ruby_curl_postfield_to_str(VALUE self) {
- // FIXME This is using the deprecated curl_escape func
- ruby_curl_postfield *rbcpf;
- VALUE result = Qnil;
- VALUE name = Qnil;
- char *tmpchrs;
-
- Data_Get_Struct(self, ruby_curl_postfield, rbcpf);
-
- if (rbcpf->name != Qnil) {
- name = rbcpf->name;
- if (rb_type(name) == T_STRING) {
- name = rbcpf->name;
- } else if (rb_respond_to(name,rb_intern("to_s"))) {
- name = rb_funcall(name, rb_intern("to_s"), 0);
- }
- else {
- name = Qnil; // we can't handle this object
- }
- }
- if (name == Qnil) {
- rb_raise(eCurlErrInvalidPostField, "Cannot convert unnamed field to string %s:%d, make sure your field name responds_to :to_s", __FILE__, __LINE__);
- }
-
- tmpchrs = curl_escape(StringValuePtr(name), (int)RSTRING_LEN(name));
-
- if (!tmpchrs) {
- rb_raise(eCurlErrInvalidPostField, "Failed to url-encode name `%s'", tmpchrs);
- } else {
- VALUE tmpcontent = Qnil;
- VALUE escd_name = rb_str_new2(tmpchrs);
- curl_free(tmpchrs);
-
- if (rbcpf->content_proc != Qnil) {
- tmpcontent = rb_funcall(rbcpf->content_proc, idCall, 1, self);
- } else if (rbcpf->content != Qnil) {
- tmpcontent = rbcpf->content;
- } else if (rbcpf->local_file != Qnil) {
- tmpcontent = rbcpf->local_file;
- } else if (rbcpf->remote_file != Qnil) {
- tmpcontent = rbcpf->remote_file;
- } else {
- tmpcontent = rb_str_new2("");
- }
- if (TYPE(tmpcontent) != T_STRING) {
- if (rb_respond_to(tmpcontent, rb_intern("to_s"))) {
- tmpcontent = rb_funcall(tmpcontent, rb_intern("to_s"), 0);
- }
- else {
- rb_raise(rb_eRuntimeError, "postfield(%s) is not a string and does not respond_to to_s", RSTRING_PTR(escd_name) );
- }
- }
- //fprintf(stderr, "encoding content: %ld - %s\n", RSTRING_LEN(tmpcontent), RSTRING_PTR(tmpcontent) );
- tmpchrs = curl_escape(RSTRING_PTR(tmpcontent), (int)RSTRING_LEN(tmpcontent));
- if (!tmpchrs) {
- rb_raise(eCurlErrInvalidPostField, "Failed to url-encode content `%s'", tmpchrs);
- } else {
- VALUE escd_content = rb_str_new2(tmpchrs);
- curl_free(tmpchrs);
-
- result = escd_name;
- rb_str_cat(result, "=", 1);
- rb_str_concat(result, escd_content);
- }
- }
-
- return result;
-}
-
-
-/* =================== INIT LIB =====================*/
-void init_curb_postfield() {
- VALUE sc;
-
- idCall = rb_intern("call");
-
- cCurlPostField = rb_define_class_under(mCurl, "PostField", rb_cObject);
-
- /* Class methods */
- rb_define_singleton_method(cCurlPostField, "content", ruby_curl_postfield_new_content, -1);
- rb_define_singleton_method(cCurlPostField, "file", ruby_curl_postfield_new_file, -1);
-
- sc = rb_singleton_class(cCurlPostField);
- rb_undef(sc, rb_intern("new"));
-
- rb_define_method(cCurlPostField, "name=", ruby_curl_postfield_name_set, 1);
- rb_define_method(cCurlPostField, "name", ruby_curl_postfield_name_get, 0);
- rb_define_method(cCurlPostField, "content=", ruby_curl_postfield_content_set, 1);
- rb_define_method(cCurlPostField, "content", ruby_curl_postfield_content_get, 0);
- rb_define_method(cCurlPostField, "content_type=", ruby_curl_postfield_content_type_set, 1);
- rb_define_method(cCurlPostField, "content_type", ruby_curl_postfield_content_type_get, 0);
- rb_define_method(cCurlPostField, "local_file=", ruby_curl_postfield_local_file_set, 1);
- rb_define_method(cCurlPostField, "local_file", ruby_curl_postfield_local_file_get, 0);
- rb_define_method(cCurlPostField, "remote_file=", ruby_curl_postfield_remote_file_set, 1);
- rb_define_method(cCurlPostField, "remote_file", ruby_curl_postfield_remote_file_get, 0);
-
- rb_define_method(cCurlPostField, "set_content_proc", ruby_curl_postfield_content_proc_set, -1);
-
- rb_define_method(cCurlPostField, "to_str", ruby_curl_postfield_to_str, 0);
- rb_define_alias(cCurlPostField, "to_s", "to_str");
-}
+/* curb_postfield.c - Field class for POST method
+ * Copyright (c)2006 Ross Bamford.
+ * Licensed under the Ruby License. See LICENSE for details.
+ *
+ * $Id: curb_postfield.c 30 2006-12-09 12:30:24Z roscopeco $
+ */
+#include "curb_postfield.h"
+#include "curb_errors.h"
+
+extern VALUE mCurl;
+
+static VALUE idCall;
+
+#ifdef RDOC_NEVER_DEFINED
+ mCurl = rb_define_module("Curl");
+#endif
+
+VALUE cCurlPostField;
+
+
+/* ================= APPEND FORM FUNC ================ */
+
+/* This gets called by the post method on Curl::Easy for each postfield
+ * supplied in the arguments. It's job is to add the supplied field to
+ * the list that's being built for a perform.
+ *
+ * THIS FUNC MODIFIES ITS ARGUMENTS. See curl_formadd(3) for details.
+ */
+void append_to_form(VALUE self,
+ struct curl_httppost **first,
+ struct curl_httppost **last) {
+ ruby_curl_postfield *rbcpf;
+ CURLFORMcode result = -1;
+
+ Data_Get_Struct(self, ruby_curl_postfield, rbcpf);
+
+ if (rbcpf->name == Qnil) {
+ rb_raise(eCurlErrInvalidPostField, "Cannot post unnamed field");
+ } else {
+ if ((rbcpf->local_file != Qnil) || (rbcpf->remote_file != Qnil)) {
+ // is a file upload field
+ if (rbcpf->content_proc != Qnil) {
+ // with content proc
+ rbcpf->buffer_str = rb_funcall(rbcpf->content_proc, idCall, 1, self);
+
+ if (rbcpf->remote_file == Qnil) {
+ rb_raise(eCurlErrInvalidPostField, "Cannot post file upload field with no filename");
+ } else {
+ if (rbcpf->content_type == Qnil) {
+ result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name),
+ CURLFORM_BUFFER, StringValuePtr(rbcpf->remote_file),
+ CURLFORM_BUFFERPTR, StringValuePtr(rbcpf->buffer_str),
+ CURLFORM_BUFFERLENGTH, RSTRING_LEN(rbcpf->buffer_str),
+ CURLFORM_END);
+ } else {
+ result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name),
+ CURLFORM_BUFFER, StringValuePtr(rbcpf->remote_file),
+ CURLFORM_BUFFERPTR, StringValuePtr(rbcpf->buffer_str),
+ CURLFORM_BUFFERLENGTH, RSTRING_LEN(rbcpf->buffer_str),
+ CURLFORM_CONTENTTYPE, StringValuePtr(rbcpf->content_type),
+ CURLFORM_END);
+ }
+ }
+ } else if (rbcpf->content != Qnil) {
+ // with content
+ if (rbcpf->remote_file == Qnil) {
+ rb_raise(eCurlErrInvalidPostField, "Cannot post file upload field with no filename");
+ } else {
+ if (rbcpf->content_type == Qnil) {
+ result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name),
+ CURLFORM_BUFFER, StringValuePtr(rbcpf->remote_file),
+ CURLFORM_BUFFERPTR, StringValuePtr(rbcpf->content),
+ CURLFORM_BUFFERLENGTH, RSTRING_LEN(rbcpf->content),
+ CURLFORM_END);
+ } else {
+ result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name),
+ CURLFORM_BUFFER, StringValuePtr(rbcpf->remote_file),
+ CURLFORM_BUFFERPTR, StringValuePtr(rbcpf->content),
+ CURLFORM_BUFFERLENGTH, RSTRING_LEN(rbcpf->content),
+ CURLFORM_CONTENTTYPE, StringValuePtr(rbcpf->content_type),
+ CURLFORM_END);
+ }
+ }
+ } else if (rbcpf->local_file != Qnil) {
+ // with local filename
+ if (rbcpf->local_file == Qnil) {
+ rb_raise(eCurlErrInvalidPostField, "Cannot post file upload field no filename");
+ } else {
+ if (rbcpf->remote_file == Qnil) {
+ rbcpf->remote_file = rbcpf->local_file;
+ }
+
+ if (rbcpf->content_type == Qnil) {
+ result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name),
+ CURLFORM_FILE, StringValuePtr(rbcpf->local_file),
+ CURLFORM_FILENAME, StringValuePtr(rbcpf->remote_file),
+ CURLFORM_END);
+ } else {
+ result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name),
+ CURLFORM_FILE, StringValuePtr(rbcpf->local_file),
+ CURLFORM_FILENAME, StringValuePtr(rbcpf->remote_file),
+ CURLFORM_CONTENTTYPE, StringValuePtr(rbcpf->content_type),
+ CURLFORM_END);
+ }
+ }
+ } else {
+ rb_raise(eCurlErrInvalidPostField, "Cannot post file upload field with no data");
+ }
+ } else {
+ // is a content field
+ if (rbcpf->content_proc != Qnil) {
+ rbcpf->buffer_str = rb_funcall(rbcpf->content_proc, idCall, 1, self);
+
+ if (rbcpf->content_type == Qnil) {
+ result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name),
+ CURLFORM_PTRCONTENTS, StringValuePtr(rbcpf->buffer_str),
+ CURLFORM_CONTENTSLENGTH, RSTRING_LEN(rbcpf->buffer_str),
+ CURLFORM_END);
+ } else {
+ result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name),
+ CURLFORM_PTRCONTENTS, StringValuePtr(rbcpf->buffer_str),
+ CURLFORM_CONTENTSLENGTH, RSTRING_LEN(rbcpf->buffer_str),
+ CURLFORM_CONTENTTYPE, StringValuePtr(rbcpf->content_type),
+ CURLFORM_END);
+ }
+ } else if (rbcpf->content != Qnil) {
+ if (rbcpf->content_type == Qnil) {
+ result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name),
+ CURLFORM_PTRCONTENTS, StringValuePtr(rbcpf->content),
+ CURLFORM_CONTENTSLENGTH, RSTRING_LEN(rbcpf->content),
+ CURLFORM_END);
+ } else {
+ result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name),
+ CURLFORM_PTRCONTENTS, StringValuePtr(rbcpf->content),
+ CURLFORM_CONTENTSLENGTH, RSTRING_LEN(rbcpf->content),
+ CURLFORM_CONTENTTYPE, StringValuePtr(rbcpf->content_type),
+ CURLFORM_END);
+ }
+ } else {
+ rb_raise(eCurlErrInvalidPostField, "Cannot post content field with no data");
+ }
+ }
+ }
+
+ if (result != 0) {
+ const char *reason = NULL;
+
+ switch (result) {
+ case CURL_FORMADD_MEMORY:
+ reason = "Memory allocation failed";
+ break;
+ case CURL_FORMADD_OPTION_TWICE:
+ reason = "Duplicate option";
+ break;
+ case CURL_FORMADD_NULL:
+ reason = "Unexpected NULL string";
+ break;
+ case CURL_FORMADD_UNKNOWN_OPTION:
+ reason = "Unknown option";
+ break;
+ case CURL_FORMADD_INCOMPLETE:
+ reason = "Incomplete form data";
+ break;
+ case CURL_FORMADD_ILLEGAL_ARRAY:
+ reason = "Illegal array [BINDING BUG]";
+ break;
+ case CURL_FORMADD_DISABLED:
+ reason = "Installed libcurl cannot support requested feature(s)";
+ break;
+ default:
+ reason = "Unknown error";
+ }
+
+ rb_raise(eCurlErrInvalidPostField, "Failed to add field (%s)", reason);
+ }
+}
+
+
+/* ================== MARK/FREE FUNC ==================*/
+void curl_postfield_mark(ruby_curl_postfield *rbcpf) {
+ rb_gc_mark(rbcpf->name);
+ rb_gc_mark(rbcpf->content);
+ rb_gc_mark(rbcpf->content_type);
+ rb_gc_mark(rbcpf->local_file);
+ rb_gc_mark(rbcpf->remote_file);
+ rb_gc_mark(rbcpf->buffer_str);
+}
+
+void curl_postfield_free(ruby_curl_postfield *rbcpf) {
+ free(rbcpf);
+}
+
+
+/* ================= ALLOC METHODS ====================*/
+
+/*
+ * call-seq:
+ * Curl::PostField.content(name, content) => #<Curl::PostField...>
+ * Curl::PostField.content(name, content, content_type = nil) => #<Curl::PostField...>
+ * Curl::PostField.content(name, content_type = nil) { |field| ... } => #<Curl::PostField...>
+ *
+ * Create a new Curl::PostField, supplying the field name, content,
+ * and, optionally, Content-type (curl will attempt to determine this if
+ * not specified).
+ *
+ * The block form allows a block to supply the content for this field, called
+ * during the perform. The block should return a ruby string with the field
+ * data.
+ */
+static VALUE ruby_curl_postfield_new_content(int argc, VALUE *argv, VALUE klass) {
+ ruby_curl_postfield *rbcpf = ALLOC(ruby_curl_postfield);
+
+ // wierdness - we actually require two args, unless a block is provided, but
+ // we have to work that out below.
+ rb_scan_args(argc, argv, "12&", &rbcpf->name, &rbcpf->content, &rbcpf->content_type, &rbcpf->content_proc);
+
+ // special handling if theres a block, second arg is actually content_type
+ if (rbcpf->content_proc != Qnil) {
+ if (rbcpf->content != Qnil) {
+ // we were given a content-type
+ rbcpf->content_type = rbcpf->content;
+ rbcpf->content = Qnil;
+ } else {
+ // default content type
+ rbcpf->content_type = Qnil;
+ }
+ } else {
+ // no block, so make sure content was provided
+ if (rbcpf->content == Qnil) {
+ rb_raise(rb_eArgError, "Incorrect number of arguments (expected 2 or 3)");
+ }
+ }
+
+ /* assoc objects */
+ rbcpf->local_file = Qnil;
+ rbcpf->remote_file = Qnil;
+ rbcpf->buffer_str = Qnil;
+
+ return Data_Wrap_Struct(cCurlPostField, curl_postfield_mark, curl_postfield_free, rbcpf);
+}
+
+/*
+ * call-seq:
+ * Curl::PostField.file(name, local_file_name) => #<Curl::PostField...>
+ * Curl::PostField.file(name, local_file_name, remote_file_name = local_file_name) => #<Curl::PostField...>
+ * Curl::PostField.file(name, remote_file_name) { |field| ... } => #<Curl::PostField...>
+ *
+ * Create a new Curl::PostField for a file upload field, supplying the local filename
+ * to read from, and optionally the remote filename (defaults to the local name).
+ *
+ * The block form allows a block to supply the content for this field, called
+ * during the perform. The block should return a ruby string with the field
+ * data.
+ */
+static VALUE ruby_curl_postfield_new_file(int argc, VALUE *argv, VALUE klass) {
+ // TODO needs to handle content-type too
+ ruby_curl_postfield *rbcpf = ALLOC(ruby_curl_postfield);
+
+ rb_scan_args(argc, argv, "21&", &rbcpf->name, &rbcpf->local_file, &rbcpf->remote_file, &rbcpf->content_proc);
+
+ // special handling if theres a block, second arg is actually remote name.
+ if (rbcpf->content_proc != Qnil) {
+ if (rbcpf->local_file != Qnil) {
+ // we were given a local file
+ if (rbcpf->remote_file == Qnil) {
+ // we weren't given a remote, so local is actually remote
+ // (correct block call form)
+ rbcpf->remote_file = rbcpf->local_file;
+ }
+
+ // Shouldn't get a local file, so can ignore it.
+ rbcpf->local_file = Qnil;
+ }
+ } else {
+ if (rbcpf->remote_file == Qnil) {
+ rbcpf->remote_file = rbcpf->local_file;
+ }
+ }
+
+ /* assoc objects */
+ rbcpf->content = Qnil;
+ rbcpf->content_type = Qnil;
+ rbcpf->buffer_str = Qnil;
+
+ return Data_Wrap_Struct(cCurlPostField, curl_postfield_mark, curl_postfield_free, rbcpf);
+}
+
+/* ================= ATTRIBUTES ====================*/
+
+/*
+ * call-seq:
+ * field.name = "name" => "name"
+ *
+ * Set the POST field name for this PostField.
+ */
+static VALUE ruby_curl_postfield_name_set(VALUE self, VALUE name) {
+ CURB_OBJECT_SETTER(ruby_curl_postfield, name);
+}
+
+/*
+ * call-seq:
+ * field.name => "name"
+ *
+ * Obtain the POST field name for this PostField.
+ */
+static VALUE ruby_curl_postfield_name_get(VALUE self) {
+ CURB_OBJECT_GETTER(ruby_curl_postfield, name);
+}
+
+/*
+ * call-seq:
+ * field.content = "content" => "content"
+ *
+ * Set the POST field content for this PostField. Ignored when a
+ * content_proc is supplied via either +Curl::PostField.file+ or
+ * +set_content_proc+.
+ */
+static VALUE ruby_curl_postfield_content_set(VALUE self, VALUE content) {
+ CURB_OBJECT_SETTER(ruby_curl_postfield, content);
+}
+
+/*
+ * call-seq:
+ * field.content => "content"
+ *
+ * Obtain the POST field content for this PostField.
+ */
+static VALUE ruby_curl_postfield_content_get(VALUE self) {
+ CURB_OBJECT_GETTER(ruby_curl_postfield, content);
+}
+
+/*
+ * call-seq:
+ * field.content_type = "content_type" => "content_type"
+ *
+ * Set the POST field Content-type for this PostField.
+ */
+static VALUE ruby_curl_postfield_content_type_set(VALUE self, VALUE content_type) {
+ CURB_OBJECT_SETTER(ruby_curl_postfield, content_type);
+}
+
+/*
+ * call-seq:
+ * field.content_type => "content_type"
+ *
+ * Get the POST field Content-type for this PostField.
+ */
+static VALUE ruby_curl_postfield_content_type_get(VALUE self) {
+ CURB_OBJECT_GETTER(ruby_curl_postfield, content_type);
+}
+
+/*
+ * call-seq:
+ * field.local_file = "filename" => "filename"
+ *
+ * Set the POST field local filename for this PostField (when performing
+ * a file upload). Ignored when a content_proc is supplied via either
+ * +Curl::PostField.file+ or +set_content_proc+.
+ */
+static VALUE ruby_curl_postfield_local_file_set(VALUE self, VALUE local_file) {
+ CURB_OBJECT_SETTER(ruby_curl_postfield, local_file);
+}
+
+/*
+ * call-seq:
+ * field.local_file => "filename"
+ *
+ * Get the POST field local filename for this PostField (when performing
+ * a file upload).
+ */
+static VALUE ruby_curl_postfield_local_file_get(VALUE self) {
+ CURB_OBJECT_GETTER(ruby_curl_postfield, local_file);
+}
+
+/*
+ * call-seq:
+ * field.remote_file = "filename" => "filename"
+ *
+ * Set the POST field remote filename for this PostField (when performing
+ * a file upload). If no remote filename is provided, and no content_proc
+ * is supplied, the local filename is used. If no remote filename is
+ * specified when a content_proc is used, an exception will be raised
+ * during the perform.
+ */
+static VALUE ruby_curl_postfield_remote_file_set(VALUE self, VALUE remote_file) {
+ CURB_OBJECT_SETTER(ruby_curl_postfield, remote_file);
+}
+
+/*
+ * call-seq:
+ * field.local_file => "filename"
+ *
+ * Get the POST field remote filename for this PostField (when performing
+ * a file upload).
+ */
+static VALUE ruby_curl_postfield_remote_file_get(VALUE self) {
+ CURB_OBJECT_GETTER(ruby_curl_postfield, remote_file);
+}
+
+/*
+ * call-seq:
+ * field.set_content_proc { |field| ... } => <old proc>
+ *
+ * Set a content proc for this field. This proc will be called during the
+ * perform to supply the content for this field, overriding any setting
+ * of +content+ or +local_file+.
+ */
+static VALUE ruby_curl_postfield_content_proc_set(int argc, VALUE *argv, VALUE self) {
+ CURB_HANDLER_PROC_SETTER(ruby_curl_postfield, content_proc);
+}
+
+/*
+ * call-seq:
+ * field.to_str => "name=value"
+ * field.to_s => "name=value"
+ *
+ * Obtain a String representation of this PostField in url-encoded
+ * format. This is used to construct the post data for non-multipart
+ * POSTs.
+ *
+ * Only content fields may be converted to strings.
+ */
+static VALUE ruby_curl_postfield_to_str(VALUE self) {
+ // FIXME This is using the deprecated curl_escape func
+ ruby_curl_postfield *rbcpf;
+ VALUE result = Qnil;
+ VALUE name = Qnil;
+ char *tmpchrs;
+
+ Data_Get_Struct(self, ruby_curl_postfield, rbcpf);
+
+ if (rbcpf->name != Qnil) {
+ name = rbcpf->name;
+ if (rb_type(name) == T_STRING) {
+ name = rbcpf->name;
+ } else if (rb_respond_to(name,rb_intern("to_s"))) {
+ name = rb_funcall(name, rb_intern("to_s"), 0);
+ }
+ else {
+ name = Qnil; // we can't handle this object
+ }
+ }
+ if (name == Qnil) {
+ rb_raise(eCurlErrInvalidPostField, "Cannot convert unnamed field to string %s:%d, make sure your field name responds_to :to_s", __FILE__, __LINE__);
+ }
+
+ tmpchrs = curl_escape(StringValuePtr(name), (int)RSTRING_LEN(name));
+
+ if (!tmpchrs) {
+ rb_raise(eCurlErrInvalidPostField, "Failed to url-encode name `%s'", tmpchrs);
+ } else {
+ VALUE tmpcontent = Qnil;
+ VALUE escd_name = rb_str_new2(tmpchrs);
+ curl_free(tmpchrs);
+
+ if (rbcpf->content_proc != Qnil) {
+ tmpcontent = rb_funcall(rbcpf->content_proc, idCall, 1, self);
+ } else if (rbcpf->content != Qnil) {
+ tmpcontent = rbcpf->content;
+ } else if (rbcpf->local_file != Qnil) {
+ tmpcontent = rbcpf->local_file;
+ } else if (rbcpf->remote_file != Qnil) {
+ tmpcontent = rbcpf->remote_file;
+ } else {
+ tmpcontent = rb_str_new2("");
+ }
+ if (TYPE(tmpcontent) != T_STRING) {
+ if (rb_respond_to(tmpcontent, rb_intern("to_s"))) {
+ tmpcontent = rb_funcall(tmpcontent, rb_intern("to_s"), 0);
+ }
+ else {
+ rb_raise(rb_eRuntimeError, "postfield(%s) is not a string and does not respond_to to_s", RSTRING_PTR(escd_name) );
+ }
+ }
+ //fprintf(stderr, "encoding content: %ld - %s\n", RSTRING_LEN(tmpcontent), RSTRING_PTR(tmpcontent) );
+ tmpchrs = curl_escape(RSTRING_PTR(tmpcontent), (int)RSTRING_LEN(tmpcontent));
+ if (!tmpchrs) {
+ rb_raise(eCurlErrInvalidPostField, "Failed to url-encode content `%s'", tmpchrs);
+ } else {
+ VALUE escd_content = rb_str_new2(tmpchrs);
+ curl_free(tmpchrs);
+
+ result = escd_name;
+ rb_str_cat(result, "=", 1);
+ rb_str_concat(result, escd_content);
+ }
+ }
+
+ return result;
+}
+
+
+/* =================== INIT LIB =====================*/
+void init_curb_postfield() {
+ VALUE sc;
+
+ idCall = rb_intern("call");
+
+ cCurlPostField = rb_define_class_under(mCurl, "PostField", rb_cObject);
+
+ /* Class methods */
+ rb_define_singleton_method(cCurlPostField, "content", ruby_curl_postfield_new_content, -1);
+ rb_define_singleton_method(cCurlPostField, "file", ruby_curl_postfield_new_file, -1);
+
+ sc = rb_singleton_class(cCurlPostField);
+ rb_undef(sc, rb_intern("new"));
+
+ rb_define_method(cCurlPostField, "name=", ruby_curl_postfield_name_set, 1);
+ rb_define_method(cCurlPostField, "name", ruby_curl_postfield_name_get, 0);
+ rb_define_method(cCurlPostField, "content=", ruby_curl_postfield_content_set, 1);
+ rb_define_method(cCurlPostField, "content", ruby_curl_postfield_content_get, 0);
+ rb_define_method(cCurlPostField, "content_type=", ruby_curl_postfield_content_type_set, 1);
+ rb_define_method(cCurlPostField, "content_type", ruby_curl_postfield_content_type_get, 0);
+ rb_define_method(cCurlPostField, "local_file=", ruby_curl_postfield_local_file_set, 1);
+ rb_define_method(cCurlPostField, "local_file", ruby_curl_postfield_local_file_get, 0);
+ rb_define_method(cCurlPostField, "remote_file=", ruby_curl_postfield_remote_file_set, 1);
+ rb_define_method(cCurlPostField, "remote_file", ruby_curl_postfield_remote_file_get, 0);
+
+ rb_define_method(cCurlPostField, "set_content_proc", ruby_curl_postfield_content_proc_set, -1);
+
+ rb_define_method(cCurlPostField, "to_str", ruby_curl_postfield_to_str, 0);
+ rb_define_alias(cCurlPostField, "to_s", "to_str");
+}