catfriend 0.16 → 0.17

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d3298ab27969cad7ca4f284d27fdfe153f83bbfb
4
+ data.tar.gz: dced1579477fff11e1acf0252908539893f6ca6f
5
+ SHA512:
6
+ metadata.gz: c16e9adb4de53431a43c83d1b681f1a9ed59e350f4f1885d9d13203b825185c644ea4177a790bfdb8bf39f7b3afd0c0ec43b574d3c7b2c1a56fde2a6c633da8c
7
+ data.tar.gz: 46f30291ee03d29ae6ecd795fc207f8b78c760dacc638c4b30e7a3161df86dd42399172201d9ef8baf676eb75473f5de0d1b30122aa280ecee09a28cb3fa64ee
data/bin/catfriend CHANGED
@@ -4,12 +4,14 @@
4
4
  # new e-mail arrives.
5
5
  #
6
6
  # Author:: James Pike (mailto:catfriend@chilon.net)
7
- # Copyright:: Copyright (c) 2011 James Pike
7
+ # Copyright:: Copyright (c) 2011-2014 James Pike
8
8
  # License:: MIT
9
+
10
+ $LOAD_PATH << 'lib' if File.exists? 'lib/catfriend'
11
+
9
12
  require 'catfriend/filetokenstack'
10
13
  require 'catfriend/imap'
11
14
  require 'catfriend/dbus'
12
- require 'net/imap'
13
15
  require 'optparse'
14
16
 
15
17
  module Catfriend
@@ -20,149 +22,149 @@ APP_NAME = "catfriend"
20
22
  stderr_bak = $stderr.dup
21
23
  $stderr.reopen '/dev/null', 'w'
22
24
  begin
23
- require 'xdg'
25
+ require 'xdg'
24
26
  rescue LoadError ; end
25
27
  $stderr = stderr_bak # restore stderr
26
28
 
27
29
  # Reads a simple configuration format and returns an array of servers.
28
30
  def self.parse_config
29
- # xdg is optional
30
- begin
31
- config_file = XDG['CONFIG'].find APP_NAME
32
- rescue NameError ; end
33
- config_file ||= "#{ENV['HOME']}/.config/#{APP_NAME}"
34
-
35
- # for location of certificate file
36
- Dir.chdir File.dirname(config_file)
37
-
38
- servers = []
39
- current = {}
40
- defaults = {}
41
-
42
- tokens = FileTokenStack.new config_file
43
- until tokens.empty?
44
- field = tokens.shift
45
-
46
- # obviously assigning it in a loop like this is slow but hey it's
47
- # only run-once config and ruby people say DRY a lot.
48
- shift_tokens = lambda do
49
- if tokens.empty?
50
- raise ConfigError, "field #{field} requires parameter"
51
- end
52
- return tokens.shift
53
- end
31
+ # xdg is optional
32
+ begin
33
+ config_file = XDG['CONFIG'].find APP_NAME
34
+ rescue NameError ; end
35
+ config_file ||= "#{ENV['HOME']}/.config/#{APP_NAME}"
36
+
37
+ # for location of certificate file
38
+ Dir.chdir File.dirname(config_file)
39
+
40
+ servers = []
41
+ current = {}
42
+ defaults = {}
43
+
44
+ tokens = FileTokenStack.new config_file
45
+ until tokens.empty?
46
+ field = tokens.shift
47
+
48
+ # obviously assigning it in a loop like this is slow but hey it's
49
+ # only run-once config and ruby people say DRY a lot.
50
+ shift_tokens = lambda do
51
+ if tokens.empty?
52
+ raise ConfigError, "field #{field} requires parameter"
53
+ end
54
+ return tokens.shift
55
+ end
54
56
 
