catfriend 0.17 → 0.18
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.
- checksums.yaml +4 -4
- data/bin/catfriend +41 -22
- data/catfriend.example +0 -2
- data/lib/catfriend/dbus.rb +16 -10
- data/lib/catfriend/imap.rb +36 -33
- data/lib/catfriend/net_imap_exchange_patch.rb +3 -0
- data/lib/catfriend/thread.rb +0 -1
- metadata +32 -18
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bf790e35894bdf6de7a9450951dca7930b8601fa
|
|
4
|
+
data.tar.gz: 81180fcbf3e255cf107e66c7971b0804fc30bc19
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f43f0bc6d274435e575efdc2f5ccf5611ea375c29e539253be4cb1282026c842613e49d857296bfffe3497fa49a707723eea31ba2a573aea687c32b7f5109dc3
|
|
7
|
+
data.tar.gz: 32d5d40171b60f2dbff8e9a9210de7e48d9586d0e65351721c22729a02678f494e3a53baa41f028cd2019322742468d1144e210f33cdff83c0989d8584fdb291
|
data/bin/catfriend
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
# Copyright:: Copyright (c) 2011-2014 James Pike
|
|
8
8
|
# License:: MIT
|
|
9
9
|
|
|
10
|
-
$LOAD_PATH
|
|
10
|
+
$LOAD_PATH.unshift('lib') if File.exists? 'lib/catfriend'
|
|
11
11
|
|
|
12
12
|
require 'catfriend/filetokenstack'
|
|
13
13
|
require 'catfriend/imap'
|
|
@@ -47,10 +47,8 @@ def self.parse_config
|
|
|
47
47
|
|
|
48
48
|
# obviously assigning it in a loop like this is slow but hey it's
|
|
49
49
|
# only run-once config and ruby people say DRY a lot.
|
|
50
|
-
shift_tokens =
|
|
51
|
-
if tokens.empty?
|
|
52
|
-
raise ConfigError, "field #{field} requires parameter"
|
|
53
|
-
end
|
|
50
|
+
shift_tokens = -> do
|
|
51
|
+
raise ConfigError, "field #{field} requires parameter" if tokens.empty?
|
|
54
52
|
return tokens.shift
|
|
55
53
|
end
|
|
56
54
|
|
|
@@ -64,17 +62,15 @@ def self.parse_config
|
|
|
64
62
|
current[:host] = shift_tokens.call
|
|
65
63
|
when "notificationTimeout", "errorTimeout", "socketTimeout"
|
|
66
64
|
# convert from camelCase to camel_case
|
|
67
|
-
clean_field =
|
|
68
|
-
field.gsub(/([a-z])([A-Z])/) { "#{$1}_#{$2.downcase}" }
|
|
65
|
+
clean_field = field.gsub(/([a-z])([A-Z])/) { "#{$1}_#{$2.downcase}" }
|
|
69
66
|
defaults[clean_field] = shift_tokens.call
|
|
70
67
|
when "checkInterval"
|
|
71
68
|
shift_tokens.call # deprecated, ignore parameter
|
|
72
69
|
when "cert_file"
|
|
73
70
|
cert_file = shift_tokens.call
|
|
74
71
|
unless File.exists? cert_file
|
|
75
|
-
raise ConfigError,
|
|
76
|
-
|
|
77
|
-
", search path: #{File.dirname(config_file)}/"
|
|
72
|
+
raise ConfigError, "non-existant SSL certificate `#{cert_file}', "
|
|
73
|
+
+ "search path: #{File.dirname(config_file)}/"
|
|
78
74
|
end
|
|
79
75
|
current[:cert_file] = cert_file
|
|
80
76
|
when "mailbox", "id", "user", "password"
|
|
@@ -84,8 +80,7 @@ def self.parse_config
|
|
|
84
80
|
when "work"
|
|
85
81
|
current[:work_account] = true
|
|
86
82
|
else
|
|
87
|
-
raise ConfigError,
|
|
88
|
-
"invalid config parameter '#{field}'"
|
|
83
|
+
raise ConfigError, "invalid config parameter '#{field}'"
|
|
89
84
|
end
|
|
90
85
|
end
|
|
91
86
|
|
|
@@ -96,8 +91,8 @@ def self.parse_config
|
|
|
96
91
|
end
|
|
97
92
|
|
|
98
93
|
# Main interface to the application. Reads all servers from config then runs
|
|
99
|
-
# each one in a thread.
|
|
100
|
-
#
|
|
94
|
+
# each one in a thread. If the ImapServer or Dbus threads raise an error then
|
|
95
|
+
# this causes the program to exit.
|
|
101
96
|
def self.main args
|
|
102
97
|
work_accounts = false
|
|
103
98
|
foreground = false
|
|
@@ -133,21 +128,19 @@ def self.main args
|
|
|
133
128
|
Catfriend.verbose = false unless foreground
|
|
134
129
|
|
|
135
130
|
servers = parse_config
|
|
136
|
-
servers.reject!
|
|
131
|
+
servers.reject! &:work_account unless work_accounts
|
|
137
132
|
raise ConfigError, "no servers to check" if servers.empty?
|
|
138
133
|
|
|
139
134
|
if foreground
|
|
140
135
|
main_loop servers
|
|
141
136
|
else
|
|
142
|
-
pid = fork
|
|
143
|
-
main_loop servers
|
|
144
|
-
end
|
|
137
|
+
pid = fork { main_loop servers }
|
|
145
138
|
Process.detach pid
|
|
146
139
|
end
|
|
147
140
|
rescue ConfigError => e
|
|
148
141
|
puts "misconfiguration: #{e.message}"
|
|
149
142
|
rescue Interrupt
|
|
150
|
-
servers.each
|
|
143
|
+
servers.each &:kill
|
|
151
144
|
rescue => e
|
|
152
145
|
puts "unknown error #{e.message}\n#{e.backtrace.join("\n")}"
|
|
153
146
|
else
|
|
@@ -158,10 +151,36 @@ def self.main args
|
|
|
158
151
|
end
|
|
159
152
|
|
|
160
153
|
def self.main_loop servers
|
|
161
|
-
|
|
154
|
+
mutex = Mutex.new
|
|
155
|
+
error_condition = ConditionVariable.new
|
|
156
|
+
dbus = DBus.new
|
|
157
|
+
|
|
158
|
+
on_error = proc do
|
|
159
|
+
mutex.synchronize { error_condition.signal }
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
dbus.on :disconnect, on_error
|
|
163
|
+
|
|
164
|
+
servers.each do |server|
|
|
165
|
+
server.on(:error) do |message|
|
|
166
|
+
$stderr.puts "#{server.id}: #{message}"
|
|
167
|
+
on_error
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# start all child threads
|
|
162
172
|
dbus.start
|
|
163
|
-
servers.each
|
|
164
|
-
|
|
173
|
+
servers.each &:start
|
|
174
|
+
|
|
175
|
+
# then wait for an error
|
|
176
|
+
mutex.synchronize { error_condition.wait(mutex) }
|
|
177
|
+
|
|
178
|
+
# clean up and exit
|
|
179
|
+
Catfriend.whisper 'caught error condition, disconnecting from servers'
|
|
180
|
+
servers.each &:disconnect
|
|
181
|
+
|
|
182
|
+
Catfriend.whisper 'joining child threads'
|
|
183
|
+
servers.each &:join
|
|
165
184
|
dbus.join
|
|
166
185
|
end
|
|
167
186
|
|
data/catfriend.example
CHANGED
|
@@ -15,5 +15,3 @@ host insecure.work.com
|
|
|
15
15
|
# time notification remains on screen in milliseconds
|
|
16
16
|
notificationTimeout 10000
|
|
17
17
|
errorTimeout 60000 # as above but for error notifications
|
|
18
|
-
socketTimeout 60 # server socket timeout in seconds
|
|
19
|
-
checkInterval 60 # how often to wait between checks in seconds
|
data/lib/catfriend/dbus.rb
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
require 'catfriend/thread'
|
|
2
|
-
require 'catfriend/server'
|
|
3
1
|
require 'dbus'
|
|
2
|
+
require 'events'
|
|
3
|
+
|
|
4
|
+
require_relative 'thread'
|
|
5
|
+
require_relative 'server'
|
|
4
6
|
|
|
5
7
|
module Catfriend
|
|
6
8
|
|
|
@@ -8,13 +10,17 @@ SERVICE = "org.freedesktop.Catfriend"
|
|
|
8
10
|
PATH = "/org/freedesktop/Catfriend"
|
|
9
11
|
INTERFACE = "org.freedesktop.Catfriend.System"
|
|
10
12
|
|
|
13
|
+
# Represent a DBUS server interface and includes an associated thread
|
|
14
|
+
# in which to run the dbus methods.
|
|
11
15
|
class DBus
|
|
12
16
|
include Thread
|
|
17
|
+
include Events::Emitter
|
|
13
18
|
|
|
19
|
+
# This child class fulfils the DBus::Object interface
|
|
14
20
|
class DBusObject < ::DBus::Object
|
|
15
|
-
def initialize(main,
|
|
21
|
+
def initialize(main, emitter)
|
|
16
22
|
@main = main
|
|
17
|
-
@
|
|
23
|
+
@emitter = emitter
|
|
18
24
|
super PATH
|
|
19
25
|
end
|
|
20
26
|
|
|
@@ -22,19 +28,17 @@ class DBus
|
|
|
22
28
|
dbus_method :stop do
|
|
23
29
|
Catfriend.whisper "received shutdown request"
|
|
24
30
|
@main.quit # this must be run from within method handler
|
|
25
|
-
@
|
|
31
|
+
@emitter.emit :disconnect
|
|
26
32
|
end
|
|
27
33
|
end
|
|
28
34
|
end
|
|
29
35
|
|
|
30
|
-
|
|
31
|
-
@servers = servers
|
|
32
|
-
end
|
|
33
|
-
|
|
36
|
+
# Start the DBus interface
|
|
34
37
|
def init
|
|
35
38
|
@bus = ::DBus::SessionBus.instance unless @bus
|
|
36
39
|
end
|
|
37
40
|
|
|
41
|
+
# Attempt to shutdown another application listening on catfriend's address.
|
|
38
42
|
def send_shutdown
|
|
39
43
|
init
|
|
40
44
|
service = @bus.service(SERVICE)
|
|
@@ -47,12 +51,14 @@ class DBus
|
|
|
47
51
|
false
|
|
48
52
|
end
|
|
49
53
|
|
|
54
|
+
# Call send_shutdown then start the DBus interface.
|
|
50
55
|
def start_service
|
|
51
|
-
object = DBusObject.new(@main,
|
|
56
|
+
object = DBusObject.new(@main, self)
|
|
52
57
|
service = @bus.request_service(SERVICE)
|
|
53
58
|
service.export object
|
|
54
59
|
end
|
|
55
60
|
|
|
61
|
+
# Thread mixin interface: Run the DBus server in a virtual thread
|
|
56
62
|
def run
|
|
57
63
|
init
|
|
58
64
|
if send_shutdown
|
data/lib/catfriend/imap.rb
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
require 'libnotify'
|
|
2
|
-
require '
|
|
3
|
-
require 'catfriend/thread'
|
|
4
|
-
|
|
2
|
+
require 'events'
|
|
5
3
|
require 'net/imap'
|
|
6
4
|
|
|
5
|
+
require_relative 'server'
|
|
6
|
+
require_relative 'thread'
|
|
7
|
+
require_relative 'net_imap_exchange_patch'
|
|
8
|
+
|
|
7
9
|
# unless I do this I get random errors from Libnotify on startup 90% of the
|
|
8
10
|
# time... this could be a bug in autoload or ruby 1.9 rather than libnotify
|
|
9
11
|
module Libnotify
|
|
@@ -12,13 +14,12 @@ end
|
|
|
12
14
|
|
|
13
15
|
module Catfriend
|
|
14
16
|
|
|
15
|
-
require_relative 'net_imap_exchange_patch'
|
|
16
|
-
|
|
17
17
|
# This class represents a thread capable of checking and creating
|
|
18
18
|
# notifications for a single mailbox on a single IMAP server.
|
|
19
19
|
class ImapServer
|
|
20
20
|
include Thread
|
|
21
21
|
include AccessorsFromHash
|
|
22
|
+
include Events::Emitter
|
|
22
23
|
|
|
23
24
|
# Create new IMAP server with optional full configuration hash.
|
|
24
25
|
# If the hash is not supplied at construction a further call must be
|
|
@@ -32,15 +33,9 @@ class ImapServer
|
|
|
32
33
|
def configure args
|
|
33
34
|
super args
|
|
34
35
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if not @host
|
|
39
|
-
raise ConfigError, "imap host not set"
|
|
40
|
-
end
|
|
41
|
-
if not @password
|
|
42
|
-
raise ConfigError, "imap password not set"
|
|
43
|
-
end
|
|
36
|
+
raise ConfigError, "imap user not set" unless @user
|
|
37
|
+
raise ConfigError, "imap host not set" unless @host
|
|
38
|
+
raise ConfigError, "imap password not set" unless @password
|
|
44
39
|
end
|
|
45
40
|
|
|
46
41
|
# The id is a token which represents this server when displaying
|
|
@@ -50,18 +45,16 @@ class ImapServer
|
|
|
50
45
|
|
|
51
46
|
# Raise an error related to this particular server.
|
|
52
47
|
def error message
|
|
53
|
-
|
|
54
|
-
puts "#{id}: #{message}"
|
|
48
|
+
emit :error, message
|
|
55
49
|
end
|
|
56
50
|
|
|
57
51
|
# ThreadMixin interface. This connects to the mailserver and then
|
|
58
|
-
# runs #
|
|
52
|
+
# runs #sleep_until_change to do the e-mail checking if the connection
|
|
59
53
|
# succeeds.
|
|
60
54
|
def run
|
|
61
55
|
begin
|
|
62
56
|
@notification =
|
|
63
|
-
Libnotify.new :body => nil,
|
|
64
|
-
:timeout => Catfriend.notification_timeout
|
|
57
|
+
Libnotify.new :body => nil, :timeout => Catfriend.notification_timeout
|
|
65
58
|
@message_count = connect
|
|
66
59
|
notify_message @message_count
|
|
67
60
|
# :body => nil means summary only
|
|
@@ -71,25 +64,27 @@ class ImapServer
|
|
|
71
64
|
error "no response to connect, try ssl"
|
|
72
65
|
else
|
|
73
66
|
loop {
|
|
74
|
-
|
|
67
|
+
sleep_until_change
|
|
75
68
|
break if stopping?
|
|
76
69
|
}
|
|
77
70
|
end
|
|
78
71
|
end
|
|
79
72
|
|
|
80
|
-
#
|
|
81
|
-
|
|
82
|
-
# on an unrecoverable error.
|
|
83
|
-
def check_loop
|
|
73
|
+
# Waits until an event occurs which could change the message count.
|
|
74
|
+
def sleep_until_change
|
|
84
75
|
@imap.idle do |r|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
76
|
+
begin
|
|
77
|
+
Catfriend.whisper "#{id}: #{r}"
|
|
78
|
+
next if r.instance_of? Net::IMAP::ContinuationRequest
|
|
79
|
+
|
|
80
|
+
if r.instance_of? Net::IMAP::UntaggedResponse
|
|
81
|
+
case r.name
|
|
82
|
+
when 'EXISTS', 'EXPUNGE'
|
|
83
|
+
@imap.idle_done
|
|
84
|
+
end
|
|
92
85
|
end
|
|
86
|
+
rescue => e
|
|
87
|
+
error e.message
|
|
93
88
|
end
|
|
94
89
|
end
|
|
95
90
|
|
|
@@ -110,20 +105,25 @@ class ImapServer
|
|
|
110
105
|
end
|
|
111
106
|
end
|
|
112
107
|
|
|
108
|
+
# Update the associated notification with message and show it if it is not
|
|
109
|
+
# already displayed.
|
|
113
110
|
def notify_message message
|
|
114
111
|
@notification.update :summary => "#{id}: #{message}"
|
|
115
112
|
Catfriend.whisper @notification.summary
|
|
116
113
|
end
|
|
117
114
|
|
|
115
|
+
# Returns false until kill/disconnect have been called.
|
|
118
116
|
def stopping?
|
|
119
117
|
stopped? or @stopping
|
|
120
118
|
end
|
|
121
119
|
|
|
120
|
+
# Disconnect from the imap server and stop the associated thread.
|
|
122
121
|
def kill
|
|
123
122
|
disconnect
|
|
124
123
|
super
|
|
125
124
|
end
|
|
126
125
|
|
|
126
|
+
# Ask IMAP server for count of unseen messages.
|
|
127
127
|
def get_unseen_count
|
|
128
128
|
begin
|
|
129
129
|
# fetch raises an exception when the mailbox is empty
|
|
@@ -137,7 +137,7 @@ class ImapServer
|
|
|
137
137
|
# Connect to the configured IMAP server and return message count.
|
|
138
138
|
def connect
|
|
139
139
|
args = nil
|
|
140
|
-
|
|
140
|
+
unless @no_ssl
|
|
141
141
|
if @cert_file
|
|
142
142
|
args = { :ssl => { :ca_file => @cert_file } }
|
|
143
143
|
else
|
|
@@ -151,6 +151,9 @@ class ImapServer
|
|
|
151
151
|
get_unseen_count
|
|
152
152
|
end
|
|
153
153
|
|
|
154
|
+
# Reconnect to the server showing a "[reconnecting]" message. After
|
|
155
|
+
# reconnection then show the message count if it has changed from
|
|
156
|
+
# what was remembered before the disconnect.
|
|
154
157
|
def reconnect
|
|
155
158
|
notify_message "#{@message_count} [reconnecting]"
|
|
156
159
|
new_count = connect
|
|
@@ -168,7 +171,7 @@ class ImapServer
|
|
|
168
171
|
@imap.disconnect
|
|
169
172
|
end
|
|
170
173
|
|
|
171
|
-
private :connect, :reconnect, :
|
|
174
|
+
private :connect, :reconnect, :sleep_until_change, :run, :error, :notify_message
|
|
172
175
|
|
|
173
176
|
attr_writer :host, :password, :id, :user, :no_ssl, :cert_file, :mailbox
|
|
174
177
|
attr_accessor :work_account
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
require 'net/imap'
|
|
2
2
|
|
|
3
|
+
# This patch is needed because microsoft exchange servers do not correctly implement
|
|
4
|
+
# the IMAP specification. Specifially Net::IMAP#status will not work without it.
|
|
5
|
+
#
|
|
3
6
|
# source:
|
|
4
7
|
# http://claudiofloreani.blogspot.co.uk/2012/01/monkeypatching-ruby-imap-class-to-build.html
|
|
5
8
|
# thank you!
|
data/lib/catfriend/thread.rb
CHANGED
metadata
CHANGED
|
@@ -1,55 +1,69 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: catfriend
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: '0.
|
|
4
|
+
version: '0.18'
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- James Pike
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2014-03-
|
|
11
|
+
date: 2014-03-11 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: libnotify
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
|
-
- -
|
|
17
|
+
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
19
|
version: 0.7.1
|
|
20
|
-
- - ~>
|
|
20
|
+
- - "~>"
|
|
21
21
|
- !ruby/object:Gem::Version
|
|
22
22
|
version: '0.8'
|
|
23
23
|
type: :runtime
|
|
24
24
|
prerelease: false
|
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
|
26
26
|
requirements:
|
|
27
|
-
- -
|
|
27
|
+
- - ">="
|
|
28
28
|
- !ruby/object:Gem::Version
|
|
29
29
|
version: 0.7.1
|
|
30
|
-
- - ~>
|
|
30
|
+
- - "~>"
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
32
|
version: '0.8'
|
|
33
33
|
- !ruby/object:Gem::Dependency
|
|
34
34
|
name: ruby-dbus
|
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
|
-
- -
|
|
37
|
+
- - ">="
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
39
|
version: '0.7'
|
|
40
|
-
- - ~>
|
|
40
|
+
- - "~>"
|
|
41
41
|
- !ruby/object:Gem::Version
|
|
42
42
|
version: '0.11'
|
|
43
43
|
type: :runtime
|
|
44
44
|
prerelease: false
|
|
45
45
|
version_requirements: !ruby/object:Gem::Requirement
|
|
46
46
|
requirements:
|
|
47
|
-
- -
|
|
47
|
+
- - ">="
|
|
48
48
|
- !ruby/object:Gem::Version
|
|
49
49
|
version: '0.7'
|
|
50
|
-
- - ~>
|
|
50
|
+
- - "~>"
|
|
51
51
|
- !ruby/object:Gem::Version
|
|
52
52
|
version: '0.11'
|
|
53
|
+
- !ruby/object:Gem::Dependency
|
|
54
|
+
name: events
|
|
55
|
+
requirement: !ruby/object:Gem::Requirement
|
|
56
|
+
requirements:
|
|
57
|
+
- - "~>"
|
|
58
|
+
- !ruby/object:Gem::Version
|
|
59
|
+
version: '0.9'
|
|
60
|
+
type: :runtime
|
|
61
|
+
prerelease: false
|
|
62
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
63
|
+
requirements:
|
|
64
|
+
- - "~>"
|
|
65
|
+
- !ruby/object:Gem::Version
|
|
66
|
+
version: '0.9'
|
|
53
67
|
description: E-mail checker with libnotify desktop notifications.
|
|
54
68
|
email:
|
|
55
69
|
- catfriend@chilon.net
|
|
@@ -59,14 +73,14 @@ extensions: []
|
|
|
59
73
|
extra_rdoc_files: []
|
|
60
74
|
files:
|
|
61
75
|
- LICENSE
|
|
76
|
+
- bin/catfriend
|
|
62
77
|
- catfriend.example
|
|
63
|
-
- lib/catfriend/server.rb
|
|
64
78
|
- lib/catfriend/dbus.rb
|
|
65
|
-
- lib/catfriend/thread.rb
|
|
66
|
-
- lib/catfriend/net_imap_exchange_patch.rb
|
|
67
|
-
- lib/catfriend/imap.rb
|
|
68
79
|
- lib/catfriend/filetokenstack.rb
|
|
69
|
-
-
|
|
80
|
+
- lib/catfriend/imap.rb
|
|
81
|
+
- lib/catfriend/net_imap_exchange_patch.rb
|
|
82
|
+
- lib/catfriend/server.rb
|
|
83
|
+
- lib/catfriend/thread.rb
|
|
70
84
|
homepage: https://github.com/nuisanceofcats/catfriend
|
|
71
85
|
licenses:
|
|
72
86
|
- Expat
|
|
@@ -77,17 +91,17 @@ require_paths:
|
|
|
77
91
|
- lib
|
|
78
92
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
79
93
|
requirements:
|
|
80
|
-
- -
|
|
94
|
+
- - ">="
|
|
81
95
|
- !ruby/object:Gem::Version
|
|
82
96
|
version: '0'
|
|
83
97
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
84
98
|
requirements:
|
|
85
|
-
- -
|
|
99
|
+
- - ">="
|
|
86
100
|
- !ruby/object:Gem::Version
|
|
87
101
|
version: '1.3'
|
|
88
102
|
requirements: []
|
|
89
103
|
rubyforge_project:
|
|
90
|
-
rubygems_version: 2.
|
|
104
|
+
rubygems_version: 2.2.2
|
|
91
105
|
signing_key:
|
|
92
106
|
specification_version: 4
|
|
93
107
|
summary: E-mail checker with desktop notifications.
|