pony-express 0.2.5 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +11 -4
- data/VERSION +1 -1
- data/ext/extconf.rb +17 -5
- data/ext/mimetic.cxx +100 -10
- data/lib/pony-express.rb +7 -3
- metadata +1 -1
data/README
CHANGED
@@ -2,7 +2,7 @@ A fast and lightweight ruby mailer based on http://github.com/benprew/pony. For
|
|
2
2
|
historical perspective read http://en.wikipedia.org/wiki/Pony_Express.
|
3
3
|
|
4
4
|
INSTALL:
|
5
|
-
|
5
|
+
|
6
6
|
sudo apt-get install libmimetic-dev
|
7
7
|
sudo gem install pony-express
|
8
8
|
|
@@ -10,8 +10,9 @@ REQUIREMENT:
|
|
10
10
|
|
11
11
|
* Ruby >= 1.9.1
|
12
12
|
* rubygems >= 1.3.5
|
13
|
-
* ruby development libraries
|
14
|
-
* mimetic >= 0.9.6 development libraries
|
13
|
+
* ruby development libraries (debian: ruby1.9.1-dev)
|
14
|
+
* mimetic >= 0.9.6 development libraries (debian: libmimetic-dev)
|
15
|
+
* pcre++ development libraries (debian: libpcre++-dev)
|
15
16
|
|
16
17
|
USAGE:
|
17
18
|
|
@@ -19,12 +20,18 @@ USAGE:
|
|
19
20
|
>> args = { from: "jim@example.com", to: "ralph@example.com", subject: "test email", via: "sendmail" }
|
20
21
|
>> args[:text] = "Hello!"
|
21
22
|
>> args[:html] = "<strong>Hello!</strong>"
|
23
|
+
>> args[:attachments] = [ "/home/jim/party.png" ]
|
22
24
|
>> PonyExpress.mail(args)
|
23
25
|
|
24
26
|
|
25
27
|
TODO:
|
26
28
|
|
27
|
-
*
|
29
|
+
* Check for Memory Leaks, build a Rainbow machine and rope in some Unicorns.
|
30
|
+
|
31
|
+
DON'T ASK:
|
32
|
+
|
33
|
+
* Support for Ruby 1.8 or Rubinius or JRuby
|
34
|
+
* MIME parsing. If you need a full blown mail library have a look at the mail gem.
|
28
35
|
|
29
36
|
|
30
37
|
LICENSE:
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.5.0
|
data/ext/extconf.rb
CHANGED
@@ -7,15 +7,27 @@ $CFLAGS = "-DHAVE_INTTYPES_H"
|
|
7
7
|
Config::CONFIG['CC'] = 'g++'
|
8
8
|
Config::CONFIG['CPP'] = 'g++'
|
9
9
|
|
10
|
-
|
10
|
+
headers_mimetic = [ 'stdio.h', 'mimetic/mimetic.h' ]
|
11
|
+
headers_pcrecpp = [ 'stdio.h', 'pcre++.h' ]
|
11
12
|
|
12
|
-
|
13
|
-
if have_library('mimetic', nil, headers)
|
14
|
-
create_makefile 'mimetic'
|
15
|
-
else
|
13
|
+
if !have_library('mimetic', nil, headers_mimetic)
|
16
14
|
puts <<-ERROR
|
15
|
+
|
17
16
|
Cannot find mimetic headers or libraries.
|
18
17
|
Try sudo apt-get install libmimetic-dev on debian flavors of linux.
|
18
|
+
|
19
|
+
ERROR
|
20
|
+
exit 1
|
21
|
+
end
|
22
|
+
|
23
|
+
if !have_library('pcre++', nil, headers_pcrecpp)
|
24
|
+
puts <<-ERROR
|
25
|
+
|
26
|
+
Cannot find pcre++ headers or libraries.
|
27
|
+
Try sudo apt-get install libpcre++-dev on debian flavors of linux.
|
28
|
+
|
19
29
|
ERROR
|
20
30
|
exit 1
|
21
31
|
end
|
32
|
+
|
33
|
+
create_makefile('mimetic')
|
data/ext/mimetic.cxx
CHANGED
@@ -4,6 +4,8 @@
|
|
4
4
|
#include <unistd.h>
|
5
5
|
#include <sstream>
|
6
6
|
#include <exception>
|
7
|
+
#include <algorithm>
|
8
|
+
#include <pcre++.h>
|
7
9
|
#include <mimetic/mimetic.h>
|
8
10
|
|
9
11
|
#define ID_CONST_GET rb_intern("const_get")
|
@@ -15,15 +17,75 @@ static VALUE eArgumentError;
|
|
15
17
|
|
16
18
|
using namespace std;
|
17
19
|
using namespace mimetic;
|
20
|
+
using namespace pcrepp;
|
18
21
|
|
19
22
|
#define VALUEFUNC(f) ((VALUE (*)(ANYARGS)) f)
|
20
23
|
|
24
|
+
map<string, string> MimeTypes;
|
25
|
+
|
26
|
+
/* RFC References
|
27
|
+
|
28
|
+
http://www.ietf.org/rfc/rfc2822.txt Internet Message Format.
|
29
|
+
http://www.ietf.org/rfc/rfc1341.txt MIME Extensions - Multipart related.
|
30
|
+
http://www.ietf.org/rfc/rfc2392.txt Content-ID and Message-ID.
|
31
|
+
|
32
|
+
*/
|
33
|
+
|
34
|
+
// TODO: CHECK FOR MEMORY LEAKS - not sure if mimetic releases mime parts.
|
35
|
+
|
36
|
+
bool mimetic_attach_file(MimeEntity *m, char* filename, const char *mimetype) {
|
37
|
+
filebuf ifile;
|
38
|
+
ostringstream encoded;
|
39
|
+
ifile.open(filename, ios::in);
|
40
|
+
if (ifile.is_open()) {
|
41
|
+
istream is(&ifile);
|
42
|
+
Attachment *at = new Attachment(filename, ContentType(mimetype));
|
43
|
+
Base64::Encoder b64;
|
44
|
+
ostreambuf_iterator<char> oi(encoded);
|
45
|
+
istreambuf_iterator<char> ibegin(is), iend;
|
46
|
+
encode(ibegin, iend, b64, oi);
|
47
|
+
at->body().assign(encoded.str());
|
48
|
+
m->body().parts().push_back(at);
|
49
|
+
ifile.close();
|
50
|
+
return true;
|
51
|
+
}
|
52
|
+
else {
|
53
|
+
rb_raise(eRuntimeError, "Mimetic: Unable to read attachment file %s", filename);
|
54
|
+
}
|
55
|
+
return false;
|
56
|
+
}
|
57
|
+
|
58
|
+
string get_mime_type(string file) {
|
59
|
+
Pcre regex("\\.");
|
60
|
+
string extn = regex.split(file).back();
|
61
|
+
transform(extn.begin(), extn.end(), extn.begin(), ::tolower);
|
62
|
+
string mime = MimeTypes[extn];
|
63
|
+
return mime.length() > 0 ? mime : MimeTypes["bin"];
|
64
|
+
}
|
65
|
+
|
66
|
+
void rb_load_mime_types(VALUE self, VALUE filename) {
|
67
|
+
char buffer[4096];
|
68
|
+
vector<string> data;
|
69
|
+
Pcre regex("[\\r\\n\\t]+");
|
70
|
+
ifstream file(RSTRING_PTR(filename), ios::in);
|
71
|
+
if (file.is_open()) {
|
72
|
+
while (!file.eof()) {
|
73
|
+
file.getline(buffer, 4096);
|
74
|
+
data = regex.split(buffer);
|
75
|
+
for (int i = 1; i < data.size(); i++)
|
76
|
+
MimeTypes[data[i]] = data[0];
|
77
|
+
}
|
78
|
+
file.close();
|
79
|
+
}
|
80
|
+
else {
|
81
|
+
rb_raise(eRuntimeError, "Mimetic: Unable to load mime.types");
|
82
|
+
}
|
83
|
+
}
|
84
|
+
|
21
85
|
VALUE rb_mimetic_build(VALUE self, VALUE options) {
|
22
86
|
ostringstream output;
|
23
|
-
|
24
|
-
|
87
|
+
VALUE attachment;
|
25
88
|
VALUE text = rb_hash_aref(options, ID2SYM(rb_intern("text")));
|
26
|
-
VALUE html = rb_hash_aref(options, ID2SYM(rb_intern("html")));
|
27
89
|
VALUE to = rb_hash_aref(options, ID2SYM(rb_intern("to")));
|
28
90
|
VALUE from = rb_hash_aref(options, ID2SYM(rb_intern("from")));
|
29
91
|
VALUE subject = rb_hash_aref(options, ID2SYM(rb_intern("subject")));
|
@@ -33,6 +95,18 @@ VALUE rb_mimetic_build(VALUE self, VALUE options) {
|
|
33
95
|
if (to == Qnil) rb_raise(eArgumentError, "Mimetic.build called without :to");
|
34
96
|
if (subject == Qnil) rb_raise(eArgumentError, "Mimetic.build called without :subject");
|
35
97
|
|
98
|
+
// optional fields
|
99
|
+
VALUE html = rb_hash_aref(options, ID2SYM(rb_intern("html")));
|
100
|
+
VALUE tcid = rb_hash_aref(options, ID2SYM(rb_intern("text_content_id")));
|
101
|
+
VALUE hcid = rb_hash_aref(options, ID2SYM(rb_intern("html_content_id")));
|
102
|
+
VALUE replyto = rb_hash_aref(options, ID2SYM(rb_intern("replyto")));
|
103
|
+
VALUE cc = rb_hash_aref(options, ID2SYM(rb_intern("cc")));
|
104
|
+
VALUE bcc = rb_hash_aref(options, ID2SYM(rb_intern("bcc")));
|
105
|
+
VALUE files = rb_hash_aref(options, ID2SYM(rb_intern("attachments")));
|
106
|
+
|
107
|
+
if (files != Qnil && TYPE(files) != T_ARRAY)
|
108
|
+
rb_raise(eArgumentError, "Mimetic.build expects :attachments to be an array");
|
109
|
+
|
36
110
|
VALUE errors = Qnil;
|
37
111
|
MimeEntity *message = NULL;
|
38
112
|
MimeEntity *html_part = NULL;
|
@@ -50,12 +124,12 @@ VALUE rb_mimetic_build(VALUE self, VALUE options) {
|
|
50
124
|
message->body().parts().push_back(html_part);
|
51
125
|
text_part->header().contentType("text/plain; charset=UTF-8");
|
52
126
|
text_part->header().contentTransferEncoding("8bit");
|
53
|
-
text_part->header().
|
127
|
+
text_part->header().contentId(tcid == Qnil ? ContentId() : ContentId(RSTRING_PTR(tcid)));
|
54
128
|
text_part->header().mimeVersion(v1);
|
55
129
|
text_part->body().assign(RSTRING_PTR(text));
|
56
130
|
html_part->header().contentType("text/html; charset=UTF-8");
|
57
131
|
html_part->header().contentTransferEncoding("7bit");
|
58
|
-
html_part->header().
|
132
|
+
html_part->header().contentId(hcid == Qnil ? ContentId() : ContentId(RSTRING_PTR(hcid)));
|
59
133
|
html_part->header().mimeVersion(v1);
|
60
134
|
html_part->body().assign(RSTRING_PTR(html));
|
61
135
|
}
|
@@ -63,14 +137,30 @@ VALUE rb_mimetic_build(VALUE self, VALUE options) {
|
|
63
137
|
message->body().assign(RSTRING_PTR(text));
|
64
138
|
message->header().contentType("text/plain; charset=UTF-8");
|
65
139
|
message->header().contentTransferEncoding("8bit");
|
66
|
-
message->header().messageid(message_id++);
|
67
140
|
message->header().mimeVersion(v1);
|
68
141
|
}
|
69
|
-
|
142
|
+
|
143
|
+
if (files != Qnil && RARRAY_LEN(files) > 0) {
|
144
|
+
MimeEntity *m = message;
|
145
|
+
message = new MultipartMixed();
|
146
|
+
message->header().mimeVersion(v1);
|
147
|
+
message->body().parts().push_back(m);
|
148
|
+
for (long i = 0; i < RARRAY_LEN(files); i++) {
|
149
|
+
attachment = rb_ary_entry(files, i);
|
150
|
+
if (attachment != Qnil && TYPE(attachment) == T_STRING)
|
151
|
+
mimetic_attach_file(message, RSTRING_PTR(attachment), get_mime_type(RSTRING_PTR(attachment)).c_str());
|
152
|
+
}
|
153
|
+
}
|
154
|
+
|
70
155
|
message->header().from(RSTRING_PTR(from));
|
71
156
|
message->header().to(RSTRING_PTR(to));
|
72
157
|
message->header().subject(RSTRING_PTR(subject));
|
73
|
-
|
158
|
+
message->header().messageid(1);
|
159
|
+
|
160
|
+
if (replyto != Qnil) message->header().replyto(RSTRING_PTR(replyto));
|
161
|
+
if (cc != Qnil) message->header().cc(RSTRING_PTR(cc));
|
162
|
+
if (bcc != Qnil) message->header().bcc(RSTRING_PTR(bcc));
|
163
|
+
|
74
164
|
output << *message << endl;
|
75
165
|
delete message;
|
76
166
|
return rb_str_new2(output.str().c_str());
|
@@ -86,13 +176,13 @@ VALUE rb_mimetic_build(VALUE self, VALUE options) {
|
|
86
176
|
|
87
177
|
rb_raise(eRuntimeError, "Mimetic boo boo : %s\n", RSTRING_PTR(errors));
|
88
178
|
}
|
89
|
-
|
90
|
-
|
179
|
+
|
91
180
|
extern "C" {
|
92
181
|
void Init_mimetic(void) {
|
93
182
|
eRuntimeError = CONST_GET(rb_mKernel, "RuntimeError");
|
94
183
|
eArgumentError = CONST_GET(rb_mKernel, "ArgumentError");
|
95
184
|
rb_mMimetic = rb_define_module("Mimetic");
|
96
185
|
rb_define_module_function(rb_mMimetic, "build", VALUEFUNC(rb_mimetic_build), 1);
|
186
|
+
rb_define_module_function(rb_mMimetic, "load_mime_types", VALUEFUNC(rb_load_mime_types), 1);
|
97
187
|
}
|
98
188
|
}
|
data/lib/pony-express.rb
CHANGED
@@ -6,17 +6,18 @@ module PonyExpress
|
|
6
6
|
TRANSPORTS = [ :smtp, :sendmail ]
|
7
7
|
DEFAULT_SMTP_OPTIONS = { :host => 'localhost', :port => '25', :domain => 'localhost.localdomain' }
|
8
8
|
|
9
|
+
Mimetic.load_mime_types File.dirname(__FILE__) + "/../mime.types"
|
10
|
+
|
9
11
|
def self.build options
|
10
12
|
# TODO validation.
|
11
13
|
Mimetic.build(options)
|
12
14
|
end
|
13
15
|
|
14
16
|
def self.mail options
|
15
|
-
via = options.delete(:via) ||
|
17
|
+
via = (options.delete(:via) || :smtp).to_sym
|
16
18
|
via_options = options.delete(:via_options) || {}
|
17
19
|
|
18
|
-
|
19
|
-
if TRANSPORTS.include? via.to_sym
|
20
|
+
if TRANSPORTS.include? via
|
20
21
|
case via
|
21
22
|
when :sendmail then transport_via_sendmail build(options), via_options
|
22
23
|
when :smtp then transport_via_smtp build(options), options[:from], options[:to], via_options
|
@@ -26,6 +27,8 @@ module PonyExpress
|
|
26
27
|
end
|
27
28
|
end
|
28
29
|
|
30
|
+
private
|
31
|
+
|
29
32
|
def self.sendmail_binary
|
30
33
|
sendmail = `which sendmail`.chomp
|
31
34
|
sendmail.empty? ? '/usr/sbin/sendmail' : sendmail
|
@@ -56,4 +59,5 @@ module PonyExpress
|
|
56
59
|
smtp.send_message content, from, to
|
57
60
|
smtp.finish
|
58
61
|
end
|
62
|
+
|
59
63
|
end
|