55
- case field
56
- when "host","imap"
57
- # host is deprecated
58
- if not current.empty?
59
- servers << ImapServer.new(current)
60
- current = {}
61
- end
62
- current[:host] = shift_tokens.call
63
- when "notificationTimeout", "errorTimeout", "socketTimeout"
64
- # convert from camelCase to camel_case
65
- clean_field =
66
- field.gsub(/([a-z])([A-Z])/) { "#{$1}_#{$2.downcase}" }
67
- defaults[clean_field] = shift_tokens.call
68
- when "checkInterval"
69
- shift_tokens.call # deprecated, ignore parameter
70
- when "cert_file"
71
- cert_file = shift_tokens.call
72
- unless File.exists? cert_file
73
- raise ConfigError,
74
- "non-existant SSL certificate `#{cert_file}'" +
75
- ", search path: #{File.dirname(config_file)}/"
76
- end
77
- current[:cert_file] = cert_file
78
- when "mailbox", "id", "user", "password"
79
- current[field] = shift_tokens.call
80
- when "nossl"
81
- current[:no_ssl] = true
82
- when "work"
83
- current[:work_account] = true
84
- else
85
- raise ConfigError,
86
- "invalid config parameter '#{field}'"
87
- end
57
+ case field
58
+ when "host","imap"
59
+ # host is deprecated
60
+ if not current.empty?
61
+ servers << ImapServer.new(current)
62
+ current = {}
63
+ end
64
+ current[:host] = shift_tokens.call
65
+ when "notificationTimeout", "errorTimeout", "socketTimeout"
66
+ # convert from camelCase to camel_case
67
+ clean_field =
68
+ field.gsub(/([a-z])([A-Z])/) { "#{$1}_#{$2.downcase}" }
69
+ defaults[clean_field] = shift_tokens.call
70
+ when "checkInterval"
71
+ shift_tokens.call # deprecated, ignore parameter
72
+ when "cert_file"
73
+ cert_file = shift_tokens.call
74
+ unless File.exists? cert_file
75
+ raise ConfigError,
76
+ "non-existant SSL certificate `#{cert_file}'" +
77
+ ", search path: #{File.dirname(config_file)}/"
78
+ end
79
+ current[:cert_file] = cert_file
80
+ when "mailbox", "id", "user", "password"
81
+ current[field] = shift_tokens.call
82
+ when "nossl"
83
+ current[:no_ssl] = true
84
+ when "work"
85
+ current[:work_account] = true
86
+ else
87
+ raise ConfigError,
88
+ "invalid config parameter '#{field}'"
88
89
  end
90
+ end
89
91
 
90
- servers << ImapServer.new(current) unless current.empty?
91
- Catfriend.notification_timeout = (defaults["notification_timeout"] or 60).to_i
92
+ servers << ImapServer.new(current) unless current.empty?
93
+ Catfriend.notification_timeout = (defaults["notification_timeout"] or 60).to_i
92
94
 
93
- servers
95
+ servers
94
96
  end
95
97
 
96
98
  # Main interface to the application. Reads all servers from config then runs
97
99
  # each one in a thread. The program exits when all threads encounter an
98
100
  # unrecoverable error. Perhaps I should make it exit if any thread exits.
99
101
  def self.main args
100
- work_accounts = false
101
- foreground = false
102
- Catfriend.verbose = false
103
- done_action = false
104
- begin
105
- OptionParser.new do |opts|
106
- opts.banner = "usage: #{APP_NAME} [options]"
107
- opts.on("-f", "--foreground", "run in foreground") do
108
- foreground = true
109
- end
110
-
111
- opts.on("-w", "--work", "enable work accounts") do
112
- work_accounts = true
113
- end
114
- opts.on("-v", "--verbose", "verbose output to console") do
115
- Catfriend.verbose = true
116
- end
117
-
118
- opts.on("-s", "--stop", "shut down running server") do
119
- done_action = true
120
- dbus = DBus.new
121
- if dbus.send_shutdown
122
- puts "sent shutdown signal"
123
- else
124
- puts "could not send shutdown signal, no server running?"
125
- end
126
- end
127
- end.parse!
128
-
129
- return 0 if done_action
130
-
131
- Catfriend.verbose = false unless foreground
132
-
133
- servers = parse_config
134
- servers.reject! { |s| s.work_account } unless work_accounts
135
- raise ConfigError, "no servers to check" if servers.empty?
136
-
137
- if foreground
138
- main_loop servers
102
+ work_accounts = false
103
+ foreground = false
104
+ Catfriend.verbose = false
105
+ done_action = false
106
+ begin
107
+ OptionParser.new do |opts|
108
+ opts.banner = "usage: #{APP_NAME} [options]"
109
+ opts.on("-f", "--foreground", "run in foreground") do
110
+ foreground = true
111
+ end
112
+
113
+ opts.on("-w", "--work", "enable work accounts") do
114
+ work_accounts = true
115
+ end
116
+ opts.on("-v", "--verbose", "verbose output to console") do
117
+ Catfriend.verbose = true
118
+ end
119
+
120
+ opts.on("-s", "--stop", "shut down running server") do
121
+ done_action = true
122
+ dbus = DBus.new
123
+ if dbus.send_shutdown
124
+ puts "sent shutdown signal"
139
125
  else
