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.



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

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

Instance Attribute Details

#passObject

Returns the value of attribute pass.



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

def pass
  @pass
end

#portObject

Returns the value of attribute port.



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

def port
  @port
end

#responsesObject (readonly)

Returns the value of attribute responses.



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

def responses
  @responses
end

#userObject

Returns the value of attribute user.



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

def user
  @user
end

Instance Method Details

#capabilityObject

Returns the server’s capabilities.



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

def capability
  @capability ||= query_server { connection.capability }
end

#clear_logObject

Clears the server response log



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

def clear_log
  @log = []
end

#collect_statsObject

Collects stats for all mailboxes recursively.



164
165
166
# File 'lib/imapcli/client.rb', line 164

def collect_stats
  mailbox_root.collect_stats(self)
end

#connectionObject

Returns a connection to the server.

The value is cached.



74
75
76
# File 'lib/imapcli/client.rb', line 74

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.



185
186
187
# File 'lib/imapcli/client.rb', line 185

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’).



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

def greeting
  query_server { connection.greeting.data.text.strip }
end

#last_responseObject

Returns the last response from the server



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

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).



82
83
84
85
86
87
88
89
# File 'lib/imapcli/client.rb', line 82

def 
  raise('no connection to a server') unless connection
  begin
    response_ok? connection.(@user, @pass)
  rescue Net::IMAP::NoResponseError => error
    log_error error
  end
end

#logoutObject

Logs out of the server.



92
93
94
95
# File 'lib/imapcli/client.rb', line 92

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

#mailbox_rootObject

Returns a tree of Imapcli::Mailbox objects.

The value is cached.



178
179
180
# File 'lib/imapcli/client.rb', line 178

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.



171
172
173
# File 'lib/imapcli/client.rb', line 171

def mailboxes
  @mailboxes ||= query_server { @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.



148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/imapcli/client.rb', line 148

def message_sizes(mailbox)
  # Could use the EXAMINE command to get the number of messages in a mailbox,
  # but we need to retrieve an array of message indexes anyway (to compute
  # the total mailbox size), so we can save one roundtrip to the server.
  # query_server { connection.examine(mailbox) }
  # total = connection.responses['EXISTS'][0]
  # unseen = query_server { connection.search('UNSEEN') }.length
  messages = messages(mailbox)
  if messages.length > 0
    query_server { connection.fetch(messages, 'RFC822.SIZE').map { |f| f.attr['RFC822.SIZE'] } }
  else
    []
  end
end

#messages(mailbox) ⇒ Object

Returns an array of message indexes for a mailbox.

The value is currently NOT cached.



133
134
135
136
# File 'lib/imapcli/client.rb', line 133

def messages(mailbox)
  query_server { connection.examine(mailbox) }
  query_server { 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.



120
121
122
123
124
125
126
127
128
# File 'lib/imapcli/client.rb', line 120

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

#separatorObject

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



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

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

#serverObject

Attribute reader for the server domain name



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

def server
  @server
end

#server=(server_with_optional_port) ⇒ Object

Attribute writer for the server domain name; a port may be appended with a colon.

If no port is appended, the default port (993) will be used.



32
33
34
35
36
37
38
39
40
# File 'lib/imapcli/client.rb', line 32

def server=(server_with_optional_port)
  match = server_with_optional_port.match('^([^:]+):(\d+)$')
  if match
    @server = match[1]
    @port = match[2].to_i
  else
    @server = server_with_optional_port
  end
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)


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

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])$'
end

#supports_quotaObject

Returns true if the server supports the IMAP QUOTA extension.



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

def supports_quota
  capability.include? 'QUOTA'
end

#user_valid?Boolean

Perform very basic sanity check on user name

Returns:

  • (Boolean)


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

def user_valid?
  @user&.length > 0
end

#valid?Boolean

Returns true if both server and user name are valid.

Returns:

  • (Boolean)


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

def valid?
  server_valid? && user_valid?
end