pony-express 0.7.0 → 0.8.0
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/README.rdoc +2 -1
- data/VERSION +1 -1
- data/ext/mimetic.cxx +79 -20
- data/lib/pony-express.rb +56 -14
- metadata +3 -6
data/README.rdoc
CHANGED
@@ -21,8 +21,9 @@ historical perspective read http://en.wikipedia.org/wiki/Pony_Express.
|
|
21
21
|
|
22
22
|
require "pony-express"
|
23
23
|
mail = PonyExpress::Mail.new to: "burns@plant.local", from: "homer@home.local", via: "sendmail"
|
24
|
+
mail.add cc: "smithers@plant.local", bcc: "carl@plant.local", replyto: "homer+work@home.local"
|
24
25
|
mail.add subject: "Hello Mr.Burns", text: "Can I have more donuts ?", html: "<strong>More Dooooonuuuuts!</strong>"
|
25
|
-
mail.add attachments: [ "/home/homer/donuts.png" ]
|
26
|
+
mail.add attachments: [ "/home/homer/donuts.png" ], headers: [{name: "X-FooBar", value: "test"}]
|
26
27
|
mail.dispatch
|
27
28
|
|
28
29
|
Don't want to create new mail objects ? Just pass in all options to PonyExpress.mail
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.8.0
|
data/ext/mimetic.cxx
CHANGED
@@ -18,6 +18,8 @@ extern "C" {
|
|
18
18
|
#define FORCE_ENCODING(str,enc) rb_enc_associate(str, rb_to_encoding(enc)); ENC_CODERANGE_CLEAR(str);
|
19
19
|
#define TO_S(v) rb_funcall(v, rb_intern("to_s"), 0)
|
20
20
|
#define CSTRING(v) RSTRING_PTR(TYPE(v) != T_STRING ? TO_S(v) : v)
|
21
|
+
#define RBSTRING(v) rb_str_new2(v.c_str())
|
22
|
+
#define SYM(v) ID2SYM(rb_intern(v))
|
21
23
|
|
22
24
|
static VALUE rb_mMimetic;
|
23
25
|
static VALUE rb_UTF8, rb_ASCII;
|
@@ -74,7 +76,7 @@ bool mimetic_attach_file(MimeEntity *m, char* filename) {
|
|
74
76
|
return true;
|
75
77
|
}
|
76
78
|
else {
|
77
|
-
rb_raise(rb_eRuntimeError, "
|
79
|
+
rb_raise(rb_eRuntimeError, "Unable to read attachment file %s", filename);
|
78
80
|
}
|
79
81
|
return false;
|
80
82
|
}
|
@@ -124,17 +126,17 @@ void rb_load_mime_types(VALUE self, VALUE filename) {
|
|
124
126
|
file.close();
|
125
127
|
}
|
126
128
|
else {
|
127
|
-
rb_raise(rb_eRuntimeError, "
|
129
|
+
rb_raise(rb_eRuntimeError, "Unable to load mime.types");
|
128
130
|
}
|
129
131
|
}
|
130
132
|
|
131
133
|
VALUE rb_mimetic_build(VALUE self, VALUE options) {
|
132
134
|
ostringstream output;
|
133
135
|
VALUE attachment;
|
134
|
-
VALUE text = rb_hash_aref(options,
|
135
|
-
VALUE to = rb_hash_aref(options,
|
136
|
-
VALUE from = rb_hash_aref(options,
|
137
|
-
VALUE subject = rb_hash_aref(options,
|
136
|
+
VALUE text = rb_hash_aref(options, SYM("text"));
|
137
|
+
VALUE to = rb_hash_aref(options, SYM("to"));
|
138
|
+
VALUE from = rb_hash_aref(options, SYM("from"));
|
139
|
+
VALUE subject = rb_hash_aref(options, SYM("subject"));
|
138
140
|
|
139
141
|
if (text == Qnil) rb_raise(rb_eArgError, "Mimetic.build called without :text");
|
140
142
|
if (from == Qnil) rb_raise(rb_eArgError, "Mimetic.build called without :from");
|
@@ -142,15 +144,15 @@ VALUE rb_mimetic_build(VALUE self, VALUE options) {
|
|
142
144
|
if (subject == Qnil) rb_raise(rb_eArgError, "Mimetic.build called without :subject");
|
143
145
|
|
144
146
|
// optional fields
|
145
|
-
VALUE mid = rb_hash_aref(options,
|
146
|
-
VALUE html = rb_hash_aref(options,
|
147
|
-
VALUE tcid = rb_hash_aref(options,
|
148
|
-
VALUE hcid = rb_hash_aref(options,
|
149
|
-
VALUE replyto = rb_hash_aref(options,
|
150
|
-
VALUE cc = rb_hash_aref(options,
|
151
|
-
VALUE bcc = rb_hash_aref(options,
|
152
|
-
VALUE files = rb_hash_aref(options,
|
153
|
-
VALUE headers = rb_hash_aref(options,
|
147
|
+
VALUE mid = rb_hash_aref(options, SYM("message_id"));
|
148
|
+
VALUE html = rb_hash_aref(options, SYM("html"));
|
149
|
+
VALUE tcid = rb_hash_aref(options, SYM("text_content_id"));
|
150
|
+
VALUE hcid = rb_hash_aref(options, SYM("html_content_id"));
|
151
|
+
VALUE replyto = rb_hash_aref(options, SYM("replyto"));
|
152
|
+
VALUE cc = rb_hash_aref(options, SYM("cc"));
|
153
|
+
VALUE bcc = rb_hash_aref(options, SYM("bcc"));
|
154
|
+
VALUE files = rb_hash_aref(options, SYM("attachments"));
|
155
|
+
VALUE headers = rb_hash_aref(options, SYM("headers"));
|
154
156
|
|
155
157
|
if (mid != Qnil && TYPE(mid) != T_STRING)
|
156
158
|
rb_raise(rb_eArgError, "Mimetic.build expects :message_id to be a string");
|
@@ -168,9 +170,9 @@ VALUE rb_mimetic_build(VALUE self, VALUE options) {
|
|
168
170
|
rb_raise(rb_eArgError, "Mimetic.build expects :headers to be an array");
|
169
171
|
|
170
172
|
VALUE errors = Qnil;
|
171
|
-
MimeEntity *message =
|
172
|
-
MimeEntity *html_part =
|
173
|
-
MimeEntity *text_part =
|
173
|
+
MimeEntity *message = 0;
|
174
|
+
MimeEntity *html_part = 0;
|
175
|
+
MimeEntity *text_part = 0;
|
174
176
|
MimeVersion v1("1.0");
|
175
177
|
|
176
178
|
try {
|
@@ -178,7 +180,7 @@ VALUE rb_mimetic_build(VALUE self, VALUE options) {
|
|
178
180
|
|
179
181
|
if (html != Qnil && text != Qnil) {
|
180
182
|
delete message;
|
181
|
-
message
|
183
|
+
message = new MultipartAlternative;
|
182
184
|
html_part = new MimeEntity;
|
183
185
|
text_part = new MimeEntity;
|
184
186
|
message->body().parts().push_back(text_part);
|
@@ -253,13 +255,70 @@ VALUE rb_mimetic_build(VALUE self, VALUE options) {
|
|
253
255
|
errors = rb_str_new2("Unknown Error");
|
254
256
|
}
|
255
257
|
|
256
|
-
rb_raise(rb_eRuntimeError, "
|
258
|
+
rb_raise(rb_eRuntimeError, "%s\n", CSTRING(errors));
|
259
|
+
}
|
260
|
+
|
261
|
+
VALUE parse_mime_parts(MimeEntity &me) {
|
262
|
+
VALUE contents = rb_ary_new();
|
263
|
+
MimeEntityList::const_iterator curr = me.body().parts().begin(), end = me.body().parts().end();
|
264
|
+
while (curr != end) {
|
265
|
+
VALUE content = rb_hash_new();
|
266
|
+
MimeEntity *part = *curr;
|
267
|
+
|
268
|
+
rb_hash_aset(content, SYM("type"), RBSTRING(part->header().contentType().str()));
|
269
|
+
if (part->header().contentType().isMultipart()) {
|
270
|
+
rb_hash_aset(content, SYM("content"), parse_mime_parts(*part));
|
271
|
+
}
|
272
|
+
else {
|
273
|
+
rb_hash_aset(content, SYM("content"), RBSTRING(part->body()));
|
274
|
+
rb_hash_aset(content, SYM("encoding"), RBSTRING(part->header().contentTransferEncoding().str()));
|
275
|
+
rb_hash_aset(content, SYM("disposition"), RBSTRING(part->header().contentDisposition().str()));
|
276
|
+
}
|
277
|
+
rb_ary_push(contents, content);
|
278
|
+
curr++;
|
279
|
+
}
|
280
|
+
return contents;
|
281
|
+
}
|
282
|
+
|
283
|
+
VALUE rb_mimetic_parse(VALUE self, VALUE data) {
|
284
|
+
if (NIL_P(data))
|
285
|
+
return Qnil;
|
286
|
+
|
287
|
+
VALUE errors = Qnil;
|
288
|
+
|
289
|
+
try {
|
290
|
+
stringstream mime;
|
291
|
+
mime << CSTRING(data);
|
292
|
+
MimeEntity me;
|
293
|
+
me.load(mime);
|
294
|
+
|
295
|
+
VALUE message = rb_hash_new();
|
296
|
+
|
297
|
+
rb_hash_aset(message, SYM("from"), RBSTRING(me.header().from().str()));
|
298
|
+
rb_hash_aset(message, SYM("to"), RBSTRING(me.header().to().str()));
|
299
|
+
rb_hash_aset(message, SYM("subject"), RBSTRING(me.header().subject()));
|
300
|
+
rb_hash_aset(message, SYM("cc"), RBSTRING(me.header().cc().str()));
|
301
|
+
rb_hash_aset(message, SYM("bcc"), RBSTRING(me.header().bcc().str()));
|
302
|
+
rb_hash_aset(message, SYM("replyto"), RBSTRING(me.header().replyto().str()));
|
303
|
+
|
304
|
+
rb_hash_aset(message, SYM("contents"), parse_mime_parts(me));
|
305
|
+
return message;
|
306
|
+
}
|
307
|
+
catch(exception &e) {
|
308
|
+
errors = rb_str_new2(e.what());
|
309
|
+
}
|
310
|
+
catch (...) {
|
311
|
+
errors = rb_str_new2("Unknown Error");
|
312
|
+
}
|
313
|
+
|
314
|
+
rb_raise(rb_eRuntimeError, "%s\n", CSTRING(errors));
|
257
315
|
}
|
258
316
|
|
259
317
|
extern "C" {
|
260
318
|
void Init_mimetic(void) {
|
261
319
|
rb_mMimetic = rb_define_module("Mimetic");
|
262
320
|
rb_define_module_function(rb_mMimetic, "build", VALUEFUNC(rb_mimetic_build), 1);
|
321
|
+
rb_define_module_function(rb_mMimetic, "parse", VALUEFUNC(rb_mimetic_parse), 1);
|
263
322
|
rb_define_module_function(rb_mMimetic, "load_mime_types", VALUEFUNC(rb_load_mime_types), 1);
|
264
323
|
rb_UTF8 = rb_str_new2("UTF-8");
|
265
324
|
rb_ASCII = rb_str_new2("US-ASCII");
|
data/lib/pony-express.rb
CHANGED
@@ -3,8 +3,9 @@ begin; require 'smtp_tls'; rescue LoadError; end
|
|
3
3
|
require_relative '../ext/mimetic'
|
4
4
|
|
5
5
|
module PonyExpress
|
6
|
-
TRANSPORTS
|
7
|
-
DEFAULT_SMTP_OPTIONS = { :
|
6
|
+
TRANSPORTS = [ :smtp, :sendmail ]
|
7
|
+
DEFAULT_SMTP_OPTIONS = { host: 'localhost', port: '25', domain: 'localhost.localdomain' }
|
8
|
+
@@sendmail_binary = '/usr/sbin/sendmail'
|
8
9
|
|
9
10
|
Mimetic.load_mime_types File.dirname(__FILE__) + "/../mime.types"
|
10
11
|
|
@@ -13,8 +14,34 @@ module PonyExpress
|
|
13
14
|
Mimetic.build(options)
|
14
15
|
end
|
15
16
|
|
17
|
+
# NOTE: parse is very much WIP, YMMV
|
18
|
+
def parse content
|
19
|
+
Mimetic.parse(content)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Build and dispatch email in one go.
|
23
|
+
#
|
24
|
+
# @example mail.
|
25
|
+
# require "pony-express"
|
26
|
+
# PonyExpress.mail to: "burns@plant.local", from: "homer@home.local", via: "sendmail",
|
27
|
+
# subject: "Hello Mr.Burns", text: "Can I have more donuts ?",
|
28
|
+
# html: "<strong>More Dooooonuuuuts!</strong>",
|
29
|
+
# attachments: [ "/home/homer/donuts.png" ],
|
30
|
+
# headers: [{name: "X-FooBar", value: "test"}]
|
31
|
+
#
|
32
|
+
# @param [String] to Email address.
|
33
|
+
# @param [String] from Email address.
|
34
|
+
# @param [String] subject Subject (will be converted to quoted printable if needed).
|
35
|
+
# @param [String] text Plain text content.
|
36
|
+
# @param [String] cc Email address (optional).
|
37
|
+
# @param [String] bcc Email address (optional).
|
38
|
+
# @param [String] replyto Email address (optional).
|
39
|
+
# @param [String] html HTML content (optional).
|
40
|
+
# @param [Array] attachments List of email attachments (optional).
|
41
|
+
# @param [Array] headers List of email headers (optional).
|
42
|
+
# @return [TrueClass or FalseClass]
|
16
43
|
def mail options
|
17
|
-
via
|
44
|
+
via = options.delete(:via) || :smtp
|
18
45
|
via_options = options.delete(:via_options) || {}
|
19
46
|
|
20
47
|
if TRANSPORTS.include? via
|
@@ -27,22 +54,19 @@ module PonyExpress
|
|
27
54
|
end
|
28
55
|
end
|
29
56
|
|
57
|
+
def sendmail_binary= binary
|
58
|
+
@@sendmail_binary = binary
|
59
|
+
end
|
60
|
+
|
30
61
|
def sendmail_binary
|
31
|
-
|
32
|
-
sendmail.empty? ? '/usr/sbin/sendmail' : sendmail
|
62
|
+
@@sendmail_binary
|
33
63
|
end
|
34
64
|
|
35
|
-
def transport_via_sendmail content, options={}
|
36
|
-
IO.popen('-', 'w
|
37
|
-
if pipe
|
38
|
-
pipe.write(content)
|
39
|
-
else
|
40
|
-
exec(sendmail_binary, "-t")
|
41
|
-
end
|
42
|
-
end
|
65
|
+
def transport_via_sendmail content, options = {}
|
66
|
+
IO.popen([sendmail_binary, '-t'], 'w') {|io| io.write(content)}
|
43
67
|
end
|
44
68
|
|
45
|
-
def transport_via_smtp content, from, to, options={}
|
69
|
+
def transport_via_smtp content, from, to, options = {}
|
46
70
|
o = DEFAULT_SMTP_OPTIONS.merge(options)
|
47
71
|
smtp = Net::SMTP.new(o[:host], o[:port])
|
48
72
|
if o[:tls]
|
@@ -66,19 +90,37 @@ module PonyExpress
|
|
66
90
|
@options = opt
|
67
91
|
end
|
68
92
|
|
93
|
+
|
94
|
+
# Add an option.
|
95
|
+
#
|
96
|
+
# @example add.
|
97
|
+
# require "pony-express"
|
98
|
+
# mail = PonyExpress::Mail.new
|
99
|
+
# mail.add to: "burns@plant.local"
|
69
100
|
def add opt
|
70
101
|
@options.merge! opt
|
71
102
|
end
|
72
103
|
|
104
|
+
# Remove an option.
|
105
|
+
#
|
106
|
+
# @example remove.
|
107
|
+
# require "pony-express"
|
108
|
+
# mail = PonyExpress::Mail.new
|
109
|
+
# mail.add to: "burns@plant.local", cc: "smithers@plant.local"
|
110
|
+
# mail.remove :cc
|
73
111
|
def remove opt
|
74
112
|
keys = opt.keys
|
75
113
|
@options.reject! {|k, v| keys.include?(k) }
|
76
114
|
end
|
77
115
|
|
116
|
+
# Send the email via the selected transport.
|
117
|
+
#
|
78
118
|
def dispatch
|
79
119
|
mail(@options)
|
80
120
|
end
|
81
121
|
|
122
|
+
# Return the encoded email content.
|
123
|
+
#
|
82
124
|
def content
|
83
125
|
build(@options)
|
84
126
|
end
|
metadata
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pony-express
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash: 3
|
5
4
|
prerelease: false
|
6
5
|
segments:
|
7
6
|
- 0
|
8
|
-
-
|
7
|
+
- 8
|
9
8
|
- 0
|
10
|
-
version: 0.
|
9
|
+
version: 0.8.0
|
11
10
|
platform: ruby
|
12
11
|
authors:
|
13
12
|
- Bharanee Rathna
|
@@ -15,7 +14,7 @@ autorequire:
|
|
15
14
|
bindir: bin
|
16
15
|
cert_chain: []
|
17
16
|
|
18
|
-
date:
|
17
|
+
date: 2011-04-07 00:00:00 +10:00
|
19
18
|
default_executable:
|
20
19
|
dependencies: []
|
21
20
|
|
@@ -51,7 +50,6 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
51
50
|
requirements:
|
52
51
|
- - ">="
|
53
52
|
- !ruby/object:Gem::Version
|
54
|
-
hash: 3
|
55
53
|
segments:
|
56
54
|
- 0
|
57
55
|
version: "0"
|
@@ -60,7 +58,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
60
58
|
requirements:
|
61
59
|
- - ">="
|
62
60
|
- !ruby/object:Gem::Version
|
63
|
-
hash: 3
|
64
61
|
segments:
|
65
62
|
- 0
|
66
63
|
version: "0"
|