140
- pid = fork do
141
- main_loop servers
142
- end
143
- Process.detach pid
126
+ puts "could not send shutdown signal, no server running?"
144
127
  end
145
- rescue ConfigError => e
146
- puts "misconfiguration: #{e.message}"
147
- rescue Interrupt
148
- servers.each { |s| s.kill }
149
- rescue => e
150
- puts "unknown error #{e.message}\n#{e.backtrace.join("\n")}"
128
+ end
129
+ end.parse!
130
+
131
+ return 0 if done_action
132
+
133
+ Catfriend.verbose = false unless foreground
134
+
135
+ servers = parse_config
136
+ servers.reject! { |s| s.work_account } unless work_accounts
137
+ raise ConfigError, "no servers to check" if servers.empty?
138
+
139
+ if foreground
140
+ main_loop servers
151
141
  else
152
- return 0
142
+ pid = fork do
143
+ main_loop servers
144
+ end
145
+ Process.detach pid
153
146
  end
154
-
155
- 1
147
+ rescue ConfigError => e
148
+ puts "misconfiguration: #{e.message}"
149
+ rescue Interrupt
150
+ servers.each { |s| s.kill }
151
+ rescue => e
152
+ puts "unknown error #{e.message}\n#{e.backtrace.join("\n")}"
153
+ else
154
+ return 0
155
+ end
156
+
157
+ 1
156
158
  end
157
159
 
158
160
  def self.main_loop servers
159
- dbus = DBus.new servers
160
- dbus.start
161
- servers.each { |s| s.start }
162
- servers.each { |s| s.join }
163
- dbus.join
161
+ dbus = DBus.new servers
162
+ dbus.start
163
+ servers.each { |s| s.start }
164
+ servers.each { |s| s.join }
165
+ dbus.join
164
166
  end
165
167
 
166
- end ########################### end module
168
+ end
167
169
 
168
170
  exit Catfriend.main ARGV
@@ -2,70 +2,70 @@ require 'catfriend/thread'
2
2
  require 'catfriend/server'
3
3
  require 'dbus'
4
4
 
5
- module Catfriend # {
5
+ module Catfriend
6
6
 
7
7
  SERVICE = "org.freedesktop.Catfriend"
8
8
  PATH = "/org/freedesktop/Catfriend"
9
9
  INTERFACE = "org.freedesktop.Catfriend.System"
10
10
 
11
11
  class DBus
12
- include Thread
12
+ include Thread
13
13
 
14
- class DBusObject < ::DBus::Object
15
- def initialize(main, servers)
16
- @main = main
17
- @servers = servers
18
- super PATH
19
- end
20
-
21
- dbus_interface INTERFACE do
22
- dbus_method :stop do
23
- Catfriend.whisper "received shutdown request"
24
- @main.quit # this must be run from within method handler
25
- @servers.each { |s| s.disconnect }
26
- end
27
- end
14
+ class DBusObject < ::DBus::Object
15
+ def initialize(main, servers)
16
+ @main = main
17
+ @servers = servers
18
+ super PATH
28
19
  end
29
20
 
30
- def initialize(servers = nil)
31
- @servers = servers
21
+ dbus_interface INTERFACE do
22
+ dbus_method :stop do
23
+ Catfriend.whisper "received shutdown request"
24
+ @main.quit # this must be run from within method handler
25
+ @servers.each { |s| s.disconnect }
26
+ end
32
27
  end
28
+ end
33
29
 
34
- def init
35
- @bus = ::DBus::SessionBus.instance unless @bus
36
- end
30
+ def initialize(servers = nil)
31
+ @servers = servers
32
+ end
37
33
 
38
- def send_shutdown
39
- init
40
- service = @bus.service(SERVICE)
41
- object = service.object(PATH)
42
- object.introspect
43
- object.default_iface = INTERFACE
44
- object.stop
45
- true
46
- rescue
47
- false
48
- end
34
+ def init
35
+ @bus = ::DBus::SessionBus.instance unless @bus
36
+ end
49
37
 
