pony-express 0.2.5 → 0.5.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.
Files changed (6) hide show
  1. data/README +11 -4
  2. data/VERSION +1 -1
  3. data/ext/extconf.rb +17 -5
  4. data/ext/mimetic.cxx +100 -10
  5. data/lib/pony-express.rb +7 -3
  6. 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 (debian: ruby1.9.1-dev)
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
- * Attachments, Rainbows and Unicorns.
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.2.5
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
- dir_config("mimetic", ["/usr/local", "/opt/local", "/usr"])
10
+ headers_mimetic = [ 'stdio.h', 'mimetic/mimetic.h' ]
11
+ headers_pcrecpp = [ 'stdio.h', 'pcre++.h' ]
11
12
 
12
- headers = [ 'stdio.h', 'mimetic/mimetic.h' ]
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
- int message_id = 1;
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().messageid(message_id++);
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().messageid(message_id++);
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) || 'sendmail'
17
+ via = (options.delete(:via) || :smtp).to_sym
16
18
  via_options = options.delete(:via_options) || {}
17
19
 
18
- via = options.delete(:via) || :smtp
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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pony-express
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bharanee Rathna