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.
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