50
- def start_service
51
- object = DBusObject.new(@main, @servers)
52
- service = @bus.request_service(SERVICE)
53
- service.export object
54
- end
38
+ def send_shutdown
39
+ init
40
+ service = @bus.service(SERVICE)
41
+ object = service.object(PATH)
42
+ object.introspect
43
+ object.default_iface = INTERFACE
44
+ object.stop
45
+ true
46
+ rescue
47
+ false
48
+ end
55
49
 
56
- def run
57
- init
58
- if send_shutdown
59
- Catfriend.whisper "shut down existing catfriend"
60
- end
50
+ def start_service
51
+ object = DBusObject.new(@main, @servers)
52
+ service = @bus.request_service(SERVICE)
53
+ service.export object
54
+ end
61
55
 
62
- @main = ::DBus::Main.new
63
- start_service
64
- @main << @bus
65
- @main.run
66
- rescue => e
67
- puts "dbus unknown error #{e.message}\n#{e.backtrace.join("\n")}"
56
+ def run
57
+ init
58
+ if send_shutdown
59
+ Catfriend.whisper "shut down existing catfriend"
68
60
  end
61
+
62
+ @main = ::DBus::Main.new
63
+ start_service
64
+ @main << @bus
65
+ @main.run
66
+ rescue => e
67
+ puts "dbus unknown error #{e.message}\n#{e.backtrace.join("\n")}"
68
+ end
69
69
  end
70
70
 
71
- end # } end module
71
+ end
@@ -2,40 +2,40 @@ module Catfriend
2
2
 
3
3
  # Adapt a file to a stack of tokens.
4
4
  class FileTokenStack
5
- # Initialize the token stack with the path of the file, the given token
6
- # match and comment skipping regexs.
7
- def initialize file, token_match = /\S+/, comment_match = /#.*/
8
- @token_match = token_match
9
- @comment_match = comment_match
10
- @stream = File.new file, "r"
11
- @tokens = []
12
- get_next_tokens
13
- end
5
+ # Initialize the token stack with the path of the file, the given token
6
+ # match and comment skipping regexs.
7
+ def initialize file, token_match = /\S+/, comment_match = /#.*/
8
+ @token_match = token_match
9
+ @comment_match = comment_match
10
+ @stream = File.new file, "r"
11
+ @tokens = []
12
+ get_next_tokens
13
+ end
14
14
 
15
- def get_next_tokens
16
- # Never let the token stack get empty so that empty? can always
17
- # work ahead of time.. this wouldn't be good for a network stream as
18
- # it would block before delivering the last token.
19
- while @tokens.empty?
20
- @line = @stream.gets
21
- return unless @line
22
- @tokens = @line.sub(@comment_match, '').scan(@token_match) if @line
23
- end
15
+ def get_next_tokens
16
+ # Never let the token stack get empty so that empty? can always
17
+ # work ahead of time.. this wouldn't be good for a network stream as
18
+ # it would block before delivering the last token.
19
+ while @tokens.empty?
20
+ @line = @stream.gets
21
+ return unless @line
22
+ @tokens = @line.sub(@comment_match, '').scan(@token_match) if @line
24
23
  end
24
+ end
25
25
 
26
- # Shift the next token from the current stream position.
27
- def shift
28
- ret = @tokens.shift
29
- get_next_tokens
30
- ret
31
- end
26
+ # Shift the next token from the current stream position.
27
+ def shift
28
+ ret = @tokens.shift
29
+ get_next_tokens
30
+ ret
31
+ end
32
32
 
33
- # Report if any tokens remain
34
- def empty?
35
- @stream.eof and @tokens.empty?
36
- end
33
+ # Report if any tokens remain
34
+ def empty?
35
+ @stream.eof and @tokens.empty?
36
+ end
37
37
 
38
- private :get_next_tokens
38
+ private :get_next_tokens
39
39
  end
40
40
 
41
- end # end module
41
+ end
@@ -2,169 +2,176 @@ require 'libnotify'
2
2
  require 'catfriend/server'
3
3
  require 'catfriend/thread'
4
4
 
5
+ require 'net/imap'
6
+
5
7
  # unless I do this I get random errors from Libnotify on startup 90% of the
