pony-express 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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"
|