ruby_gntp 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/ChangeLog CHANGED
@@ -16,3 +16,15 @@
16
16
  == Version 0.1.1 - 2009/4/17
17
17
  * Add NOTE to example directory.
18
18
 
19
+ == Version 0.1.2 - 2009/08/15
20
+ * Enabled password authentication mainly for sending notifications to a network machine.
21
+
22
+ == Version 0.1.3 - 2009/08/19
23
+ * Added notification icon sending.
24
+ * Now sends out Origin-X headers.
25
+ * Use \r\n instead of \n in the header lines.
26
+
27
+ == Version 0.2.0 - 2009/11/03
28
+ * Merge spidahman's commits. Lot of thanks, spidahman!
29
+ * Add some test(spec).
30
+
data/lib/ruby_gntp.rb CHANGED
@@ -29,6 +29,7 @@
29
29
  #
30
30
  #}}}
31
31
  require 'socket'
32
+ require 'digest/md5'
32
33
 
33
34
  #$DEBUG = true
34
35
 
@@ -36,13 +37,17 @@ class TooFewParametersError < Exception
36
37
  end
37
38
 
38
39
  class GNTP
39
- attr_reader :app_name, :target_host, :target_port
40
+ attr_reader :app_name, :target_host, :target_port, :password
40
41
  attr_reader :message if $DEBUG
41
42
 
42
- def initialize(app_name = 'Ruby/GNTP', host = 'localhost', port = 23053)
43
+ RUBY_GNTP_NAME = 'ruby_gntp'
44
+ RUBY_GNTP_VERSION = '0.1.3.1'
45
+
46
+ def initialize(app_name = 'Ruby/GNTP', host = 'localhost', password = '', port = 23053)
43
47
  @app_name = app_name
44
48
  @target_host = host
45
49
  @target_port = port
50
+ @password = password
46
51
  end
47
52
 
48
53
  #
@@ -53,36 +58,37 @@ class GNTP
53
58
  raise TooFewParametersError, "Need least one 'notification' for register" unless @notifications
54
59
 
55
60
  @app_icon = params[:app_icon]
61
+ @binaries = []
62
+
63
+ @message = register_header(@app_name, @app_icon)
64
+ @message << output_origin_headers
56
65
 
57
- @message = <<EOF
58
- GNTP/1.0 REGISTER NONE
59
- Application-Name: #{@app_name}
60
- Notifications-Count: #{@notifications.size}
61
- EOF
62
- @message << "Application-Icon: #{@app_icon}\n" if @app_icon
63
- @message << "\n"
66
+ @message << "Notifications-Count: #{@notifications.size}\r\n"
67
+ @message << "\r\n"
64
68
 
65
69
  @notifications.each do |notification|
66
70
  name = notification[:name]
67
- disp_name = notification[:disp_name]
71
+ disp_name = notification[:disp_name] || name
68
72
  enabled = notification[:enabled] || true
69
73
  icon = notification[:icon]
70
74
 
71
- @message += <<EOF
72
- Notification-Name: #{name}
73
- EOF
74
- @message << "Notification-Display-Name: #{disp_name}\n" if disp_name
75
- @message << "Notification-Enabled: #{enabled}\n" if enabled
76
- @message << "Notification-Icon: #{icon}\n" if icon
77
- @message << "\n"
75
+ @message << "Notification-Name: #{name}\r\n"
76
+ @message << "Notification-Enabled: #{enabled ? 'True' : 'False'}\r\n"
77
+ @message << "Notification-Display-Name: #{disp_name}\r\n"
78
+ @message << "#{handle_icon(icon, 'Notification')}\r\n" if icon
78
79
  end
79
80
 
81
+ @binaries.each {|binary|
82
+ @message << output_binary(binary)
83
+ }
84
+
85
+ @message << "\r\n"
86
+
80
87
  unless (ret = send_and_recieve(@message))
81
88
  raise "Register failed"
82
89
  end
83
90
  end
84
91
 
85
-
86
92
  #
87
93
  # notify
88
94
  #
@@ -95,16 +101,16 @@ EOF
95
101
  icon = params[:icon] || get_notification_icon(name)
96
102
  sticky = params[:sticky]
97
103
 