6
8
  # time... this could be a bug in autoload or ruby 1.9 rather than libnotify
7
9
  module Libnotify
8
- class API ; end
10
+ class API ; end
9
11
  end
10
12
 
11
13
  module Catfriend
12
14
 
15
+ require_relative 'net_imap_exchange_patch'
16
+
13
17
  # This class represents a thread capable of checking and creating
14
18
  # notifications for a single mailbox on a single IMAP server.
15
19
  class ImapServer
16
- include Thread
17
- include AccessorsFromHash
18
-
19
- # Create new IMAP server with optional full configuration hash.
20
- # If the hash is not supplied at construction a further call must be
21
- # made to #configure before #start is called to start the thread.
22
- def initialize(args = nil)
23
- configure args if args
20
+ include Thread
21
+ include AccessorsFromHash
22
+
23
+ # Create new IMAP server with optional full configuration hash.
24
+ # If the hash is not supplied at construction a further call must be
25
+ # made to #configure before #start is called to start the thread.
26
+ def initialize(args = nil)
27
+ configure args if args
28
+ end
29
+
30
+ # Configure all attributes based on hash then make sure this
31
+ # represents a total valid configuration.
32
+ def configure args
33
+ super args
34
+
35
+ if not @user
36
+ raise ConfigError, "imap user not set"
24
37
  end
25
-
26
- # Configure all attributes based on hash then make sure this
27
- # represents a total valid configuration.
28
- def configure args
29
- super args
30
-
31
- if not @user
32
- raise ConfigError, "imap user not set"
33
- end
34
- if not @host
35
- raise ConfigError, "imap host not set"
36
- end
37
- if not @password
38
- raise ConfigError, "imap password not set"
39
- end
38
+ if not @host
39
+ raise ConfigError, "imap host not set"
40
40
  end
41
-
42
- # The id is a token which represents this server when displaying
43
- # notifications and is set to the host unless over-ridden by the
44
- # configuration file
45
- def id ; @id || @host ; end
46
-
47
- # Raise an error related to this particular server.
48
- def error message
49
- # consider raising notification instead?
50
- puts "#{id}: #{message}"
41
+ if not @password
42
+ raise ConfigError, "imap password not set"
51
43
  end
52
-
53
- # ThreadMixin interface. This connects to the mailserver and then
54
- # runs #check_loop to do the e-mail checking if the connection
55
- # succeeds.
56
- def run
57
- begin
58
- @notification =
59
- Libnotify.new :body => nil,
60
- :timeout => Catfriend.notification_timeout
61
- @message_count = connect
62
- notify_message @message_count
63
- # :body => nil means summary only
64
- rescue OpenSSL::SSL::SSLError
65
- error "try providing ssl certificate"
66
- rescue Net::IMAP::NoResponseError
67
- error "no response to connect, try ssl"
68
- else
69
- loop {
70
- check_loop
71
- break if stopping?
72
- }
73
- end
44
+ end
45
+
46
+ # The id is a token which represents this server when displaying
47
+ # notifications and is set to the host unless over-ridden by the
48
+ # configuration file
49
+ def id ; @id || @host ; end
50
+
51
+ # Raise an error related to this particular server.
52
+ def error message
53
+ # consider raising notification instead?
54
+ puts "#{id}: #{message}"
55
+ end
56
+
57
+ # ThreadMixin interface. This connects to the mailserver and then
58
+ # runs #check_loop to do the e-mail checking if the connection
59
+ # succeeds.
60
+ def run
61
+ begin
62
+ @notification =
63
+ Libnotify.new :body => nil,
64
+ :timeout => Catfriend.notification_timeout
65
+ @message_count = connect
66
+ notify_message @message_count
67
+ # :body => nil means summary only
68
+ rescue OpenSSL::SSL::SSLError
69
+ error "try providing ssl certificate"
70
+ rescue Net::IMAP::NoResponseError
71
+ error "no response to connect, try ssl"
72
+ else
73
+ loop {
74
+ check_loop
75
+ break if stopping?
76
+ }
74
77
  end
