Class: Imapcli::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/imapcli/client.rb

Overview

Wrapper for Net::IMAP

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(server_with_optional_port, user, pass) ⇒ Client

Initializs the Client class.

server_with_optional_port is the server’s domain name; the port may be added following a colon. Default port is 993. user is the user (account) name to log into the server. pass is the password to log into the server.



16
17
18
19
20
21
22
# File 'lib/imapcli/client.rb', line 16

def initialize(server_with_optional_port, user, pass)
  @port = 993 # default
  self.server = server_with_optional_port
  @user = user
  @pass = pass
  clear_responses
end

Instance Attribute Details

#passObject

rubocop:disable Metrics/ClassLength



8
9
10
# File 'lib/imapcli/client.rb', line 8

def pass
  @pass
end

#portObject

rubocop:disable Metrics/ClassLength



8
9
10
# File 'lib/imapcli/client.rb', line 8

def port
  @port
end

#serverObject

Attribute reader for the server domain name



25
26
27
# File 'lib/imapcli/client.rb', line 25

def server
  @server
end

#userObject

rubocop:disable Metrics/ClassLength



8
9
10
# File 'lib/imapcli/client.rb', line 8

def user
  @user
end

Instance Method Details

#capabilityObject

Returns the server’s capabilities.



109
110
111
# File 'lib/imapcli/client.rb', line 109

def capability
  @capability ||= query_server('capability') { connection.capability }
end

#clear_responsesObject

Clears the server response log



61
62
63
# File 'lib/imapcli/client.rb', line 61

def clear_responses
  @log = []
end

#collect_statsObject

Collects stats for all mailboxes recursively.



169
170
171
# File 'lib/imapcli/client.rb', line 169

def collect_stats
  mailbox_root.collect_stats(self)
end

#connectionObject

Returns a connection to the server.

The value is cached.



78
79
80
# File 'lib/imapcli/client.rb', line 78

def connection
  @connection ||= Net::IMAP.new(@server, @port, true)
end

#find_mailbox(mailbox) ⇒ Object

Attempts to locate a given mailbox in the mailbox_root.

Returns nil if the mailbox is not found.



190
191
192
# File 'lib/imapcli/client.rb', line 190

def find_mailbox(mailbox)
  mailbox_root.find_sub_mailbox(mailbox, separator)
end

#greetingObject

Returns the server’s greeting (which may reveal the server software name such as ‘Dovecot’).



104
105
106
# File 'lib/imapcli/client.rb', line 104

def greeting
  query_server('greeting') { connection.greeting.data.text.strip }
end

#last_responseObject

Returns the last response from the server



71
72
73
# File 'lib/imapcli/client.rb', line 71

def last_response
  @log.last
end

#loginObject

Logs in to the server.

Returns true if login was successful, false if not (e..g, invalid credentials).



86
87
88
89
90
91
92
93
94
# File 'lib/imapcli/client.rb', line 86

def 
  raise('no connection to a server') unless connection

  begin
    response_ok? connection.(@user, @pass)
  rescue Net::IMAP::NoResponseError => e
    log_error e
  end
end

#logoutObject

Logs out of the server.



97
98
99
100
# File 'lib/imapcli/client.rb', line 97

def logout
  # access instance variable to avoid creating a new connection
  @connection&.logout
end

#mailbox_rootObject

Returns a tree of Imapcli::Mailbox objects.

The value is cached.



183
184
185
# File 'lib/imapcli/client.rb', line 183

def mailbox_root
  @mailbox_root ||= Mailbox.new(mailboxes)
end

#mailboxesObject

Gets a list of Net::IMAP::MailboxList items, one for each mailbox.

The value is cached.



176
177
178
# File 'lib/imapcli/client.rb', line 176

def mailboxes
  @mailboxes ||= query_server('list') { @connection.list('', '*') }
end

#message_sizes(mailbox) ⇒ Object

Examines a mailbox and returns statistics about the messages in it.

Returns an array with the following keys:

  • :count: Total count of messages.

  • :size: Total size of all messages in bytes.

  • :min: Size of the smallest message.

  • :q1: First quartile of message sizes.

  • :median: Median of message sizes.

  • :q3: Third quartile of messages sizes.

  • :max: Size of largest message.



153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/imapcli/client.rb', line 153

def message_sizes(mailbox)
  messages = messages(mailbox)
  if messages.empty?
    []
  else
    query_server('fetch(...)') do
      messages.each_slice(1000).map do |some_messages|
        connection.fetch(some_messages, 'RFC822.SIZE').map do |f|
          f.attr['RFC822.SIZE']
        end
      end.flatten
    end
  end
end

#messages(mailbox) ⇒ Object

Returns an array of message indexes for a mailbox.

The value is currently NOT cached.



138
139
140
141
# File 'lib/imapcli/client.rb', line 138

def messages(mailbox)
  query_server("examine('#{mailbox}')") { connection.examine(mailbox) }
  query_server("search('ALL')") { connection.search('ALL') }
end

#quotaObject

If the server supports_quota, returns an array containing the current usage (in kiB), the total quota (in kiB), and the percent usage.



125
126
127
128
129
130
131
132
133
# File 'lib/imapcli/client.rb', line 125

def quota
  return unless supports_quota

  @quota ||= begin
    info = query_server("getquotaroot('INBOX')") { @connection.getquotaroot('INBOX')[1] }
    percent = info.quota.to_i.positive? ? info.usage.to_i.fdiv(info.quota.to_i) * 100 : nil
    [info.usage, info.quota, percent]
  end
end

#responsesObject

Returns the IMAP server response log



66
67
68
# File 'lib/imapcli/client.rb', line 66

def responses
  @log
end

#separatorObject

Returns the character that is used to separate nested mailbox names.



114
115
116
# File 'lib/imapcli/client.rb', line 114

def separator
  @separator ||= query_server("list('')") { connection.list('', '')[0].delim }
end

#server_valid?Boolean

Perform basic sanity check on server name

Note that a propery regex for an FQDN is hard to achieve. See stackoverflow.com/a/106223/270712 and elsewhere.

Returns:

  • (Boolean)


45
46
47
# File 'lib/imapcli/client.rb', line 45

def server_valid?
  @server.match? '^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$' # rubocop:disable Layout/LineLength
end

#supports_quotaObject

Returns true if the server supports the IMAP QUOTA extension.



119
120
121
# File 'lib/imapcli/client.rb', line 119

def supports_quota
  capability.include? 'QUOTA'
end

#user_valid?Boolean

Perform very basic sanity check on user name

Returns:

  • (Boolean)


51
52
53
# File 'lib/imapcli/client.rb', line 51

def user_valid?
  @user&.length&.> 0
end

#valid?Boolean

Returns true if both server and user name are valid.

Returns:

  • (Boolean)


56
57
58
# File 'lib/imapcli/client.rb', line 56

def valid?
  server_valid? && user_valid?
end