98
- @message = <<EOF
99
- GNTP/1.0 NOTIFY NONE
100
- Application-Name: #{@app_name}
101
- Notification-Name: #{name}
102
- Notification-Title: #{title}
103
- EOF
104
- @message << "Notification-Text: #{text}\n" if text
105
- @message << "Notification-Sticky: #{sticky}\n" if sticky
106
- @message << "Notification-Icon: #{icon}\n" if icon
107
- @message << "\n"
104
+ @binaries = []
105
+
106
+ @message = notify_header(app_name, name, title, text, sticky, icon)
107
+ @message << output_origin_headers
108
+
109
+ @binaries.each {|binary|
110
+ @message << output_binary(binary)
111
+ }
112
+
113
+ @message << "\r\n"
108
114
 
109
115
  unless (ret = send_and_recieve(@message))
110
116
  raise "Notify failed"
@@ -116,7 +122,11 @@ EOF
116
122
  # instant notification
117
123
  #
118
124
  def self.notify(params)
119
- growl = GNTP.new(params[:app_name])
125
+ host = params[:host]
126
+ passwd = params[:passwd]
127
+
128
+ growl = GNTP.new(params[:app_name], host, passwd)
129
+
120
130
  notification = params
121
131
  notification[:name] = params[:app_name] || "Ruby/GNTP notification"