75
-
76
- # Continually waits for new e-mail raising notifications when new
77
- # e-mail arrives or when error conditions happen. This methods only exits
78
- # on an unrecoverable error.
79
- def check_loop
80
- @imap.idle do |r|
81
- Catfriend.whisper "#{id}: #{r}"
82
- next if r.instance_of? Net::IMAP::ContinuationRequest
83
-
84
- if r.instance_of? Net::IMAP::UntaggedResponse
85
- case r.name
86
- when 'EXISTS'
87
- # some servers send this even when the message count
88
- # hasn't increased so suspiciously double-check
89
- if r.data != @message_count
90
- notify_message(r.data) if r.data > @message_count
91
- @message_count = r.data
92
- end
93
- when 'EXPUNGE'
94
- @message_count -= 1
95
- end
96
- end
97
- end
98
-
99
- Catfriend.whisper "idle loop over"
100
- rescue Net::IMAP::Error, IOError
101
- # reconnect and carry on
102
- reconnect unless stopping?
103
- rescue => e
104
- unless stopping?
105
- # todo: see if we have to re-open socket
106
- notify_message "#{@message_count} [error: #{e.message}]"
107
- puts e.backtrace.join "\n"
78
+ end
79
+
80
+ # Continually waits for new e-mail raising notifications when new
81
+ # e-mail arrives or when error conditions happen. This methods only exits
82
+ # on an unrecoverable error.
83
+ def check_loop
84
+ @imap.idle do |r|
85
+ Catfriend.whisper "#{id}: #{r}"
86
+ next if r.instance_of? Net::IMAP::ContinuationRequest
87
+
88
+ if r.instance_of? Net::IMAP::UntaggedResponse
89
+ case r.name
90
+ when 'EXISTS', 'EXPUNGE'
91
+ @imap.idle_done
108
92
  end
93
+ end
109
94
  end
110
95
 
111
- def notify_message message
112
- @notification.update :summary => "#{id}: #{message}"
113
- Catfriend.whisper @notification.summary
96
+ Catfriend.whisper "idle loop over"
97
+ count = get_unseen_count
98
+ if count != @message_count
99
+ notify_message(count) if count > @message_count
100
+ @message_count = count
114
101
  end
115
-
116
- def stopping?
117
- stopped? or @stopping
102
+ rescue Net::IMAP::Error, IOError
103
+ # reconnect and carry on
104
+ reconnect unless stopping?
105
+ rescue => e
106
+ unless stopping?
107
+ # todo: see if we have to re-open socket
108
+ notify_message "#{@message_count} [error: #{e.message}]"
109
+ puts e.backtrace.join "\n"
118
110
  end
119
-
120
- def kill
121
- disconnect
122
- super
111
+ end
112
+
113
+ def notify_message message
114
+ @notification.update :summary => "#{id}: #{message}"
115
+ Catfriend.whisper @notification.summary
116
+ end
117
+
118
+ def stopping?
119
+ stopped? or @stopping
120
+ end
121
+
122
+ def kill
123
+ disconnect
124
+ super
125
+ end
126
+
127
+ def get_unseen_count
128
+ begin
129
+ # fetch raises an exception when the mailbox is empty
130
+ @imap.status(@mailbox || "INBOX", ["UNSEEN"])["UNSEEN"]
131
+ rescue => e
132
+ error "failed to get count of unseen messages"
133
+ 0
123
134
  end
124
-
125
- # Connect to the configured IMAP server and return message count.
126
- def connect
127
- args = nil
128
- if not @no_ssl
129
- if @cert_file
130
- args = { :ssl => { :ca_file => @cert_file } }
131
- else
132
- args = { :ssl => true }
133
- end
134
- end
135
- @imap = Net::IMAP.new(@host, args)
136
- @imap.login(@user, @password)
137
- @imap.select(@mailbox || "INBOX")
138
-
139
- begin
140
- # fetch raises an exception when the mailbox is empty
141
- @imap.fetch('*', 'UID').first.seqno
142
- rescue
143
- 0
144
- end
135
+ end
136
+
137
+ # Connect to the configured IMAP server and return message count.
138
+ def connect
139
+ args = nil
140
+ if not @no_ssl
141
+ if @cert_file
142
+ args = { :ssl => { :ca_file => @cert_file } }
143
+ else
144
+ args = { :ssl => true }
145
+ end
145
146
  end
