postini 0.0.6 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +4 -0
- data/Manifest.txt +12 -26
- data/README.txt +11 -4
- data/Rakefile +27 -4
- data/features/development.feature +13 -0
- data/features/step_definitions/common_steps.rb +172 -0
- data/features/support/common.rb +29 -0
- data/features/support/env.rb +6 -0
- data/features/support/matchers.rb +11 -0
- data/lib/postini/automated_batch_service.rb +464 -0
- data/lib/postini/configuration_check.rb +25 -0
- data/lib/postini/endpoint_resolver_service.rb +41 -0
- data/lib/postini/endpoints.rb +26 -0
- data/lib/postini/exceptions.rb +85 -0
- data/lib/postini.rb +74 -102
- data/script/console +1 -1
- data/script/txt2html +5 -16
- data/spec/exceptions_spec.rb +19 -0
- data/spec/postini_spec.rb +5 -38
- data/spec/rcov.opts +1 -0
- data/spec/spec_helper.rb +1 -1
- data/tasks/rspec.rake +1 -19
- data/vendor/automatedbatch.wsdl +1 -1
- metadata +21 -54
- data/config/hoe.rb +0 -76
- data/config/requirements.rb +0 -15
- data/lib/postini/api/automatedbatch/AutomatedBatch.rb +0 -1244
- data/lib/postini/api/automatedbatch/AutomatedBatchDriver.rb +0 -216
- data/lib/postini/api/automatedbatch/AutomatedBatchMappingRegistry.rb +0 -1883
- data/lib/postini/api/automatedbatch/AutomatedBatchServiceClient.rb +0 -523
- data/lib/postini/api/endpointresolver/EndpointResolver.rb +0 -121
- data/lib/postini/api/endpointresolver/EndpointResolverDriver.rb +0 -51
- data/lib/postini/api/endpointresolver/EndpointResolverMappingRegistry.rb +0 -268
- data/lib/postini/api/endpointresolver/EndpointResolverServiceClient.rb +0 -38
- data/lib/postini/api.rb +0 -8
- data/lib/postini/domain.rb +0 -72
- data/lib/postini/helpers/attributes.rb +0 -94
- data/lib/postini/helpers.rb +0 -13
- data/lib/postini/user.rb +0 -91
- data/lib/postini/users/aliases.rb +0 -55
- data/lib/postini/version.rb +0 -9
- data/spec/attribute_helper_spec.rb +0 -47
- data/spec/domain_spec.rb +0 -36
- data/spec/user_spec.rb +0 -142
- data/website/index.html +0 -89
- data/website/index.txt +0 -60
- data/website/javascripts/rounded_corners_lite.inc.js +0 -285
- data/website/spam_box.png +0 -0
- data/website/stylesheets/screen.css +0 -143
- data/website/template.html.erb +0 -56
@@ -1,55 +0,0 @@
|
|
1
|
-
module Postini
|
2
|
-
module Users
|
3
|
-
|
4
|
-
# Managing aliases of the users
|
5
|
-
module Aliases
|
6
|
-
# Return the list of aliases for the mailbox
|
7
|
-
def aliases
|
8
|
-
if @aliases.nil?
|
9
|
-
remote = self.class.automated_batch_port( self.address )
|
10
|
-
query = Postini::API::AutomatedBatch::ListusersqueryParams.new
|
11
|
-
query.aliases = 1
|
12
|
-
query.childorgs = 1
|
13
|
-
query.primaryqs = self.address
|
14
|
-
query.targetOrg = self.orgid
|
15
|
-
request = Postini::API::AutomatedBatch::Listusers.new(
|
16
|
-
Postini.auth,
|
17
|
-
"ALL",
|
18
|
-
query
|
19
|
-
)
|
20
|
-
|
21
|
-
response = remote.listusers( request )
|
22
|
-
|
23
|
-
@aliases = []
|
24
|
-
response.each { |user_record| @aliases << user_record.address }
|
25
|
-
end
|
26
|
-
|
27
|
-
@aliases
|
28
|
-
end
|
29
|
-
|
30
|
-
# Add an alias to this user
|
31
|
-
def add_alias( address )
|
32
|
-
@aliases = nil # clear our cache
|
33
|
-
remote = self.class.automated_batch_port( self.address )
|
34
|
-
request = Postini::API::AutomatedBatch::Addalias.new( Postini.auth, self.address, address )
|
35
|
-
remote.addalias( request )
|
36
|
-
end
|
37
|
-
|
38
|
-
# Removes the specified alias
|
39
|
-
def remove_alias( address )
|
40
|
-
@aliases = nil # clear our cache
|
41
|
-
remote = self.class.automated_batch_port( self.address )
|
42
|
-
request = Postini::API::AutomatedBatch::Deletealias.new( Postini.auth, address )
|
43
|
-
remote.deletealias( request )
|
44
|
-
end
|
45
|
-
|
46
|
-
# Removes all aliases from the user
|
47
|
-
def clear_aliases
|
48
|
-
aliases.each do |address|
|
49
|
-
remove_alias( address )
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
data/lib/postini/version.rb
DELETED
@@ -1,47 +0,0 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
-
|
3
|
-
class AttributeTester
|
4
|
-
include Postini::Helpers::Attributes
|
5
|
-
|
6
|
-
has_attribute :id => :int
|
7
|
-
has_attribute :now => :timestamp
|
8
|
-
has_attribute :active => :boolean
|
9
|
-
has_attributes :address, :name, :type => :string
|
10
|
-
|
11
|
-
end
|
12
|
-
|
13
|
-
describe Postini::Helpers::Attributes do
|
14
|
-
|
15
|
-
describe "and initialization" do
|
16
|
-
|
17
|
-
it "should handle no attributes" do
|
18
|
-
lambda {
|
19
|
-
at = AttributeTester.new
|
20
|
-
}.should_not raise_error
|
21
|
-
end
|
22
|
-
|
23
|
-
it "should set valid attributes" do
|
24
|
-
at = AttributeTester.new( :id => 1, :active => true )
|
25
|
-
at.id.should be(1)
|
26
|
-
at.active.should be_true
|
27
|
-
end
|
28
|
-
|
29
|
-
it "should raise exceptions on invalid values" do
|
30
|
-
lambda {
|
31
|
-
AttributeTester.new( :wtf => "is this?" )
|
32
|
-
}.should raise_error
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
describe "and getters/setters" do
|
37
|
-
|
38
|
-
before(:each) do
|
39
|
-
@at = AttributeTester.new
|
40
|
-
end
|
41
|
-
|
42
|
-
it "that should act like attr_accessors" do
|
43
|
-
@at.active = true
|
44
|
-
@at.active.should be_true
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
data/spec/domain_spec.rb
DELETED
@@ -1,36 +0,0 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
-
|
3
|
-
describe Postini::Domain do
|
4
|
-
|
5
|
-
describe "a new instance" do
|
6
|
-
before(:each) do
|
7
|
-
@domain = Postini::Domain.new
|
8
|
-
end
|
9
|
-
|
10
|
-
it "should indicate a new instance" do
|
11
|
-
@domain.should be_new
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
describe "a popuated instance" do
|
16
|
-
|
17
|
-
before(:each) do
|
18
|
-
@domain = Postini::Domain.new( :id => 100, :name => 'example.com' )
|
19
|
-
end
|
20
|
-
|
21
|
-
it "should have attibutes passed to #new" do
|
22
|
-
@domain.id.should be(100)
|
23
|
-
@domain.name.should eql('example.com')
|
24
|
-
end
|
25
|
-
|
26
|
-
it "should not be new" do
|
27
|
-
@domain.should_not be_new
|
28
|
-
end
|
29
|
-
|
30
|
-
it "should not be created" do
|
31
|
-
@domain.create.should be_false
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
end
|
36
|
-
|
data/spec/user_spec.rb
DELETED
@@ -1,142 +0,0 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
-
|
3
|
-
describe Postini::User do
|
4
|
-
|
5
|
-
before(:each) do
|
6
|
-
Postini.stubs(:endpoint_uri).with(anything).returns('http://example.com/api2/automatedbatch')
|
7
|
-
Postini.stubs(:auth).with(anything).returns(
|
8
|
-
Postini::API::AutomatedBatch::AuthElem.new(
|
9
|
-
'0000000000000000', 'postini4r@jumboinc.com', 'secret', nil
|
10
|
-
)
|
11
|
-
)
|
12
|
-
end
|
13
|
-
|
14
|
-
describe "provides convenience methods" do
|
15
|
-
it "load a user" do
|
16
|
-
address = 'support@jumboinc.com'
|
17
|
-
|
18
|
-
mock_request = Postini::API::AutomatedBatch::Displayuser.new( Postini.auth, address )
|
19
|
-
Postini::API::AutomatedBatch::Displayuser.expects(:new).with( Postini.auth, address ).returns( mock_request )
|
20
|
-
|
21
|
-
mock_remote = Postini::API::AutomatedBatch::AutomatedBatchPort.new( Postini.endpoint_uri(address) )
|
22
|
-
Postini::User.expects(:automated_batch_port).with(address).returns(mock_remote)
|
23
|
-
|
24
|
-
mock_user = Postini::API::AutomatedBatch::UserRecord.new
|
25
|
-
mock_user.address = address
|
26
|
-
mock_user.user_id = 0
|
27
|
-
|
28
|
-
mock_response = Postini::API::AutomatedBatch::DisplayuserResponse.new( mock_user )
|
29
|
-
|
30
|
-
mock_remote.expects(:displayuser).with(mock_request).returns(mock_response)
|
31
|
-
|
32
|
-
####
|
33
|
-
|
34
|
-
user = Postini::User.find( address )
|
35
|
-
|
36
|
-
user.address.should eql(address)
|
37
|
-
user.id.should be(0)
|
38
|
-
end
|
39
|
-
|
40
|
-
it "destroy a user" do
|
41
|
-
address = 'support@jumboinc.com'
|
42
|
-
|
43
|
-
mock_remote = Postini::API::AutomatedBatch::AutomatedBatchPort.new( Postini.endpoint_uri(address) )
|
44
|
-
mock_remote.expects(:deleteuser).with(anything)
|
45
|
-
Postini::User.expects(:automated_batch_port).with(address).returns(mock_remote)
|
46
|
-
|
47
|
-
Postini::User.destroy(address)
|
48
|
-
|
49
|
-
pending "IMPROVE"
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
describe "when new" do
|
54
|
-
before(:each) do
|
55
|
-
@user = Postini::User.new( :active => "no" )
|
56
|
-
end
|
57
|
-
|
58
|
-
it "should convert the options hash into instance variables" do
|
59
|
-
@user.active.should eql("no")
|
60
|
-
end
|
61
|
-
|
62
|
-
it "should indicate so" do
|
63
|
-
@user.should be_new
|
64
|
-
end
|
65
|
-
|
66
|
-
it "should only be created if all validations pass" do
|
67
|
-
Postini::User.expects(:automated_batch_port).never
|
68
|
-
|
69
|
-
@user.create.should be_false
|
70
|
-
end
|
71
|
-
|
72
|
-
it "should be created if valid" do
|
73
|
-
@user.address = 'support@jumboinc.com'
|
74
|
-
@user.orgid = 'support'
|
75
|
-
|
76
|
-
mock_args = Postini::API::AutomatedBatch::Adduserargs.new( @user.orgid, 0 )
|
77
|
-
Postini::API::AutomatedBatch::Adduserargs.expects(:new).with( @user.orgid, 0 ).returns(mock_args)
|
78
|
-
|
79
|
-
mock_request = Postini::API::AutomatedBatch::Adduser.new( Postini.auth, @user.address, mock_args )
|
80
|
-
Postini::API::AutomatedBatch::Adduser.expects(:new).with( Postini.auth, @user.address, mock_args ).returns(mock_request)
|
81
|
-
|
82
|
-
mock_remote = Postini::API::AutomatedBatch::AutomatedBatchPort.new( Postini.endpoint_uri )
|
83
|
-
Postini::User.expects(:automated_batch_port).returns(mock_remote)
|
84
|
-
mock_remote.expects(:adduser).with(mock_request)
|
85
|
-
|
86
|
-
####
|
87
|
-
|
88
|
-
@user.create
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
describe "when loaded" do
|
93
|
-
before(:each) do
|
94
|
-
@user = Postini::User.new(
|
95
|
-
:id => 0,
|
96
|
-
:address => 'support@jumboinc.com',
|
97
|
-
:orgid => 'support'
|
98
|
-
)
|
99
|
-
end
|
100
|
-
|
101
|
-
it "cannot be created again" do
|
102
|
-
Postini::User.expects(:automated_batch_port).never
|
103
|
-
|
104
|
-
@user.create.should be_false
|
105
|
-
end
|
106
|
-
|
107
|
-
it "can be deleted" do
|
108
|
-
Postini::User.expects(:destroy).with(@user.address)
|
109
|
-
|
110
|
-
@user.destroy
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
describe "and aliases" do
|
115
|
-
before(:each) do
|
116
|
-
@user = Postini::User.new( :address => 'support@jumboinc.com' )
|
117
|
-
|
118
|
-
end
|
119
|
-
|
120
|
-
it "can be loaded" do
|
121
|
-
mock_remote = Postini::API::AutomatedBatch::AutomatedBatchPort.new( Postini.endpoint_uri(@user.address) )
|
122
|
-
Postini::User.expects(:automated_batch_port).with(@user.address).returns(mock_remote)
|
123
|
-
mock_remote.expects(:listusers).with(anything).returns([])
|
124
|
-
|
125
|
-
@user.aliases.should be_empty
|
126
|
-
|
127
|
-
pending "IMPROVE"
|
128
|
-
end
|
129
|
-
|
130
|
-
it "can be added to" do
|
131
|
-
pending
|
132
|
-
end
|
133
|
-
|
134
|
-
it "can be removed" do
|
135
|
-
pending
|
136
|
-
end
|
137
|
-
|
138
|
-
it "can be cleared" do
|
139
|
-
pending
|
140
|
-
end
|
141
|
-
end
|
142
|
-
end
|
data/website/index.html
DELETED
@@ -1,89 +0,0 @@
|
|
1
|
-
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
2
|
-
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
3
|
-
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
4
|
-
<head>
|
5
|
-
<link rel="stylesheet" href="stylesheets/screen.css" type="text/css" media="screen" />
|
6
|
-
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
7
|
-
<title>
|
8
|
-
postini Gem
|
9
|
-
</title>
|
10
|
-
<script src="javascripts/rounded_corners_lite.inc.js" type="text/javascript"></script>
|
11
|
-
<style>
|
12
|
-
|
13
|
-
</style>
|
14
|
-
<script type="text/javascript">
|
15
|
-
window.onload = function() {
|
16
|
-
settings = {
|
17
|
-
tl: { radius: 10 },
|
18
|
-
tr: { radius: 10 },
|
19
|
-
bl: { radius: 10 },
|
20
|
-
br: { radius: 10 },
|
21
|
-
antiAlias: true,
|
22
|
-
autoPad: true,
|
23
|
-
validTags: ["div"]
|
24
|
-
}
|
25
|
-
var versionBox = new curvyCorners(settings, document.getElementById("version"));
|
26
|
-
versionBox.applyCornersToAll();
|
27
|
-
}
|
28
|
-
</script>
|
29
|
-
</head>
|
30
|
-
<body>
|
31
|
-
<div id="main">
|
32
|
-
|
33
|
-
<h1>postini Gem</h1>
|
34
|
-
<div id="version" class="clickable" onclick='document.location = "http://rubyforge.org/projects/postini4r"; return false'>
|
35
|
-
<p>Get Version</p>
|
36
|
-
<a href="http://rubyforge.org/projects/postini4r" class="numbers">0.0.6</a>
|
37
|
-
<a href="http://www.spaminabox.co.za"><img src="spam_box.png" alt="SPAM in a Box" /></a>
|
38
|
-
</div>
|
39
|
-
<h2>Part of the postini4r project</h2>
|
40
|
-
<p>The postini4r project is a collection of sub-projects that aim to implement a broad range of Postini-related tools.</p>
|
41
|
-
<h2>What</h2>
|
42
|
-
<p>The postini gem is a Ruby wrapper library for the <a href="http://www.postini.com/">Postini</a> <span class="caps">SOAP</span> <span class="caps">API</span> (Early Access Program).</p>
|
43
|
-
<p>The postini gem aims to be fully compliant with the Postini <span class="caps">API</span>, starting off with what I need first, and then expanding to eventually cover all the available commands in the Batch Command Reference.</p>
|
44
|
-
<h2>Requirements</h2>
|
45
|
-
<ul>
|
46
|
-
<li>The postini gem requires that you gain access to the Postini Early Access Program and an <span class="caps">API</span> key. You need to contact your Postini Service Provider (or Postini directly if you deal with them).</li>
|
47
|
-
<li>soap4r</li>
|
48
|
-
</ul>
|
49
|
-
<h2>Installing</h2>
|
50
|
-
<p><pre class='syntax'><span class="ident">sudo</span> <span class="ident">gem</span> <span class="ident">install</span> <span class="ident">postini</span></pre></p>
|
51
|
-
<h2>Demonstration of usage</h2>
|
52
|
-
<p><span class="caps">TODO</span> – Coming soon, refer to the current specs for some samples</p>
|
53
|
-
<h2>Forum</h2>
|
54
|
-
<p><a href="http://groups.google.com/group/postini-ruby">http://groups.google.com/group/postini-ruby</a></p>
|
55
|
-
<p>Join us in the postini-ruby group for discussing everything aroung Postini and Ruby, and possibly Postini in general.</p>
|
56
|
-
<h2>How to submit patches</h2>
|
57
|
-
<p>Read the <a href="http://drnicwilliams.com/2007/06/01/8-steps-for-fixing-other-peoples-code/">8 steps for fixing other people’s code</a> and for section <a href="http://drnicwilliams.com/2007/06/01/8-steps-for-fixing-other-peoples-code/#8b-google-groups">8b: Submit patch to Google Groups</a>, use the Google Group above.</p>
|
58
|
-
<p>You can fetch the source from:</p>
|
59
|
-
<ul>
|
60
|
-
<li>github: <a href="http://github.com/kennethkalmer/postini4r-postini/tree/master">http://github.com/kennethkalmer/postini4r-postini/tree/master</a></li>
|
61
|
-
</ul>
|
62
|
-
<pre>git clone git://github.com/kennethkalmer/postini4r-postini.git</pre>
|
63
|
-
<h3>Build and test instructions</h3>
|
64
|
-
<pre>cd postini4r-postini
|
65
|
-
rake spec
|
66
|
-
rake install_gem</pre>
|
67
|
-
<h2>License</h2>
|
68
|
-
<p>This code is free to use under the terms of the <span class="caps">MIT</span> license.</p>
|
69
|
-
<h2>Contact</h2>
|
70
|
-
<p>Comments are welcome. Send an email to <a href="mailto:kenneth.kalmer@gmail.com">me</a> via the <a href="http://groups.google.com/group/postini4r">forum</a></p>
|
71
|
-
<h2>Credits</h2>
|
72
|
-
<p>postini4r is developed by <a href="http://www.opensourcery.co.za/">Kenneth Kalmer</a> for <a href="http://www.spaminabox.co.za"><span class="caps">SPAM</span> in a Box</a> who has allowed the library to be released under the <span class="caps">MIT</span> License through their sponsorship.</p>
|
73
|
-
<p class="coda">
|
74
|
-
<a href="http://www.opensourcery.co.za">Kenneth Kalmer</a> &
|
75
|
-
<a href="http://www.spaminabox.co.za">SPAM in a Box</a>, 11th March 2009<br>
|
76
|
-
Theme extended from <a href="http://rb2js.rubyforge.org/">Paul Battley</a>
|
77
|
-
</p>
|
78
|
-
</div>
|
79
|
-
|
80
|
-
<script type="text/javascript">
|
81
|
-
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
|
82
|
-
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
|
83
|
-
</script>
|
84
|
-
<script type="text/javascript">
|
85
|
-
var pageTracker = _gat._getTracker("UA-192703-10");
|
86
|
-
pageTracker._trackPageview();
|
87
|
-
</script>
|
88
|
-
</body>
|
89
|
-
</html>
|
data/website/index.txt
DELETED
@@ -1,60 +0,0 @@
|
|
1
|
-
h1. postini Gem
|
2
|
-
h2. Part of the postini4r project
|
3
|
-
|
4
|
-
The postini4r project is a collection of sub-projects that aim to implement a broad range of Postini-related tools.
|
5
|
-
|
6
|
-
h2. What
|
7
|
-
|
8
|
-
The postini gem is a Ruby wrapper library for the "Postini":http://www.postini.com/ SOAP API (Early Access Program).
|
9
|
-
|
10
|
-
The postini gem aims to be fully compliant with the Postini API, starting off with what I need first, and then expanding to eventually cover all the available commands in the Batch Command Reference.
|
11
|
-
|
12
|
-
h2. Requirements
|
13
|
-
|
14
|
-
* The postini gem requires that you gain access to the Postini Early Access Program and an API key. You need to contact your Postini Service Provider (or Postini directly if you deal with them).
|
15
|
-
* soap4r
|
16
|
-
|
17
|
-
h2. Installing
|
18
|
-
|
19
|
-
<pre syntax="ruby">sudo gem install postini</pre>
|
20
|
-
|
21
|
-
h2. Demonstration of usage
|
22
|
-
|
23
|
-
TODO - Coming soon, refer to the current specs for some samples
|
24
|
-
|
25
|
-
h2. Forum
|
26
|
-
|
27
|
-
"http://groups.google.com/group/postini-ruby":http://groups.google.com/group/postini-ruby
|
28
|
-
|
29
|
-
Join us in the postini-ruby group for discussing everything aroung Postini and Ruby, and possibly Postini in general.
|
30
|
-
|
31
|
-
h2. How to submit patches
|
32
|
-
|
33
|
-
Read the "8 steps for fixing other people's code":http://drnicwilliams.com/2007/06/01/8-steps-for-fixing-other-peoples-code/ and for section "8b: Submit patch to Google Groups":http://drnicwilliams.com/2007/06/01/8-steps-for-fixing-other-peoples-code/#8b-google-groups, use the Google Group above.
|
34
|
-
|
35
|
-
You can fetch the source from:
|
36
|
-
|
37
|
-
* github: "http://github.com/kennethkalmer/postini4r-postini/tree/master":http://github.com/kennethkalmer/postini4r-postini/tree/master
|
38
|
-
|
39
|
-
<pre>git clone git://github.com/kennethkalmer/postini4r-postini.git</pre>
|
40
|
-
|
41
|
-
|
42
|
-
h3. Build and test instructions
|
43
|
-
|
44
|
-
<pre>cd postini4r-postini
|
45
|
-
rake spec
|
46
|
-
rake install_gem</pre>
|
47
|
-
|
48
|
-
|
49
|
-
h2. License
|
50
|
-
|
51
|
-
This code is free to use under the terms of the MIT license.
|
52
|
-
|
53
|
-
h2. Contact
|
54
|
-
|
55
|
-
Comments are welcome. Send an email to "me":mailto:kenneth.kalmer@gmail.com via the "forum":http://groups.google.com/group/postini4r
|
56
|
-
|
57
|
-
h2. Credits
|
58
|
-
|
59
|
-
postini4r is developed by "Kenneth Kalmer":http://www.opensourcery.co.za/ for "SPAM in a Box":http://www.spaminabox.co.za who has allowed the library to be released under the MIT License through their sponsorship.
|
60
|
-
|