122
132
  growl.register(:notifications => [
@@ -131,7 +141,6 @@ EOF
131
141
  # send and recieve
132
142
  #
133
143
  def send_and_recieve msg
134
- msg.gsub!(/\n/, "\r\n")
135
144
  print msg if $DEBUG
136
145
 
137
146
  sock = TCPSocket.open(@target_host, @target_port)
@@ -156,29 +165,132 @@ EOF
156
165
  return nil unless notification
157
166
  return notification[:icon]
158
167
  end
168
+
169
+ #
170
+ # outputs the registration header
171
+ #
172
+ def register_header(app_name, app_icon)
173
+ message = "#{get_gntp_header_start('REGISTER')}\r\n"
174
+ message << "Application-Name: #{app_name}\r\n"
175
+ message << "#{handle_icon(@app_icon, 'Application')}\r\n" if app_icon
176
+ message
177
+ end
178
+
179
+ #
180
+ # outputs the notification header
181
+ #
182
+ def notify_header(app_name, name, title, text, sticky, icon)
183
+ message = "#{get_gntp_header_start('NOTIFY')}\r\n"
184
+ message << "Application-Name: #{@app_name}\r\n"
185
+ message << "Notification-Name: #{name}\r\n"
186
+ message << "Notification-Title: #{title}\r\n"
187
+ message << "Notification-Text: #{text}\r\n" if text
188
+ message << "Notification-Sticky: #{sticky}\r\n" if sticky
189
+ message << "#{handle_icon(icon, 'Notification')}\r\n" if icon
190
+ message
191
+ end
192
+
193
+ def output_origin_headers
194
+ message = "Origin-Machine-Name: #{Socket.gethostname}\r\n"
195
+ message << "Origin-Software-Name: #{RUBY_GNTP_NAME}\r\n"
196
+ message << "Origin-Software-Version: #{RUBY_GNTP_VERSION}\r\n"
197
+
198
+ platformname, platformversion = '', ''
199
+
200
+ if ENV['OS']
201
+ ver = `ver`
202
+ if ver.index('[')
203
+ matches = ver.scan(/(.*)\[+(.*)\]+/)[0]
204
+ platformname, platformversion = matches[0], matches[1]
205
+ else
206
+ platformname, platformversion = 'Microsoft Windows', ver
207
+ end
208
+ else
209
+ platformname, platformversion = `uname -s`, `uname -r`
210
+ end
211
+
212
+ message << "Origin-Platform-Name: #{platformname.strip}\r\n"
213
+ message << "Origin-Platform-Version: #{platformversion.strip}\r\n"
214
+ end
215
+
216
+ #
217
+ # get start of the GNTP header
218
+ #
219
+ def get_gntp_header_start(type)
220
+ if @password.empty?
221
+ "GNTP/1.0 #{type} NONE"
222
+ else
223
+ saltvar = Time.now.to_s
224
+ salt = Digest::MD5.digest(saltvar)
225
+ salthash = Digest::MD5.hexdigest(saltvar)
226
+ key = Digest::MD5.digest("#{@password}#{salt}")
227
+ keyhash = Digest::MD5.hexdigest(key)
228
+ "GNTP/1.0 #{type} NONE MD5:#{keyhash}.#{salthash}"
229
+ end
230
+ end
231
+
232
+ #
233
+ # figure out how to handle the icon
234
+ # a URL icon just gets put into the header
235
+ # a file icon gets read and stored, ready to be appended to the end of the request
236
+ #
237
+ def handle_icon(icon, type)
238
+ if File.exists?(icon) && @target_host != 'localhost'
239
+ file = File.new(icon)
240
+ data = file.read
241
+ size = data.length
242
+ if size > 0
243
+ binary = {
244
+ :size => size,
245
+ :data => data,
246
+ :uniqueid => Digest::MD5.hexdigest(data)
247
+ }
248
+ @binaries << binary
249
+ "#{type}-Icon: x-growl-resource://#{binary[:uniqueid]}"
250
+ end
251
+ else
252
+ "#{type}-Icon: #{icon}"
253
+ end
254
+ end
255
+
256
+ #
257
+ # outputs any binary data to be sent
258
+ #
259
+ def output_binary(binary)
260
+ message = "\r\n"
261
+ message << "Identifier: #{binary[:uniqueid]}\r\n"
262
+ message << "Length: #{binary[:size]}\r\n"
263
+ message << "\r\n"
264
+ message << "#{binary[:data]}\r\n"
265
+ end
159
266
  end
160
267
 
161
268
  #----------------------------
162
269
  # self test code
163
270
  if __FILE__ == $0
271
+ host = ARGV[0] || 'loaclhost'
272
+ passwd = host && ARGV[1]
273
+
164
274
  #--- Use standard notification method ('register' first then 'notify')
165
- growl = GNTP.new("Ruby/GNTP self test")
166
- growl.register({:notifications => [{
275
+ growl = GNTP.new("Ruby/GNTP self test", host, passwd)
276
+ growl.register(:notifications => [{
167
277
  :name => "notify",
168
- :enabled => true,
169
- }]})
278
+ :enabled => true
279
+ }])
170
280
 
171
- growl.notify({
281
+ growl.notify(
172
282
  :name => "notify",
173
283
  :title => "Congraturation",
174
284
  :text => "Congraturation! You are successful install ruby_gntp.",
175
285
  :icon => "http://www.hatena.ne.jp/users/sn/snaka72/profile.gif",
176
- :sticky=> true,
177
- })
286
+ :sticky=> true
287
+ )
178
288
 
179
289
  #--- Use instant notification method (just 'notify')
180
290
  GNTP.notify({
181
291
  :app_name => "Instant notify",
292
+ :host => host,
293
+ :passwd => passwd,
182
294
  :title => "Instant notification",
183
295
  :text => "Instant notification available now.",
184
296
  :icon => "http://www.hatena.ne.jp/users/sn/snaka72/profile.gif",
@@ -0,0 +1,74 @@
1
+ require '../lib/ruby_gntp'
2
+ require 'ruby_gntp_spec_helper'
3
+
4
+ # use Double Ruby for mock/stub framework.
5
+ Spec::Runner.configure do |conf|
6
+ conf.mock_with :rr
7
+ end
8
+
9
+ # describe GNTP behavior
10
+ describe GNTP do
11
+ include GNTPExampleHelperMethods
12
+
13
+ DEFAULT_APP_NAME = "Ruby/GNTP"
14
+ NOTIFICATION_NAME = "TestApp"
15
+
16
+ before do
17
+ @sended_messages = []
18
+ @ok_response = StringIO.new(["GNTP/1.0 -OK NONE\r\n", "\r\n"].join)
19
+ @opened_socket = create_stub_socket(@ok_response, @sended_messages)
20
+ end
21
+
22
+ it "can register notifications with minimum params" do
23
+ @gntp = GNTP.new
24
+ @gntp.register :notifications => [{:name => NOTIFICATION_NAME}]
25
+
26
+ [
27
+ "GNTP/1.0 REGISTER NONE\r\n",
28
+ "Application-Name: #{DEFAULT_APP_NAME}\r\n",
29
+ "Notifications-Count: 1\r\n",
30
+ "\r\n",
31
+ "Notification-Name: #{NOTIFICATION_NAME}\r\n",
32
+ "Notification-Display-Name: #{NOTIFICATION_NAME}\r\n",
33
+ "Notification-Enabled: True\r\n"
34
+ ].each {|expected_text|
35
+ @sended_messages.last.should include(expected_text)
36
+ }
37
+ end
38
+
39
+ it "can register notifications to remote host" do
40
+ @gntp = GNTP.new "TestApp", "1.2.3.4", "password", 12345
41
+ @gntp.register :notifications => [{:name => NOTIFICATION_NAME}]
42
+
43
+ @opened_socket[:host].should == "1.2.3.4"
44
+ @opened_socket[:port].should == 12345
45
+
46
+ @sended_messages.last.first.should match(/GNTP\/1\.0 REGISTER NONE MD5:\S+\r\n/)
47
+ [
48
+ "Application-Name: TestApp\r\n",
49
+ "Notifications-Count: 1\r\n",
50
+ "\r\n",
51
+ "Notification-Name: #{NOTIFICATION_NAME}\r\n",
52
+ "Notification-Display-Name: #{NOTIFICATION_NAME}\r\n",
53
+ "Notification-Enabled: True\r\n"
54
+ ].each {|expected_text|
55
+ @sended_messages.last.should include(expected_text)
56
+ }
57
+ end
58
+
59
+ it "can notify with minimum params" do
60
+ @gntp = GNTP.new
61
+ @gntp.register :notifications => [{:name => NOTIFICATION_NAME}]
62
+ @gntp.notify :name => NOTIFICATION_NAME
63
+
64
+ [
65
+ "GNTP/1.0 NOTIFY NONE\r\n",
66
+ "Application-Name: #{DEFAULT_APP_NAME}\r\n",
67
+ "Notification-Name: #{NOTIFICATION_NAME}\r\n",
68
+ "Notification-Title: \r\n"
69
+ ].each {|expected_text|
70
+ @sended_messages.last.should include(expected_text)
71
+ }
72
+ end
73
+
74
+ end
@@ -0,0 +1,34 @@
1
+ module GNTPExampleHelperMethods
2
+
3
+ def create_stub_socket(ok_response, sended_messages)
4
+
5
+ sock = {}
6
+
7
+ stub(sock).write do |string|
8
+ lines = []
9
+ buf = StringIO.new(string)
10
+
11
+ while line = buf.gets
12
+ lines << line
13
+ end
14
+
15
+ sended_messages << lines
16
+ ok_response.rewind
17
+ end
18
+
19
+ stub(sock).gets do
20
+ ok_response.gets
21
+ end
22
+
23
+ stub(sock).close
24
+
25
+ stub(TCPSocket).open do |host, port|
26
+ sock[:host] = host
27
+ sock[:port] = port
28
+ sock
29
+ end
30
+
31
+ sock
32
+ end
33
+
34
+ end
metadata CHANGED
@@ -1,20 +1,23 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_gntp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - snaka
8
+ - David Hayward (spidah)
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
12
 
12
- date: 2009-04-20 00:00:00 +09:00
13
+ date: 2009-11-04 00:00:00 +09:00
13
14
  default_executable:
14
15
  dependencies: []
15
16
 
16
17
  description:
17
- email: snaka.gml@gmail.com
18
+ email:
19
+ - snaka.gml@gmail.com
20
+ - spidahman@gmail.com
18
21
  executables: []
19
22
 
20
23
  extensions: []
@@ -25,10 +28,12 @@ files:
25
28
  - lib/ruby_gntp.rb
26
29
  - example/twitter_notifier.rb
27
30
  - example/gntp-notify
31
+ - test/ruby_gntp_spec.rb
32
+ - test/ruby_gntp_spec_helper.rb
28
33
  - README
29
34
  - TODO
30
35
  - ChangeLog
31
- has_rdoc: false
36
+ has_rdoc: true
32
37
  homepage: http://snaka.github.com/ruby_gntp/
33
38
  licenses: []
34
39
 
@@ -52,7 +57,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
52
57
  requirements: []
53
58
 
54
59
  rubyforge_project:
55
- rubygems_version: 1.3.2
60
+ rubygems_version: 1.3.4
56
61
  signing_key:
57
62
  specification_version: 3
58
63
  summary: Ruby library for GNTP(Growl Notification Transport Protocol) client