146
-
147
- def reconnect
148
- notify_message "#{@message_count} [reconnecting]"
149
- new_count = connect
150
- if new_count != @message_count
151
- notify_message new_count
152
- else
153
- # todo: only if it was still open
154
- @notification.close
155
- end
156
- @message_count = new_count
147
+ @imap = Net::IMAP.new(@host, args)
148
+ @imap.login(@user, @password)
149
+ @imap.select(@mailbox || "INBOX")
150
+
151
+ get_unseen_count
152
+ end
153
+
154
+ def reconnect
155
+ notify_message "#{@message_count} [reconnecting]"
156
+ new_count = connect
157
+ if new_count != @message_count
158
+ notify_message new_count
159
+ else
160
+ # todo: only if it was still open
161
+ @notification.close
157
162
  end
163
+ @message_count = new_count
164
+ end
158
165
 
159
- def disconnect
160
- @stopping = true
161
- @imap.disconnect
162
- end
166
+ def disconnect
167
+ @stopping = true
168
+ @imap.disconnect
169
+ end
163
170
 
164
- private :connect, :reconnect, :check_loop, :run, :error, :notify_message
171
+ private :connect, :reconnect, :check_loop, :run, :error, :notify_message
165
172
 
166
- attr_writer :host, :password, :id, :user, :no_ssl, :cert_file, :mailbox
167
- attr_accessor :work_account
173
+ attr_writer :host, :password, :id, :user, :no_ssl, :cert_file, :mailbox
174
+ attr_accessor :work_account
168
175
  end
169
176
 
170
- end # end module
177
+ end
@@ -0,0 +1,27 @@
1
+ require 'net/imap'
2
+
3
+ # source:
4
+ # http://claudiofloreani.blogspot.co.uk/2012/01/monkeypatching-ruby-imap-class-to-build.html
5
+ # thank you!
6
+
7
+ module Net
8
+ class IMAP
9
+ class ResponseParser
10
+ def response
11
+ token = lookahead
12
+ case token.symbol
13
+ when T_PLUS
14
+ result = continue_req
15
+ when T_STAR
16
+ result = response_untagged
17
+ else
18
+ result = response_tagged
19
+ end
20
+ match(T_SPACE) if lookahead.symbol == T_SPACE
21
+ match(T_CRLF)
22
+ match(T_EOF)
23
+ return result
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,25 +1,25 @@
1
1
  module Catfriend
2
2
 
3
3
  class << self
4
- attr_accessor :notification_timeout, :verbose
4
+ attr_accessor :notification_timeout, :verbose
5
5
 
6
- # puts something if -v was used
7
- def whisper *args
8
- puts *args if verbose
9
- end
6
+ # puts something if -v was used
7
+ def whisper *args
8
+ puts *args if verbose
9
+ end
10
10
  end
11
11
 
12
12
  # Mixin to provide #configure which allows all instance variables with write
13
13
  # accessors declared to be set from a hash.
14
14
  module AccessorsFromHash
15
- # Call this to tranfer the hash data to corresponding attributes. Any
16
- # hash keys that do not have a corresponding write accessor in the
17
- # class are silently ignored.
18
- def configure args
19
- args.each do |opt, val|
20
- instance_variable_set("@#{opt}", val) if respond_to? "#{opt}="
21
- end
15
+ # Call this to tranfer the hash data to corresponding attributes. Any
16
+ # hash keys that do not have a corresponding write accessor in the
17
+ # class are silently ignored.
18
+ def configure args
19
+ args.each do |opt, val|
20
+ instance_variable_set("@#{opt}", val) if respond_to? "#{opt}="
22
21
  end
22
+ end
23
23
  end
24
24
 
25
25
  # This class is used to signal the user made an error in their configuration.
@@ -2,28 +2,28 @@ module Catfriend
2
2
 
3
3
  # Mixin this module and define "run" for a simple runnable/joinable thread
4
4
  module Thread
5
- # Call to start a thread running via the start method.
6
- def start ; @thread = ::Thread.new { run } ; end
5
+ # Call to start a thread running via the start method.
6
+ def start ; @thread = ::Thread.new { run } ; end
7
7
 
8
- # Test whether thread is currently stopped or closing down.
9
- def stopped? ; @thread.nil? ; end
8
+ # Test whether thread is currently stopped or closing down.
9
+ def stopped? ; @thread.nil? ; end
10
10
 
11
- # Join thread if it has started.
12
- def join
13
- unless stopped?
14
- @thread.join
15
- @thread = nil
16
- end
11
+ # Join thread if it has started.
12
+ def join
13
+ unless stopped?
14
+ @thread.join
15
+ @thread = nil
17
16
  end
17
+ end
18
18
 
19
19
 
20
- # Kill thread if it has started.
21
- def kill
22
- unless stopped?
23
- @thread.kill
24
- @thread = nil
25
- end
20
+ # Kill thread if it has started.
21
+ def kill
22
+ unless stopped?
23
+ @thread.kill
24
+ @thread = nil
26
25
  end
26
+ end
27
27
  end
28
28
 
29
29
  end # end Catfriend module
metadata CHANGED
@@ -1,38 +1,55 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: catfriend
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.16'
5
- prerelease:
4
+ version: '0.17'
6
5
  platform: ruby
7
6
  authors:
8
7
  - James Pike
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-02-23 00:00:00.000000000 Z
11
+ date: 2014-03-09 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: libnotify
16
- requirement: &12342540 !ruby/object:Gem::Requirement
17
- none: false
15
+ requirement: !ruby/object:Gem::Requirement
18
16
  requirements:
19
- - - ! '>='
17
+ - - '>='
20
18
  - !ruby/object:Gem::Version
21
19
  version: 0.7.1
20
+ - - ~>
21
+ - !ruby/object:Gem::Version
22
+ version: '0.8'
22
23
  type: :runtime
23
24
  prerelease: false
24
- version_requirements: *12342540
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 0.7.1
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: '0.8'
25
33
  - !ruby/object:Gem::Dependency
26
34
  name: ruby-dbus
27
- requirement: &12340600 !ruby/object:Gem::Requirement
28
- none: false
35
+ requirement: !ruby/object:Gem::Requirement
29
36
  requirements:
30
- - - ! '>='
37
+ - - '>='
31
38
  - !ruby/object:Gem::Version
32
39
  version: '0.7'
40
+ - - ~>
41
+ - !ruby/object:Gem::Version
42
+ version: '0.11'
33
43
  type: :runtime
34
44
  prerelease: false
35
- version_requirements: *12340600
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - '>='
48
+ - !ruby/object:Gem::Version
49
+ version: '0.7'
50
+ - - ~>
51
+ - !ruby/object:Gem::Version
52
+ version: '0.11'
36
53
  description: E-mail checker with libnotify desktop notifications.
37
54
  email:
38
55
  - catfriend@chilon.net
@@ -43,36 +60,35 @@ extra_rdoc_files: []
43
60
  files:
44
61
  - LICENSE
45
62
  - catfriend.example
46
- - lib/catfriend/dbus.rb
47
63
  - lib/catfriend/server.rb
48
- - lib/catfriend/filetokenstack.rb
64
+ - lib/catfriend/dbus.rb
49
65
  - lib/catfriend/thread.rb
66
+ - lib/catfriend/net_imap_exchange_patch.rb
50
67
  - lib/catfriend/imap.rb
51
- - !binary |-
52
- YmluL2NhdGZyaWVuZA==
68
+ - lib/catfriend/filetokenstack.rb
69
+ - bin/catfriend
53
70
  homepage: https://github.com/nuisanceofcats/catfriend
54
71
  licenses:
55
72
  - Expat
73
+ metadata: {}
56
74
  post_install_message:
57
75
  rdoc_options: []
58
76
  require_paths:
59
77
  - lib
60
78
  required_ruby_version: !ruby/object:Gem::Requirement
61
- none: false
62
79
  requirements:
63
- - - ! '>='
80
+ - - '>='
64
81
  - !ruby/object:Gem::Version
65
82
  version: '0'
66
83
  required_rubygems_version: !ruby/object:Gem::Requirement
67
- none: false
68
84
  requirements:
69
- - - ! '>='
85
+ - - '>='
70
86
  - !ruby/object:Gem::Version
71
87
  version: '1.3'
72
88
  requirements: []
73
89
  rubyforge_project:
74
- rubygems_version: 1.8.11
90
+ rubygems_version: 2.0.14
75
91
  signing_key:
76
- specification_version: 3
92
+ specification_version: 4
77
93
  summary: E-mail checker with desktop notifications.
78
94
  test_files: []