rubysl-net-imap 1.0.0 → 2.0.1
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/.travis.yml +3 -2
- data/lib/rubysl/net/imap/imap.rb +767 -412
- data/lib/rubysl/net/imap/version.rb +1 -1
- data/rubysl-net-imap.gemspec +3 -1
- metadata +18 -17
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 3308e84d0e8024e4ece77eec112a7334dd2dbb2a
         | 
| 4 | 
            +
              data.tar.gz: 8b643041df74e584d02daff83af3890bd5c3b9cc
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 4e67f4cb84f0d12d8b957dde9604e0c5baea70d9da4249dfc29230b2e3deff38b77c38d1fd7366ed5d01c80835359ca14c6a0d3f7f8dc830c85ade0531b37873
         | 
| 7 | 
            +
              data.tar.gz: efac146ad3c2c8b579f2454f39b342aaa7130c83ef6da4e72473b1cd8dfb5bd3e483900eed0c5f549e0812dff90d86e47f1ef113cb98eb17f90e5a0314f3f267
         | 
    
        data/.travis.yml
    CHANGED
    
    
    
        data/lib/rubysl/net/imap/imap.rb
    CHANGED
    
    | @@ -9,13 +9,14 @@ | |
| 9 9 | 
             
            # Documentation: Shugo Maeda, with RDoc conversion and overview by William
         | 
| 10 10 | 
             
            # Webber.
         | 
| 11 11 | 
             
            #
         | 
| 12 | 
            -
            # See Net::IMAP for documentation. | 
| 12 | 
            +
            # See Net::IMAP for documentation.
         | 
| 13 13 | 
             
            #
         | 
| 14 14 |  | 
| 15 15 |  | 
| 16 16 | 
             
            require "socket"
         | 
| 17 17 | 
             
            require "monitor"
         | 
| 18 18 | 
             
            require "digest/md5"
         | 
| 19 | 
            +
            require "strscan"
         | 
| 19 20 | 
             
            begin
         | 
| 20 21 | 
             
              require "openssl"
         | 
| 21 22 | 
             
            rescue LoadError
         | 
| @@ -44,12 +45,12 @@ module Net | |
| 44 45 | 
             
              # read-only access) #examine().  Once the client has successfully
         | 
| 45 46 | 
             
              # selected a mailbox, they enter _selected_ state, and that
         | 
| 46 47 | 
             
              # mailbox becomes the _current_ mailbox, on which mail-item
         | 
| 47 | 
            -
              # related commands implicitly operate. | 
| 48 | 
            +
              # related commands implicitly operate.
         | 
| 48 49 | 
             
              #
         | 
| 49 50 | 
             
              # Messages have two sorts of identifiers: message sequence
         | 
| 50 | 
            -
              # numbers, and UIDs. | 
| 51 | 
            +
              # numbers, and UIDs.
         | 
| 51 52 | 
             
              #
         | 
| 52 | 
            -
              # Message sequence numbers number messages within a mail box | 
| 53 | 
            +
              # Message sequence numbers number messages within a mail box
         | 
| 53 54 | 
             
              # from 1 up to the number of items in the mail box.  If new
         | 
| 54 55 | 
             
              # message arrives during a session, it receives a sequence
         | 
| 55 56 | 
             
              # number equal to the new size of the mail box.  If messages
         | 
| @@ -57,7 +58,7 @@ module Net | |
| 57 58 | 
             
              # sequence numbers "shuffled down" to fill the gaps.
         | 
| 58 59 | 
             
              #
         | 
| 59 60 | 
             
              # UIDs, on the other hand, are permanently guaranteed not to
         | 
| 60 | 
            -
              # identify another message within the same mailbox, even if | 
| 61 | 
            +
              # identify another message within the same mailbox, even if
         | 
| 61 62 | 
             
              # the existing message is deleted.  UIDs are required to
         | 
| 62 63 | 
             
              # be assigned in ascending (but not necessarily sequential)
         | 
| 63 64 | 
             
              # order within a mailbox; this means that if a non-IMAP client
         | 
| @@ -90,11 +91,11 @@ module Net | |
| 90 91 | 
             
              #     imap.store(message_id, "+FLAGS", [:Deleted])
         | 
| 91 92 | 
             
              #   end
         | 
| 92 93 | 
             
              #   imap.expunge
         | 
| 93 | 
            -
              # | 
| 94 | 
            +
              #
         | 
| 94 95 | 
             
              # == Thread Safety
         | 
| 95 96 | 
             
              #
         | 
| 96 97 | 
             
              # Net::IMAP supports concurrent threads. For example,
         | 
| 97 | 
            -
              # | 
| 98 | 
            +
              #
         | 
| 98 99 | 
             
              #   imap = Net::IMAP.new("imap.foo.net", "imap2")
         | 
| 99 100 | 
             
              #   imap.authenticate("cram-md5", "bar", "password")
         | 
| 100 101 | 
             
              #   imap.select("inbox")
         | 
| @@ -102,7 +103,7 @@ module Net | |
| 102 103 | 
             
              #   search_result = imap.search(["BODY", "hello"])
         | 
| 103 104 | 
             
              #   fetch_result = fetch_thread.value
         | 
| 104 105 | 
             
              #   imap.disconnect
         | 
| 105 | 
            -
              # | 
| 106 | 
            +
              #
         | 
| 106 107 | 
             
              # This script invokes the FETCH command and the SEARCH command concurrently.
         | 
| 107 108 | 
             
              #
         | 
| 108 109 | 
             
              # == Errors
         | 
| @@ -112,9 +113,9 @@ module Net | |
| 112 113 | 
             
              #
         | 
| 113 114 | 
             
              # NO:: the attempted command could not be successfully completed.  For
         | 
| 114 115 | 
             
              #      instance, the username/password used for logging in are incorrect;
         | 
| 115 | 
            -
              #      the selected mailbox does not exists; etc. | 
| 116 | 
            +
              #      the selected mailbox does not exists; etc.
         | 
| 116 117 | 
             
              #
         | 
| 117 | 
            -
              # BAD:: the request from the client does not follow the server's | 
| 118 | 
            +
              # BAD:: the request from the client does not follow the server's
         | 
| 118 119 | 
             
              #       understanding of the IMAP protocol.  This includes attempting
         | 
| 119 120 | 
             
              #       commands from the wrong client state; for instance, attempting
         | 
| 120 121 | 
             
              #       to perform a SEARCH command without having SELECTed a current
         | 
| @@ -146,8 +147,8 @@ module Net | |
| 146 147 | 
             
              #
         | 
| 147 148 | 
             
              # Finally, a Net::IMAP::DataFormatError is thrown if low-level data
         | 
| 148 149 | 
             
              # is found to be in an incorrect format (for instance, when converting
         | 
| 149 | 
            -
              # between UTF-8 and UTF-16), and Net::IMAP::ResponseParseError is | 
| 150 | 
            -
              # thrown if a server response is non-parseable. | 
| 150 | 
            +
              # between UTF-8 and UTF-16), and Net::IMAP::ResponseParseError is
         | 
| 151 | 
            +
              # thrown if a server response is non-parseable.
         | 
| 151 152 | 
             
              #
         | 
| 152 153 | 
             
              #
         | 
| 153 154 | 
             
              # == References
         | 
| @@ -199,7 +200,7 @@ module Net | |
| 199 200 | 
             
              #
         | 
| 200 201 | 
             
              class IMAP
         | 
| 201 202 | 
             
                include MonitorMixin
         | 
| 202 | 
            -
                if defined?(OpenSSL)
         | 
| 203 | 
            +
                if defined?(OpenSSL::SSL)
         | 
| 203 204 | 
             
                  include OpenSSL
         | 
| 204 205 | 
             
                  include SSL
         | 
| 205 206 | 
             
                end
         | 
| @@ -269,12 +270,24 @@ module Net | |
| 269 270 | 
             
                  return @@debug = val
         | 
| 270 271 | 
             
                end
         | 
| 271 272 |  | 
| 273 | 
            +
                # Returns the max number of flags interned to symbols.
         | 
| 274 | 
            +
                def self.max_flag_count
         | 
| 275 | 
            +
                  return @@max_flag_count
         | 
| 276 | 
            +
                end
         | 
| 277 | 
            +
             | 
| 278 | 
            +
                # Sets the max number of flags interned to symbols.
         | 
| 279 | 
            +
                def self.max_flag_count=(count)
         | 
| 280 | 
            +
                  @@max_flag_count = count
         | 
| 281 | 
            +
                end
         | 
| 282 | 
            +
             | 
| 272 283 | 
             
                # Adds an authenticator for Net::IMAP#authenticate.  +auth_type+
         | 
| 273 284 | 
             
                # is the type of authentication this authenticator supports
         | 
| 274 285 | 
             
                # (for instance, "LOGIN").  The +authenticator+ is an object
         | 
| 275 286 | 
             
                # which defines a process() method to handle authentication with
         | 
| 276 | 
            -
                # the server.  See Net::IMAP::LoginAuthenticator | 
| 277 | 
            -
                # Net::IMAP::CramMD5Authenticator  | 
| 287 | 
            +
                # the server.  See Net::IMAP::LoginAuthenticator,
         | 
| 288 | 
            +
                # Net::IMAP::CramMD5Authenticator, and Net::IMAP::DigestMD5Authenticator
         | 
| 289 | 
            +
                # for examples.
         | 
| 290 | 
            +
                #
         | 
| 278 291 | 
             
                #
         | 
| 279 292 | 
             
                # If +auth_type+ refers to an existing authenticator, it will be
         | 
| 280 293 | 
             
                # replaced by the new one.
         | 
| @@ -282,17 +295,44 @@ module Net | |
| 282 295 | 
             
                  @@authenticators[auth_type] = authenticator
         | 
| 283 296 | 
             
                end
         | 
| 284 297 |  | 
| 298 | 
            +
                # The default port for IMAP connections, port 143
         | 
| 299 | 
            +
                def self.default_port
         | 
| 300 | 
            +
                  return PORT
         | 
| 301 | 
            +
                end
         | 
| 302 | 
            +
             | 
| 303 | 
            +
                # The default port for IMAPS connections, port 993
         | 
| 304 | 
            +
                def self.default_tls_port
         | 
| 305 | 
            +
                  return SSL_PORT
         | 
| 306 | 
            +
                end
         | 
| 307 | 
            +
             | 
| 308 | 
            +
                class << self
         | 
| 309 | 
            +
                  alias default_imap_port default_port
         | 
| 310 | 
            +
                  alias default_imaps_port default_tls_port
         | 
| 311 | 
            +
                  alias default_ssl_port default_tls_port
         | 
| 312 | 
            +
                end
         | 
| 313 | 
            +
             | 
| 285 314 | 
             
                # Disconnects from the server.
         | 
| 286 315 | 
             
                def disconnect
         | 
| 287 316 | 
             
                  begin
         | 
| 288 | 
            -
                     | 
| 289 | 
            -
             | 
| 290 | 
            -
             | 
| 291 | 
            -
                     | 
| 292 | 
            -
             | 
| 317 | 
            +
                    begin
         | 
| 318 | 
            +
                      # try to call SSL::SSLSocket#io.
         | 
| 319 | 
            +
                      @sock.io.shutdown
         | 
| 320 | 
            +
                    rescue NoMethodError
         | 
| 321 | 
            +
                      # @sock is not an SSL::SSLSocket.
         | 
| 322 | 
            +
                      @sock.shutdown
         | 
| 323 | 
            +
                    end
         | 
| 324 | 
            +
                  rescue Errno::ENOTCONN
         | 
| 325 | 
            +
                    # ignore `Errno::ENOTCONN: Socket is not connected' on some platforms.
         | 
| 326 | 
            +
                  rescue Exception => e
         | 
| 327 | 
            +
                    @receiver_thread.raise(e)
         | 
| 293 328 | 
             
                  end
         | 
| 294 329 | 
             
                  @receiver_thread.join
         | 
| 295 | 
            -
                   | 
| 330 | 
            +
                  synchronize do
         | 
| 331 | 
            +
                    unless @sock.closed?
         | 
| 332 | 
            +
                      @sock.close
         | 
| 333 | 
            +
                    end
         | 
| 334 | 
            +
                  end
         | 
| 335 | 
            +
                  raise e if e
         | 
| 296 336 | 
             
                end
         | 
| 297 337 |  | 
| 298 338 | 
             
                # Returns true if disconnected from the server.
         | 
| @@ -307,7 +347,7 @@ module Net | |
| 307 347 | 
             
                #
         | 
| 308 348 | 
             
                # Note that the Net::IMAP class does not modify its
         | 
| 309 349 | 
             
                # behaviour according to the capabilities of the server;
         | 
| 310 | 
            -
                # it is up to the user of the class to ensure that | 
| 350 | 
            +
                # it is up to the user of the class to ensure that
         | 
| 311 351 | 
             
                # a certain capability is supported by a server before
         | 
| 312 352 | 
             
                # using it.
         | 
| 313 353 | 
             
                def capability
         | 
| @@ -328,12 +368,27 @@ module Net | |
| 328 368 | 
             
                  send_command("LOGOUT")
         | 
| 329 369 | 
             
                end
         | 
| 330 370 |  | 
| 371 | 
            +
                # Sends a STARTTLS command to start TLS session.
         | 
| 372 | 
            +
                def starttls(options = {}, verify = true)
         | 
| 373 | 
            +
                  send_command("STARTTLS") do |resp|
         | 
| 374 | 
            +
                    if resp.kind_of?(TaggedResponse) && resp.name == "OK"
         | 
| 375 | 
            +
                      begin
         | 
| 376 | 
            +
                        # for backward compatibility
         | 
| 377 | 
            +
                        certs = options.to_str
         | 
| 378 | 
            +
                        options = create_ssl_params(certs, verify)
         | 
| 379 | 
            +
                      rescue NoMethodError
         | 
| 380 | 
            +
                      end
         | 
| 381 | 
            +
                      start_tls_session(options)
         | 
| 382 | 
            +
                    end
         | 
| 383 | 
            +
                  end
         | 
| 384 | 
            +
                end
         | 
| 385 | 
            +
             | 
| 331 386 | 
             
                # Sends an AUTHENTICATE command to authenticate the client.
         | 
| 332 387 | 
             
                # The +auth_type+ parameter is a string that represents
         | 
| 333 388 | 
             
                # the authentication mechanism to be used. Currently Net::IMAP
         | 
| 334 389 | 
             
                # supports authentication mechanisms:
         | 
| 335 390 | 
             
                #
         | 
| 336 | 
            -
                #   LOGIN:: login using cleartext user and password. | 
| 391 | 
            +
                #   LOGIN:: login using cleartext user and password.
         | 
| 337 392 | 
             
                #   CRAM-MD5:: login with cleartext user and encrypted password
         | 
| 338 393 | 
             
                #              (see [RFC-2195] for a full description).  This
         | 
| 339 394 | 
             
                #              mechanism requires that the server have the user's
         | 
| @@ -381,7 +436,7 @@ module Net | |
| 381 436 | 
             
                end
         | 
| 382 437 |  | 
| 383 438 | 
             
                # Sends a SELECT command to select a +mailbox+ so that messages
         | 
| 384 | 
            -
                # in the +mailbox+ can be accessed. | 
| 439 | 
            +
                # in the +mailbox+ can be accessed.
         | 
| 385 440 | 
             
                #
         | 
| 386 441 | 
             
                # After you have selected a mailbox, you may retrieve the
         | 
| 387 442 | 
             
                # number of items in that mailbox from @responses["EXISTS"][-1],
         | 
| @@ -432,7 +487,7 @@ module Net | |
| 432 487 | 
             
                # Sends a RENAME command to change the name of the +mailbox+ to
         | 
| 433 488 | 
             
                # +newname+.
         | 
| 434 489 | 
             
                #
         | 
| 435 | 
            -
                # A Net::IMAP::NoResponseError is raised if a mailbox with the | 
| 490 | 
            +
                # A Net::IMAP::NoResponseError is raised if a mailbox with the
         | 
| 436 491 | 
             
                # name +mailbox+ cannot be renamed to +newname+ for whatever
         | 
| 437 492 | 
             
                # reason; for instance, because +mailbox+ does not exist, or
         | 
| 438 493 | 
             
                # because there is already a mailbox with the name +newname+.
         | 
| @@ -479,8 +534,8 @@ module Net | |
| 479 534 | 
             
                #   imap.create("foo/bar")
         | 
| 480 535 | 
             
                #   imap.create("foo/baz")
         | 
| 481 536 | 
             
                #   p imap.list("", "foo/%")
         | 
| 482 | 
            -
                #   #=> [#<Net::IMAP::MailboxList attr=[:Noselect], delim="/", name="foo/">, \\ | 
| 483 | 
            -
                #        #<Net::IMAP::MailboxList attr=[:Noinferiors, :Marked], delim="/", name="foo/bar">, \\ | 
| 537 | 
            +
                #   #=> [#<Net::IMAP::MailboxList attr=[:Noselect], delim="/", name="foo/">, \\
         | 
| 538 | 
            +
                #        #<Net::IMAP::MailboxList attr=[:Noinferiors, :Marked], delim="/", name="foo/bar">, \\
         | 
| 484 539 | 
             
                #        #<Net::IMAP::MailboxList attr=[:Noinferiors], delim="/", name="foo/baz">]
         | 
| 485 540 | 
             
                def list(refname, mailbox)
         | 
| 486 541 | 
             
                  synchronize do
         | 
| @@ -489,6 +544,38 @@ module Net | |
| 489 544 | 
             
                  end
         | 
| 490 545 | 
             
                end
         | 
| 491 546 |  | 
| 547 | 
            +
                # Sends a XLIST command, and returns a subset of names from
         | 
| 548 | 
            +
                # the complete set of all names available to the client.
         | 
| 549 | 
            +
                # +refname+ provides a context (for instance, a base directory
         | 
| 550 | 
            +
                # in a directory-based mailbox hierarchy).  +mailbox+ specifies
         | 
| 551 | 
            +
                # a mailbox or (via wildcards) mailboxes under that context.
         | 
| 552 | 
            +
                # Two wildcards may be used in +mailbox+: '*', which matches
         | 
| 553 | 
            +
                # all characters *including* the hierarchy delimiter (for instance,
         | 
| 554 | 
            +
                # '/' on a UNIX-hosted directory-based mailbox hierarchy); and '%',
         | 
| 555 | 
            +
                # which matches all characters *except* the hierarchy delimiter.
         | 
| 556 | 
            +
                #
         | 
| 557 | 
            +
                # If +refname+ is empty, +mailbox+ is used directly to determine
         | 
| 558 | 
            +
                # which mailboxes to match.  If +mailbox+ is empty, the root
         | 
| 559 | 
            +
                # name of +refname+ and the hierarchy delimiter are returned.
         | 
| 560 | 
            +
                #
         | 
| 561 | 
            +
                # The XLIST command is like the LIST command except that the flags
         | 
| 562 | 
            +
                # returned refer to the function of the folder/mailbox, e.g. :Sent
         | 
| 563 | 
            +
                #
         | 
| 564 | 
            +
                # The return value is an array of +Net::IMAP::MailboxList+. For example:
         | 
| 565 | 
            +
                #
         | 
| 566 | 
            +
                #   imap.create("foo/bar")
         | 
| 567 | 
            +
                #   imap.create("foo/baz")
         | 
| 568 | 
            +
                #   p imap.xlist("", "foo/%")
         | 
| 569 | 
            +
                #   #=> [#<Net::IMAP::MailboxList attr=[:Noselect], delim="/", name="foo/">, \\
         | 
| 570 | 
            +
                #        #<Net::IMAP::MailboxList attr=[:Noinferiors, :Marked], delim="/", name="foo/bar">, \\
         | 
| 571 | 
            +
                #        #<Net::IMAP::MailboxList attr=[:Noinferiors], delim="/", name="foo/baz">]
         | 
| 572 | 
            +
                def xlist(refname, mailbox)
         | 
| 573 | 
            +
                  synchronize do
         | 
| 574 | 
            +
                    send_command("XLIST", refname, mailbox)
         | 
| 575 | 
            +
                    return @responses.delete("XLIST")
         | 
| 576 | 
            +
                  end
         | 
| 577 | 
            +
                end
         | 
| 578 | 
            +
             | 
| 492 579 | 
             
                # Sends the GETQUOTAROOT command along with specified +mailbox+.
         | 
| 493 580 | 
             
                # This command is generally available to both admin and user.
         | 
| 494 581 | 
             
                # If mailbox exists, returns an array containing objects of
         | 
| @@ -533,7 +620,7 @@ module Net | |
| 533 620 | 
             
                # then that user will be stripped of any rights to that mailbox.
         | 
| 534 621 | 
             
                # The IMAP ACL commands are described in [RFC-2086].
         | 
| 535 622 | 
             
                def setacl(mailbox, user, rights)
         | 
| 536 | 
            -
                  if rights.nil? | 
| 623 | 
            +
                  if rights.nil?
         | 
| 537 624 | 
             
                    send_command("SETACL", mailbox, user, "")
         | 
| 538 625 | 
             
                  else
         | 
| 539 626 | 
             
                    send_command("SETACL", mailbox, user, rights)
         | 
| @@ -552,7 +639,7 @@ module Net | |
| 552 639 |  | 
| 553 640 | 
             
                # Sends a LSUB command, and returns a subset of names from the set
         | 
| 554 641 | 
             
                # of names that the user has declared as being "active" or
         | 
| 555 | 
            -
                # "subscribed".  +refname+ and +mailbox+ are interpreted as | 
| 642 | 
            +
                # "subscribed".  +refname+ and +mailbox+ are interpreted as
         | 
| 556 643 | 
             
                # for #list().
         | 
| 557 644 | 
             
                # The return value is an array of +Net::IMAP::MailboxList+.
         | 
| 558 645 | 
             
                def lsub(refname, mailbox)
         | 
| @@ -575,7 +662,7 @@ module Net | |
| 575 662 | 
             
                #   p imap.status("inbox", ["MESSAGES", "RECENT"])
         | 
| 576 663 | 
             
                #   #=> {"RECENT"=>0, "MESSAGES"=>44}
         | 
| 577 664 | 
             
                #
         | 
| 578 | 
            -
                # A Net::IMAP::NoResponseError is raised if status values | 
| 665 | 
            +
                # A Net::IMAP::NoResponseError is raised if status values
         | 
| 579 666 | 
             
                # for +mailbox+ cannot be returned, for instance because it
         | 
| 580 667 | 
             
                # does not exist.
         | 
| 581 668 | 
             
                def status(mailbox, attr)
         | 
| @@ -586,9 +673,9 @@ module Net | |
| 586 673 | 
             
                end
         | 
| 587 674 |  | 
| 588 675 | 
             
                # Sends a APPEND command to append the +message+ to the end of
         | 
| 589 | 
            -
                # the +mailbox+. The optional +flags+ argument is an array of | 
| 676 | 
            +
                # the +mailbox+. The optional +flags+ argument is an array of
         | 
| 590 677 | 
             
                # flags to initially passing to the new message.  The optional
         | 
| 591 | 
            -
                # +date_time+ argument specifies the creation time to assign to the | 
| 678 | 
            +
                # +date_time+ argument specifies the creation time to assign to the
         | 
| 592 679 | 
             
                # new message; it defaults to the current time.
         | 
| 593 680 | 
             
                # For example:
         | 
| 594 681 | 
             
                #
         | 
| @@ -596,7 +683,7 @@ module Net | |
| 596 683 | 
             
                #   Subject: hello
         | 
| 597 684 | 
             
                #   From: shugo@ruby-lang.org
         | 
| 598 685 | 
             
                #   To: shugo@ruby-lang.org
         | 
| 599 | 
            -
                # | 
| 686 | 
            +
                #
         | 
| 600 687 | 
             
                #   hello world
         | 
| 601 688 | 
             
                #   EOF
         | 
| 602 689 | 
             
                #
         | 
| @@ -615,7 +702,7 @@ module Net | |
| 615 702 |  | 
| 616 703 | 
             
                # Sends a CHECK command to request a checkpoint of the currently
         | 
| 617 704 | 
             
                # selected mailbox.  This performs implementation-specific
         | 
| 618 | 
            -
                # housekeeping, for instance, reconciling the mailbox's | 
| 705 | 
            +
                # housekeeping, for instance, reconciling the mailbox's
         | 
| 619 706 | 
             
                # in-memory and on-disk state.
         | 
| 620 707 | 
             
                def check
         | 
| 621 708 | 
             
                  send_command("CHECK")
         | 
| @@ -639,8 +726,8 @@ module Net | |
| 639 726 |  | 
| 640 727 | 
             
                # Sends a SEARCH command to search the mailbox for messages that
         | 
| 641 728 | 
             
                # match the given searching criteria, and returns message sequence
         | 
| 642 | 
            -
                # numbers.  +keys+ can either be a string holding the entire | 
| 643 | 
            -
                # search string, or a single-dimension array of search keywords and | 
| 729 | 
            +
                # numbers.  +keys+ can either be a string holding the entire
         | 
| 730 | 
            +
                # search string, or a single-dimension array of search keywords and
         | 
| 644 731 | 
             
                # arguments.  The following are some common search criteria;
         | 
| 645 732 | 
             
                # see [IMAP] section 6.4.4 for a full list.
         | 
| 646 733 | 
             
                #
         | 
| @@ -664,7 +751,7 @@ module Net | |
| 664 751 | 
             
                #
         | 
| 665 752 | 
             
                # OR <search-key> <search-key>:: "or" two search keys together.
         | 
| 666 753 | 
             
                #
         | 
| 667 | 
            -
                # ON <date>:: messages with an internal date exactly equal to <date>, | 
| 754 | 
            +
                # ON <date>:: messages with an internal date exactly equal to <date>,
         | 
| 668 755 | 
             
                #             which has a format similar to 8-Aug-2002.
         | 
| 669 756 | 
             
                #
         | 
| 670 757 | 
             
                # SINCE <date>:: messages with an internal date on or after <date>.
         | 
| @@ -672,7 +759,7 @@ module Net | |
| 672 759 | 
             
                # SUBJECT <string>:: messages with <string> in their subject.
         | 
| 673 760 | 
             
                #
         | 
| 674 761 | 
             
                # TO <string>:: messages with <string> in their TO field.
         | 
| 675 | 
            -
                # | 
| 762 | 
            +
                #
         | 
| 676 763 | 
             
                # For example:
         | 
| 677 764 | 
             
                #
         | 
| 678 765 | 
             
                #   p imap.search(["SUBJECT", "hello", "NOT", "NEW"])
         | 
| @@ -695,8 +782,8 @@ module Net | |
| 695 782 | 
             
                # The return value is an array of Net::IMAP::FetchData. For example:
         | 
| 696 783 | 
             
                #
         | 
| 697 784 | 
             
                #   p imap.fetch(6..8, "UID")
         | 
| 698 | 
            -
                #   #=> [#<Net::IMAP::FetchData seqno=6, attr={"UID"=>98}>, \\ | 
| 699 | 
            -
                #        #<Net::IMAP::FetchData seqno=7, attr={"UID"=>99}>, \\ | 
| 785 | 
            +
                #   #=> [#<Net::IMAP::FetchData seqno=6, attr={"UID"=>98}>, \\
         | 
| 786 | 
            +
                #        #<Net::IMAP::FetchData seqno=7, attr={"UID"=>99}>, \\
         | 
| 700 787 | 
             
                #        #<Net::IMAP::FetchData seqno=8, attr={"UID"=>100}>]
         | 
| 701 788 | 
             
                #   p imap.fetch(6, "BODY[HEADER.FIELDS (SUBJECT)]")
         | 
| 702 789 | 
             
                #   #=> [#<Net::IMAP::FetchData seqno=6, attr={"BODY[HEADER.FIELDS (SUBJECT)]"=>"Subject: test\r\n\r\n"}>]
         | 
| @@ -719,9 +806,9 @@ module Net | |
| 719 806 | 
             
                end
         | 
| 720 807 |  | 
| 721 808 | 
             
                # Sends a STORE command to alter data associated with messages
         | 
| 722 | 
            -
                # in the mailbox, in particular their flags. The +set+ parameter | 
| 723 | 
            -
                # is a number or an array of numbers or a Range object. Each number | 
| 724 | 
            -
                # is a message sequence number.  +attr+ is the name of a data item | 
| 809 | 
            +
                # in the mailbox, in particular their flags. The +set+ parameter
         | 
| 810 | 
            +
                # is a number or an array of numbers or a Range object. Each number
         | 
| 811 | 
            +
                # is a message sequence number.  +attr+ is the name of a data item
         | 
| 725 812 | 
             
                # to store: 'FLAGS' means to replace the message's flag list
         | 
| 726 813 | 
             
                # with the provided one; '+FLAGS' means to add the provided flags;
         | 
| 727 814 | 
             
                # and '-FLAGS' means to remove them.  +flags+ is a list of flags.
         | 
| @@ -729,8 +816,8 @@ module Net | |
| 729 816 | 
             
                # The return value is an array of Net::IMAP::FetchData. For example:
         | 
| 730 817 | 
             
                #
         | 
| 731 818 | 
             
                #   p imap.store(6..8, "+FLAGS", [:Deleted])
         | 
| 732 | 
            -
                #   #=> [#<Net::IMAP::FetchData seqno=6, attr={"FLAGS"=>[:Seen, :Deleted]}>, \\ | 
| 733 | 
            -
                #        #<Net::IMAP::FetchData seqno=7, attr={"FLAGS"=>[:Seen, :Deleted]}>, \\ | 
| 819 | 
            +
                #   #=> [#<Net::IMAP::FetchData seqno=6, attr={"FLAGS"=>[:Seen, :Deleted]}>, \\
         | 
| 820 | 
            +
                #        #<Net::IMAP::FetchData seqno=7, attr={"FLAGS"=>[:Seen, :Deleted]}>, \\
         | 
| 734 821 | 
             
                #        #<Net::IMAP::FetchData seqno=8, attr={"FLAGS"=>[:Seen, :Deleted]}>]
         | 
| 735 822 | 
             
                def store(set, attr, flags)
         | 
| 736 823 | 
             
                  return store_internal("STORE", set, attr, flags)
         | 
| @@ -772,9 +859,9 @@ module Net | |
| 772 859 | 
             
                  return sort_internal("UID SORT", sort_keys, search_keys, charset)
         | 
| 773 860 | 
             
                end
         | 
| 774 861 |  | 
| 775 | 
            -
                # Adds a response handler. For example, to detect when | 
| 862 | 
            +
                # Adds a response handler. For example, to detect when
         | 
| 776 863 | 
             
                # the server sends us a new EXISTS response (which normally
         | 
| 777 | 
            -
                # indicates new messages being added to the mail box), | 
| 864 | 
            +
                # indicates new messages being added to the mail box),
         | 
| 778 865 | 
             
                # you could add the following handler after selecting the
         | 
| 779 866 | 
             
                # mailbox.
         | 
| 780 867 | 
             
                #
         | 
| @@ -810,12 +897,55 @@ module Net | |
| 810 897 | 
             
                  return thread_internal("THREAD", algorithm, search_keys, charset)
         | 
| 811 898 | 
             
                end
         | 
| 812 899 |  | 
| 813 | 
            -
                # As for #thread(), but returns unique identifiers instead of | 
| 900 | 
            +
                # As for #thread(), but returns unique identifiers instead of
         | 
| 814 901 | 
             
                # message sequence numbers.
         | 
| 815 902 | 
             
                def uid_thread(algorithm, search_keys, charset)
         | 
| 816 903 | 
             
                  return thread_internal("UID THREAD", algorithm, search_keys, charset)
         | 
| 817 904 | 
             
                end
         | 
| 818 905 |  | 
| 906 | 
            +
                # Sends an IDLE command that waits for notifications of new or expunged
         | 
| 907 | 
            +
                # messages.  Yields responses from the server during the IDLE.
         | 
| 908 | 
            +
                #
         | 
| 909 | 
            +
                # Use #idle_done() to leave IDLE.
         | 
| 910 | 
            +
                def idle(&response_handler)
         | 
| 911 | 
            +
                  raise LocalJumpError, "no block given" unless response_handler
         | 
| 912 | 
            +
             | 
| 913 | 
            +
                  response = nil
         | 
| 914 | 
            +
             | 
| 915 | 
            +
                  synchronize do
         | 
| 916 | 
            +
                    tag = Thread.current[:net_imap_tag] = generate_tag
         | 
| 917 | 
            +
                    put_string("#{tag} IDLE#{CRLF}")
         | 
| 918 | 
            +
             | 
| 919 | 
            +
                    begin
         | 
| 920 | 
            +
                      add_response_handler(response_handler)
         | 
| 921 | 
            +
                      @idle_done_cond = new_cond
         | 
| 922 | 
            +
                      @idle_done_cond.wait
         | 
| 923 | 
            +
                      @idle_done_cond = nil
         | 
| 924 | 
            +
                      if @receiver_thread_terminating
         | 
| 925 | 
            +
                        raise Net::IMAP::Error, "connection closed"
         | 
| 926 | 
            +
                      end
         | 
| 927 | 
            +
                    ensure
         | 
| 928 | 
            +
                      unless @receiver_thread_terminating
         | 
| 929 | 
            +
                        remove_response_handler(response_handler)
         | 
| 930 | 
            +
                        put_string("DONE#{CRLF}")
         | 
| 931 | 
            +
                        response = get_tagged_response(tag, "IDLE")
         | 
| 932 | 
            +
                      end
         | 
| 933 | 
            +
                    end
         | 
| 934 | 
            +
                  end
         | 
| 935 | 
            +
             | 
| 936 | 
            +
                  return response
         | 
| 937 | 
            +
                end
         | 
| 938 | 
            +
             | 
| 939 | 
            +
                # Leaves IDLE.
         | 
| 940 | 
            +
                def idle_done
         | 
| 941 | 
            +
                  synchronize do
         | 
| 942 | 
            +
                    if @idle_done_cond.nil?
         | 
| 943 | 
            +
                      raise Net::IMAP::Error, "not during IDLE"
         | 
| 944 | 
            +
                    end
         | 
| 945 | 
            +
                    @idle_done_cond.signal
         | 
| 946 | 
            +
                  end
         | 
| 947 | 
            +
                end
         | 
| 948 | 
            +
             | 
| 819 949 | 
             
                # Decode a string from modified UTF-7 format to UTF-8.
         | 
| 820 950 | 
             
                #
         | 
| 821 951 | 
             
                # UTF-7 is a 7-bit encoding of Unicode [UTF7].  IMAP uses a
         | 
| @@ -825,48 +955,64 @@ module Net | |
| 825 955 | 
             
                # Net::IMAP does _not_ automatically encode and decode
         | 
| 826 956 | 
             
                # mailbox names to and from utf7.
         | 
| 827 957 | 
             
                def self.decode_utf7(s)
         | 
| 828 | 
            -
                  return s.gsub(/&( | 
| 829 | 
            -
                    if $1 | 
| 830 | 
            -
                      " | 
| 958 | 
            +
                  return s.gsub(/&([^-]+)?-/n) {
         | 
| 959 | 
            +
                    if $1
         | 
| 960 | 
            +
                      ($1.tr(",", "/") + "===").unpack("m")[0].encode(Encoding::UTF_8, Encoding::UTF_16BE)
         | 
| 831 961 | 
             
                    else
         | 
| 832 | 
            -
                       | 
| 833 | 
            -
                      x = base64.length % 4
         | 
| 834 | 
            -
                      if x > 0
         | 
| 835 | 
            -
                        base64.concat("=" * (4 - x))
         | 
| 836 | 
            -
                      end
         | 
| 837 | 
            -
                      u16tou8(base64.unpack("m")[0])
         | 
| 962 | 
            +
                      "&"
         | 
| 838 963 | 
             
                    end
         | 
| 839 964 | 
             
                  }
         | 
| 840 965 | 
             
                end
         | 
| 841 966 |  | 
| 842 967 | 
             
                # Encode a string from UTF-8 format to modified UTF-7.
         | 
| 843 968 | 
             
                def self.encode_utf7(s)
         | 
| 844 | 
            -
                  return s.gsub(/(&)| | 
| 969 | 
            +
                  return s.gsub(/(&)|[^\x20-\x7e]+/) {
         | 
| 845 970 | 
             
                    if $1
         | 
| 846 971 | 
             
                      "&-"
         | 
| 847 972 | 
             
                    else
         | 
| 848 | 
            -
                      base64 = [ | 
| 973 | 
            +
                      base64 = [$&.encode(Encoding::UTF_16BE)].pack("m")
         | 
| 849 974 | 
             
                      "&" + base64.delete("=\n").tr("/", ",") + "-"
         | 
| 850 975 | 
             
                    end
         | 
| 851 | 
            -
                  }
         | 
| 976 | 
            +
                  }.force_encoding("ASCII-8BIT")
         | 
| 977 | 
            +
                end
         | 
| 978 | 
            +
             | 
| 979 | 
            +
                # Formats +time+ as an IMAP-style date.
         | 
| 980 | 
            +
                def self.format_date(time)
         | 
| 981 | 
            +
                  return time.strftime('%d-%b-%Y')
         | 
| 982 | 
            +
                end
         | 
| 983 | 
            +
             | 
| 984 | 
            +
                # Formats +time+ as an IMAP-style date-time.
         | 
| 985 | 
            +
                def self.format_datetime(time)
         | 
| 986 | 
            +
                  return time.strftime('%d-%b-%Y %H:%M %z')
         | 
| 852 987 | 
             
                end
         | 
| 853 988 |  | 
| 854 989 | 
             
                private
         | 
| 855 990 |  | 
| 856 991 | 
             
                CRLF = "\r\n"      # :nodoc:
         | 
| 857 992 | 
             
                PORT = 143         # :nodoc:
         | 
| 993 | 
            +
                SSL_PORT = 993   # :nodoc:
         | 
| 858 994 |  | 
| 859 995 | 
             
                @@debug = false
         | 
| 860 996 | 
             
                @@authenticators = {}
         | 
| 997 | 
            +
                @@max_flag_count = 10000
         | 
| 861 998 |  | 
| 999 | 
            +
                # :call-seq:
         | 
| 1000 | 
            +
                #    Net::IMAP.new(host, options = {})
         | 
| 1001 | 
            +
                #
         | 
| 862 1002 | 
             
                # Creates a new Net::IMAP object and connects it to the specified
         | 
| 863 | 
            -
                # + | 
| 864 | 
            -
                # | 
| 865 | 
            -
                #  | 
| 866 | 
            -
                # | 
| 867 | 
            -
                #  | 
| 868 | 
            -
                # | 
| 869 | 
            -
                #  | 
| 1003 | 
            +
                # +host+.
         | 
| 1004 | 
            +
                #
         | 
| 1005 | 
            +
                # +options+ is an option hash, each key of which is a symbol.
         | 
| 1006 | 
            +
                #
         | 
| 1007 | 
            +
                # The available options are:
         | 
| 1008 | 
            +
                #
         | 
| 1009 | 
            +
                # port::  port number (default value is 143 for imap, or 993 for imaps)
         | 
| 1010 | 
            +
                # ssl::   if options[:ssl] is true, then an attempt will be made
         | 
| 1011 | 
            +
                #         to use SSL (now TLS) to connect to the server.  For this to work
         | 
| 1012 | 
            +
                #         OpenSSL [OSSL] and the Ruby OpenSSL [RSSL] extensions need to
         | 
| 1013 | 
            +
                #         be installed.
         | 
| 1014 | 
            +
                #         if options[:ssl] is a hash, it's passed to
         | 
| 1015 | 
            +
                #         OpenSSL::SSL::SSLContext#set_params as parameters.
         | 
| 870 1016 | 
             
                #
         | 
| 871 1017 | 
             
                # The most common errors are:
         | 
| 872 1018 | 
             
                #
         | 
| @@ -876,60 +1022,66 @@ module Net | |
| 876 1022 | 
             
                #                    being dropped by an intervening firewall).
         | 
| 877 1023 | 
             
                # Errno::ENETUNREACH:: there is no route to that network.
         | 
| 878 1024 | 
             
                # SocketError:: hostname not known or other socket error.
         | 
| 879 | 
            -
                # Net::IMAP::ByeResponseError:: we connected to the host, but they | 
| 1025 | 
            +
                # Net::IMAP::ByeResponseError:: we connected to the host, but they
         | 
| 880 1026 | 
             
                #                               immediately said goodbye to us.
         | 
| 881 | 
            -
                def initialize(host,  | 
| 1027 | 
            +
                def initialize(host, port_or_options = {},
         | 
| 1028 | 
            +
                               usessl = false, certs = nil, verify = true)
         | 
| 882 1029 | 
             
                  super()
         | 
| 883 1030 | 
             
                  @host = host
         | 
| 884 | 
            -
                   | 
| 1031 | 
            +
                  begin
         | 
| 1032 | 
            +
                    options = port_or_options.to_hash
         | 
| 1033 | 
            +
                  rescue NoMethodError
         | 
| 1034 | 
            +
                    # for backward compatibility
         | 
| 1035 | 
            +
                    options = {}
         | 
| 1036 | 
            +
                    options[:port] = port_or_options
         | 
| 1037 | 
            +
                    if usessl
         | 
| 1038 | 
            +
                      options[:ssl] = create_ssl_params(certs, verify)
         | 
| 1039 | 
            +
                    end
         | 
| 1040 | 
            +
                  end
         | 
| 1041 | 
            +
                  @port = options[:port] || (options[:ssl] ? SSL_PORT : PORT)
         | 
| 885 1042 | 
             
                  @tag_prefix = "RUBY"
         | 
| 886 1043 | 
             
                  @tagno = 0
         | 
| 887 1044 | 
             
                  @parser = ResponseParser.new
         | 
| 888 | 
            -
                  @sock = TCPSocket.open(host, port)
         | 
| 889 | 
            -
                  if  | 
| 890 | 
            -
                     | 
| 891 | 
            -
                      raise "SSL extension not installed"
         | 
| 892 | 
            -
                    end
         | 
| 1045 | 
            +
                  @sock = TCPSocket.open(@host, @port)
         | 
| 1046 | 
            +
                  if options[:ssl]
         | 
| 1047 | 
            +
                    start_tls_session(options[:ssl])
         | 
| 893 1048 | 
             
                    @usessl = true
         | 
| 894 | 
            -
             | 
| 895 | 
            -
                    # verify the server.
         | 
| 896 | 
            -
                    context = SSLContext::new()
         | 
| 897 | 
            -
                    context.ca_file = certs if certs && FileTest::file?(certs)
         | 
| 898 | 
            -
                    context.ca_path = certs if certs && FileTest::directory?(certs)
         | 
| 899 | 
            -
                    context.verify_mode = VERIFY_PEER if verify
         | 
| 900 | 
            -
                    if defined?(VerifyCallbackProc)
         | 
| 901 | 
            -
                      context.verify_callback = VerifyCallbackProc 
         | 
| 902 | 
            -
                    end
         | 
| 903 | 
            -
                    @sock = SSLSocket.new(@sock, context)
         | 
| 904 | 
            -
                    @sock.sync_close = true
         | 
| 905 | 
            -
                    @sock.connect   # start ssl session.
         | 
| 906 | 
            -
                    @sock.post_connection_check(@host) if verify
         | 
| 907 1049 | 
             
                  else
         | 
| 908 1050 | 
             
                    @usessl = false
         | 
| 909 1051 | 
             
                  end
         | 
| 910 1052 | 
             
                  @responses = Hash.new([].freeze)
         | 
| 911 1053 | 
             
                  @tagged_responses = {}
         | 
| 912 1054 | 
             
                  @response_handlers = []
         | 
| 913 | 
            -
                  @ | 
| 914 | 
            -
                  @ | 
| 1055 | 
            +
                  @tagged_response_arrival = new_cond
         | 
| 1056 | 
            +
                  @continuation_request_arrival = new_cond
         | 
| 1057 | 
            +
                  @idle_done_cond = nil
         | 
| 915 1058 | 
             
                  @logout_command_tag = nil
         | 
| 916 1059 | 
             
                  @debug_output_bol = true
         | 
| 917 1060 | 
             
                  @exception = nil
         | 
| 918 1061 |  | 
| 919 1062 | 
             
                  @greeting = get_response
         | 
| 1063 | 
            +
                  if @greeting.nil?
         | 
| 1064 | 
            +
                    @sock.close
         | 
| 1065 | 
            +
                    raise Error, "connection closed"
         | 
| 1066 | 
            +
                  end
         | 
| 920 1067 | 
             
                  if @greeting.name == "BYE"
         | 
| 921 1068 | 
             
                    @sock.close
         | 
| 922 | 
            -
                    raise ByeResponseError, @greeting | 
| 1069 | 
            +
                    raise ByeResponseError, @greeting
         | 
| 923 1070 | 
             
                  end
         | 
| 924 1071 |  | 
| 925 1072 | 
             
                  @client_thread = Thread.current
         | 
| 926 1073 | 
             
                  @receiver_thread = Thread.start {
         | 
| 927 | 
            -
                     | 
| 1074 | 
            +
                    begin
         | 
| 1075 | 
            +
                      receive_responses
         | 
| 1076 | 
            +
                    rescue Exception
         | 
| 1077 | 
            +
                    end
         | 
| 928 1078 | 
             
                  }
         | 
| 1079 | 
            +
                  @receiver_thread_terminating = false
         | 
| 929 1080 | 
             
                end
         | 
| 930 1081 |  | 
| 931 1082 | 
             
                def receive_responses
         | 
| 932 | 
            -
                   | 
| 1083 | 
            +
                  connection_closed = false
         | 
| 1084 | 
            +
                  until connection_closed
         | 
| 933 1085 | 
             
                    synchronize do
         | 
| 934 1086 | 
             
                      @exception = nil
         | 
| 935 1087 | 
             
                    end
         | 
| @@ -937,7 +1089,7 @@ module Net | |
| 937 1089 | 
             
                      resp = get_response
         | 
| 938 1090 | 
             
                    rescue Exception => e
         | 
| 939 1091 | 
             
                      synchronize do
         | 
| 940 | 
            -
                        @sock.close | 
| 1092 | 
            +
                        @sock.close
         | 
| 941 1093 | 
             
                        @exception = e
         | 
| 942 1094 | 
             
                      end
         | 
| 943 1095 | 
             
                      break
         | 
| @@ -953,7 +1105,7 @@ module Net | |
| 953 1105 | 
             
                        case resp
         | 
| 954 1106 | 
             
                        when TaggedResponse
         | 
| 955 1107 | 
             
                          @tagged_responses[resp.tag] = resp
         | 
| 956 | 
            -
                          @ | 
| 1108 | 
            +
                          @tagged_response_arrival.broadcast
         | 
| 957 1109 | 
             
                          if resp.tag == @logout_command_tag
         | 
| 958 1110 | 
             
                            return
         | 
| 959 1111 | 
             
                          end
         | 
| @@ -965,13 +1117,11 @@ module Net | |
| 965 1117 | 
             
                          end
         | 
| 966 1118 | 
             
                          if resp.name == "BYE" && @logout_command_tag.nil?
         | 
| 967 1119 | 
             
                            @sock.close
         | 
| 968 | 
            -
                            @exception = ByeResponseError.new(resp | 
| 969 | 
            -
                             | 
| 970 | 
            -
                            return
         | 
| 1120 | 
            +
                            @exception = ByeResponseError.new(resp)
         | 
| 1121 | 
            +
                            connection_closed = true
         | 
| 971 1122 | 
             
                          end
         | 
| 972 1123 | 
             
                        when ContinuationRequest
         | 
| 973 | 
            -
                          @ | 
| 974 | 
            -
                          @response_arrival.broadcast
         | 
| 1124 | 
            +
                          @continuation_request_arrival.signal
         | 
| 975 1125 | 
             
                        end
         | 
| 976 1126 | 
             
                        @response_handlers.each do |handler|
         | 
| 977 1127 | 
             
                          handler.call(resp)
         | 
| @@ -980,30 +1130,32 @@ module Net | |
| 980 1130 | 
             
                    rescue Exception => e
         | 
| 981 1131 | 
             
                      @exception = e
         | 
| 982 1132 | 
             
                      synchronize do
         | 
| 983 | 
            -
                        @ | 
| 1133 | 
            +
                        @tagged_response_arrival.broadcast
         | 
| 1134 | 
            +
                        @continuation_request_arrival.broadcast
         | 
| 984 1135 | 
             
                      end
         | 
| 985 1136 | 
             
                    end
         | 
| 986 1137 | 
             
                  end
         | 
| 987 1138 | 
             
                  synchronize do
         | 
| 988 | 
            -
                    @ | 
| 1139 | 
            +
                    @receiver_thread_terminating = true
         | 
| 1140 | 
            +
                    @tagged_response_arrival.broadcast
         | 
| 1141 | 
            +
                    @continuation_request_arrival.broadcast
         | 
| 1142 | 
            +
                    if @idle_done_cond
         | 
| 1143 | 
            +
                      @idle_done_cond.signal
         | 
| 1144 | 
            +
                    end
         | 
| 989 1145 | 
             
                  end
         | 
| 990 1146 | 
             
                end
         | 
| 991 1147 |  | 
| 992 | 
            -
                def get_tagged_response(tag)
         | 
| 1148 | 
            +
                def get_tagged_response(tag, cmd)
         | 
| 993 1149 | 
             
                  until @tagged_responses.key?(tag)
         | 
| 994 1150 | 
             
                    raise @exception if @exception
         | 
| 995 | 
            -
                    @ | 
| 1151 | 
            +
                    @tagged_response_arrival.wait
         | 
| 996 1152 | 
             
                  end
         | 
| 997 | 
            -
                  return pick_up_tagged_response(tag)
         | 
| 998 | 
            -
                end
         | 
| 999 | 
            -
             | 
| 1000 | 
            -
                def pick_up_tagged_response(tag)
         | 
| 1001 1153 | 
             
                  resp = @tagged_responses.delete(tag)
         | 
| 1002 1154 | 
             
                  case resp.name
         | 
| 1003 1155 | 
             
                  when /\A(?:NO)\z/ni
         | 
| 1004 | 
            -
                    raise NoResponseError, resp | 
| 1156 | 
            +
                    raise NoResponseError, resp
         | 
| 1005 1157 | 
             
                  when /\A(?:BAD)\z/ni
         | 
| 1006 | 
            -
                    raise BadResponseError, resp | 
| 1158 | 
            +
                    raise BadResponseError, resp
         | 
| 1007 1159 | 
             
                  else
         | 
| 1008 1160 | 
             
                    return resp
         | 
| 1009 1161 | 
             
                  end
         | 
| @@ -1038,7 +1190,10 @@ module Net | |
| 1038 1190 |  | 
| 1039 1191 | 
             
                def send_command(cmd, *args, &block)
         | 
| 1040 1192 | 
             
                  synchronize do
         | 
| 1041 | 
            -
                     | 
| 1193 | 
            +
                    args.each do |i|
         | 
| 1194 | 
            +
                      validate_data(i)
         | 
| 1195 | 
            +
                    end
         | 
| 1196 | 
            +
                    tag = generate_tag
         | 
| 1042 1197 | 
             
                    put_string(tag + " " + cmd)
         | 
| 1043 1198 | 
             
                    args.each do |i|
         | 
| 1044 1199 | 
             
                      put_string(" ")
         | 
| @@ -1052,7 +1207,7 @@ module Net | |
| 1052 1207 | 
             
                      add_response_handler(block)
         | 
| 1053 1208 | 
             
                    end
         | 
| 1054 1209 | 
             
                    begin
         | 
| 1055 | 
            -
                      return get_tagged_response(tag)
         | 
| 1210 | 
            +
                      return get_tagged_response(tag, cmd)
         | 
| 1056 1211 | 
             
                    ensure
         | 
| 1057 1212 | 
             
                      if block
         | 
| 1058 1213 | 
             
                        remove_response_handler(block)
         | 
| @@ -1065,7 +1220,7 @@ module Net | |
| 1065 1220 | 
             
                  @tagno += 1
         | 
| 1066 1221 | 
             
                  return format("%s%04d", @tag_prefix, @tagno)
         | 
| 1067 1222 | 
             
                end
         | 
| 1068 | 
            -
             | 
| 1223 | 
            +
             | 
| 1069 1224 | 
             
                def put_string(str)
         | 
| 1070 1225 | 
             
                  @sock.print(str)
         | 
| 1071 1226 | 
             
                  if @@debug
         | 
| @@ -1081,6 +1236,25 @@ module Net | |
| 1081 1236 | 
             
                  end
         | 
| 1082 1237 | 
             
                end
         | 
| 1083 1238 |  | 
| 1239 | 
            +
                def validate_data(data)
         | 
| 1240 | 
            +
                  case data
         | 
| 1241 | 
            +
                  when nil
         | 
| 1242 | 
            +
                  when String
         | 
| 1243 | 
            +
                  when Integer
         | 
| 1244 | 
            +
                    if data < 0 || data >= 4294967296
         | 
| 1245 | 
            +
                      raise DataFormatError, num.to_s
         | 
| 1246 | 
            +
                    end
         | 
| 1247 | 
            +
                  when Array
         | 
| 1248 | 
            +
                    data.each do |i|
         | 
| 1249 | 
            +
                      validate_data(i)
         | 
| 1250 | 
            +
                    end
         | 
| 1251 | 
            +
                  when Time
         | 
| 1252 | 
            +
                  when Symbol
         | 
| 1253 | 
            +
                  else
         | 
| 1254 | 
            +
                    data.validate
         | 
| 1255 | 
            +
                  end
         | 
| 1256 | 
            +
                end
         | 
| 1257 | 
            +
             | 
| 1084 1258 | 
             
                def send_data(data)
         | 
| 1085 1259 | 
             
                  case data
         | 
| 1086 1260 | 
             
                  when nil
         | 
| @@ -1114,30 +1288,19 @@ module Net | |
| 1114 1288 | 
             
                    put_string(str)
         | 
| 1115 1289 | 
             
                  end
         | 
| 1116 1290 | 
             
                end
         | 
| 1117 | 
            -
             | 
| 1291 | 
            +
             | 
| 1118 1292 | 
             
                def send_quoted_string(str)
         | 
| 1119 1293 | 
             
                  put_string('"' + str.gsub(/["\\]/n, "\\\\\\&") + '"')
         | 
| 1120 1294 | 
             
                end
         | 
| 1121 1295 |  | 
| 1122 1296 | 
             
                def send_literal(str)
         | 
| 1123 | 
            -
                  put_string("{" + str. | 
| 1124 | 
            -
                   | 
| 1125 | 
            -
             | 
| 1126 | 
            -
                    @response_arrival.wait
         | 
| 1127 | 
            -
                    raise @exception if @exception
         | 
| 1128 | 
            -
                  end
         | 
| 1129 | 
            -
                  if @continuation_request.nil?
         | 
| 1130 | 
            -
                    pick_up_tagged_response(Thread.current[:net_imap_tag])
         | 
| 1131 | 
            -
                    raise ResponseError.new("expected continuation request")
         | 
| 1132 | 
            -
                  end
         | 
| 1133 | 
            -
                  @continuation_request = nil
         | 
| 1297 | 
            +
                  put_string("{" + str.bytesize.to_s + "}" + CRLF)
         | 
| 1298 | 
            +
                  @continuation_request_arrival.wait
         | 
| 1299 | 
            +
                  raise @exception if @exception
         | 
| 1134 1300 | 
             
                  put_string(str)
         | 
| 1135 1301 | 
             
                end
         | 
| 1136 1302 |  | 
| 1137 1303 | 
             
                def send_number_data(num)
         | 
| 1138 | 
            -
                  if num < 0 || num >= 4294967296
         | 
| 1139 | 
            -
                    raise DataFormatError, num.to_s
         | 
| 1140 | 
            -
                  end
         | 
| 1141 1304 | 
             
                  put_string(num.to_s)
         | 
| 1142 1305 | 
             
                end
         | 
| 1143 1306 |  | 
| @@ -1252,130 +1415,56 @@ module Net | |
| 1252 1415 | 
             
                  end
         | 
| 1253 1416 | 
             
                end
         | 
| 1254 1417 |  | 
| 1255 | 
            -
                def  | 
| 1256 | 
            -
                   | 
| 1257 | 
            -
                  if  | 
| 1258 | 
            -
                     | 
| 1259 | 
            -
             | 
| 1260 | 
            -
             | 
| 1261 | 
            -
             | 
| 1262 | 
            -
                  while i < len
         | 
| 1263 | 
            -
                    c = s[i] << 8 | s[i + 1]
         | 
| 1264 | 
            -
                    i += 2
         | 
| 1265 | 
            -
                    if c == 0xfeff
         | 
| 1266 | 
            -
                      next
         | 
| 1267 | 
            -
                    elsif c < 0x0080
         | 
| 1268 | 
            -
                      buf.concat(c)
         | 
| 1269 | 
            -
                    elsif c < 0x0800
         | 
| 1270 | 
            -
                      b2 = c & 0x003f
         | 
| 1271 | 
            -
                      b1 = c >> 6
         | 
| 1272 | 
            -
                      buf.concat(b1 | 0xc0)
         | 
| 1273 | 
            -
                      buf.concat(b2 | 0x80)
         | 
| 1274 | 
            -
                    elsif c >= 0xdc00 && c < 0xe000
         | 
| 1275 | 
            -
                      raise DataFormatError, "invalid surrogate detected"
         | 
| 1276 | 
            -
                    elsif c >= 0xd800 && c < 0xdc00
         | 
| 1277 | 
            -
                      if i + 2 > len
         | 
| 1278 | 
            -
                        raise DataFormatError, "invalid surrogate detected"
         | 
| 1279 | 
            -
                      end
         | 
| 1280 | 
            -
                      low = s[i] << 8 | s[i + 1]
         | 
| 1281 | 
            -
                      i += 2
         | 
| 1282 | 
            -
                      if low < 0xdc00 || low > 0xdfff
         | 
| 1283 | 
            -
                        raise DataFormatError, "invalid surrogate detected"
         | 
| 1284 | 
            -
                      end
         | 
| 1285 | 
            -
                      c = (((c & 0x03ff)) << 10 | (low & 0x03ff)) + 0x10000
         | 
| 1286 | 
            -
                      b4 = c & 0x003f
         | 
| 1287 | 
            -
                      b3 = (c >> 6) & 0x003f
         | 
| 1288 | 
            -
                      b2 = (c >> 12) & 0x003f
         | 
| 1289 | 
            -
                      b1 = c >> 18;
         | 
| 1290 | 
            -
                      buf.concat(b1 | 0xf0)
         | 
| 1291 | 
            -
                      buf.concat(b2 | 0x80)
         | 
| 1292 | 
            -
                      buf.concat(b3 | 0x80)
         | 
| 1293 | 
            -
                      buf.concat(b4 | 0x80)
         | 
| 1294 | 
            -
                    else # 0x0800-0xffff
         | 
| 1295 | 
            -
                      b3 = c & 0x003f
         | 
| 1296 | 
            -
                      b2 = (c >> 6) & 0x003f
         | 
| 1297 | 
            -
                      b1 = c >> 12
         | 
| 1298 | 
            -
                      buf.concat(b1 | 0xe0)
         | 
| 1299 | 
            -
                      buf.concat(b2 | 0x80)
         | 
| 1300 | 
            -
                      buf.concat(b3 | 0x80)
         | 
| 1301 | 
            -
                    end
         | 
| 1302 | 
            -
                  end
         | 
| 1303 | 
            -
                  return buf
         | 
| 1304 | 
            -
                end
         | 
| 1305 | 
            -
                private_class_method :u16tou8
         | 
| 1306 | 
            -
             | 
| 1307 | 
            -
                def self.u8tou16(s)
         | 
| 1308 | 
            -
                  len = s.length
         | 
| 1309 | 
            -
                  buf = ""
         | 
| 1310 | 
            -
                  i = 0
         | 
| 1311 | 
            -
                  while i < len
         | 
| 1312 | 
            -
                    c = s[i]
         | 
| 1313 | 
            -
                    if (c & 0x80) == 0
         | 
| 1314 | 
            -
                      buf.concat(0x00)
         | 
| 1315 | 
            -
                      buf.concat(c)
         | 
| 1316 | 
            -
                      i += 1
         | 
| 1317 | 
            -
                    elsif (c & 0xe0) == 0xc0 &&
         | 
| 1318 | 
            -
                        len >= 2 &&
         | 
| 1319 | 
            -
                        (s[i + 1] & 0xc0) == 0x80
         | 
| 1320 | 
            -
                      if c == 0xc0 || c == 0xc1
         | 
| 1321 | 
            -
                        raise DataFormatError, format("non-shortest UTF-8 sequence (%02x)", c)
         | 
| 1322 | 
            -
                      end
         | 
| 1323 | 
            -
                      u = ((c & 0x1f) << 6) | (s[i + 1] & 0x3f)
         | 
| 1324 | 
            -
                      buf.concat(u >> 8)
         | 
| 1325 | 
            -
                      buf.concat(u & 0x00ff)
         | 
| 1326 | 
            -
                      i += 2
         | 
| 1327 | 
            -
                    elsif (c & 0xf0) == 0xe0 &&
         | 
| 1328 | 
            -
                        i + 2 < len &&
         | 
| 1329 | 
            -
                        (s[i + 1] & 0xc0) == 0x80 &&
         | 
| 1330 | 
            -
                        (s[i + 2] & 0xc0) == 0x80
         | 
| 1331 | 
            -
                      if c == 0xe0 && s[i + 1] < 0xa0
         | 
| 1332 | 
            -
                        raise DataFormatError, format("non-shortest UTF-8 sequence (%02x)", c)
         | 
| 1333 | 
            -
                      end
         | 
| 1334 | 
            -
                      u = ((c & 0x0f) << 12) | ((s[i + 1] & 0x3f) << 6) | (s[i + 2] & 0x3f)
         | 
| 1335 | 
            -
                      # surrogate chars
         | 
| 1336 | 
            -
                      if u >= 0xd800 && u <= 0xdfff
         | 
| 1337 | 
            -
                        raise DataFormatError, format("none-UTF-16 char detected (%04x)", u)
         | 
| 1338 | 
            -
                      end
         | 
| 1339 | 
            -
                      buf.concat(u >> 8)
         | 
| 1340 | 
            -
                      buf.concat(u & 0x00ff)
         | 
| 1341 | 
            -
                      i += 3
         | 
| 1342 | 
            -
                    elsif (c & 0xf8) == 0xf0 &&
         | 
| 1343 | 
            -
                        i + 3 < len &&
         | 
| 1344 | 
            -
                        (s[i + 1] & 0xc0) == 0x80 &&
         | 
| 1345 | 
            -
                        (s[i + 2] & 0xc0) == 0x80 &&
         | 
| 1346 | 
            -
                        (s[i + 3] & 0xc0) == 0x80
         | 
| 1347 | 
            -
                      if c == 0xf0 && s[i + 1] < 0x90
         | 
| 1348 | 
            -
                        raise DataFormatError, format("non-shortest UTF-8 sequence (%02x)", c)
         | 
| 1349 | 
            -
                      end
         | 
| 1350 | 
            -
                      u = ((c & 0x07) << 18) | ((s[i + 1] & 0x3f) << 12) |
         | 
| 1351 | 
            -
                        ((s[i + 2] & 0x3f) << 6) | (s[i + 3] & 0x3f)
         | 
| 1352 | 
            -
                      if u < 0x10000
         | 
| 1353 | 
            -
                        buf.concat(u >> 8)
         | 
| 1354 | 
            -
                        buf.concat(u & 0x00ff)
         | 
| 1355 | 
            -
                      elsif u < 0x110000
         | 
| 1356 | 
            -
                        high = ((u - 0x10000) >> 10) | 0xd800
         | 
| 1357 | 
            -
                        low = (u & 0x03ff) | 0xdc00
         | 
| 1358 | 
            -
                        buf.concat(high >> 8)
         | 
| 1359 | 
            -
                        buf.concat(high & 0x00ff)
         | 
| 1360 | 
            -
                        buf.concat(low >> 8)
         | 
| 1361 | 
            -
                        buf.concat(low & 0x00ff)
         | 
| 1362 | 
            -
                      else
         | 
| 1363 | 
            -
                        raise DataFormatError, format("none-UTF-16 char detected (%04x)", u)
         | 
| 1364 | 
            -
                      end
         | 
| 1365 | 
            -
                      i += 4
         | 
| 1366 | 
            -
                    else
         | 
| 1367 | 
            -
                      raise DataFormatError, format("illegal UTF-8 sequence (%02x)", c)
         | 
| 1418 | 
            +
                def create_ssl_params(certs = nil, verify = true)
         | 
| 1419 | 
            +
                  params = {}
         | 
| 1420 | 
            +
                  if certs
         | 
| 1421 | 
            +
                    if File.file?(certs)
         | 
| 1422 | 
            +
                      params[:ca_file] = certs
         | 
| 1423 | 
            +
                    elsif File.directory?(certs)
         | 
| 1424 | 
            +
                      params[:ca_path] = certs
         | 
| 1368 1425 | 
             
                    end
         | 
| 1369 1426 | 
             
                  end
         | 
| 1370 | 
            -
                   | 
| 1427 | 
            +
                  if verify
         | 
| 1428 | 
            +
                    params[:verify_mode] = VERIFY_PEER
         | 
| 1429 | 
            +
                  else
         | 
| 1430 | 
            +
                    params[:verify_mode] = VERIFY_NONE
         | 
| 1431 | 
            +
                  end
         | 
| 1432 | 
            +
                  return params
         | 
| 1433 | 
            +
                end
         | 
| 1434 | 
            +
             | 
| 1435 | 
            +
                def start_tls_session(params = {})
         | 
| 1436 | 
            +
                  unless defined?(OpenSSL::SSL)
         | 
| 1437 | 
            +
                    raise "SSL extension not installed"
         | 
| 1438 | 
            +
                  end
         | 
| 1439 | 
            +
                  if @sock.kind_of?(OpenSSL::SSL::SSLSocket)
         | 
| 1440 | 
            +
                    raise RuntimeError, "already using SSL"
         | 
| 1441 | 
            +
                  end
         | 
| 1442 | 
            +
                  begin
         | 
| 1443 | 
            +
                    params = params.to_hash
         | 
| 1444 | 
            +
                  rescue NoMethodError
         | 
| 1445 | 
            +
                    params = {}
         | 
| 1446 | 
            +
                  end
         | 
| 1447 | 
            +
                  context = SSLContext.new
         | 
| 1448 | 
            +
                  context.set_params(params)
         | 
| 1449 | 
            +
                  if defined?(VerifyCallbackProc)
         | 
| 1450 | 
            +
                    context.verify_callback = VerifyCallbackProc
         | 
| 1451 | 
            +
                  end
         | 
| 1452 | 
            +
                  @sock = SSLSocket.new(@sock, context)
         | 
| 1453 | 
            +
                  @sock.sync_close = true
         | 
| 1454 | 
            +
                  @sock.connect
         | 
| 1455 | 
            +
                  if context.verify_mode != VERIFY_NONE
         | 
| 1456 | 
            +
                    @sock.post_connection_check(@host)
         | 
| 1457 | 
            +
                  end
         | 
| 1371 1458 | 
             
                end
         | 
| 1372 | 
            -
                private_class_method :u8tou16
         | 
| 1373 1459 |  | 
| 1374 1460 | 
             
                class RawData # :nodoc:
         | 
| 1375 1461 | 
             
                  def send_data(imap)
         | 
| 1376 1462 | 
             
                    imap.send(:put_string, @data)
         | 
| 1377 1463 | 
             
                  end
         | 
| 1378 1464 |  | 
| 1465 | 
            +
                  def validate
         | 
| 1466 | 
            +
                  end
         | 
| 1467 | 
            +
             | 
| 1379 1468 | 
             
                  private
         | 
| 1380 1469 |  | 
| 1381 1470 | 
             
                  def initialize(data)
         | 
| @@ -1388,6 +1477,9 @@ module Net | |
| 1388 1477 | 
             
                    imap.send(:put_string, @data)
         | 
| 1389 1478 | 
             
                  end
         | 
| 1390 1479 |  | 
| 1480 | 
            +
                  def validate
         | 
| 1481 | 
            +
                  end
         | 
| 1482 | 
            +
             | 
| 1391 1483 | 
             
                  private
         | 
| 1392 1484 |  | 
| 1393 1485 | 
             
                  def initialize(data)
         | 
| @@ -1400,6 +1492,9 @@ module Net | |
| 1400 1492 | 
             
                    imap.send(:send_quoted_string, @data)
         | 
| 1401 1493 | 
             
                  end
         | 
| 1402 1494 |  | 
| 1495 | 
            +
                  def validate
         | 
| 1496 | 
            +
                  end
         | 
| 1497 | 
            +
             | 
| 1403 1498 | 
             
                  private
         | 
| 1404 1499 |  | 
| 1405 1500 | 
             
                  def initialize(data)
         | 
| @@ -1412,6 +1507,9 @@ module Net | |
| 1412 1507 | 
             
                    imap.send(:send_literal, @data)
         | 
| 1413 1508 | 
             
                  end
         | 
| 1414 1509 |  | 
| 1510 | 
            +
                  def validate
         | 
| 1511 | 
            +
                  end
         | 
| 1512 | 
            +
             | 
| 1415 1513 | 
             
                  private
         | 
| 1416 1514 |  | 
| 1417 1515 | 
             
                  def initialize(data)
         | 
| @@ -1424,6 +1522,10 @@ module Net | |
| 1424 1522 | 
             
                    imap.send(:put_string, format_internal(@data))
         | 
| 1425 1523 | 
             
                  end
         | 
| 1426 1524 |  | 
| 1525 | 
            +
                  def validate
         | 
| 1526 | 
            +
                    validate_internal(@data)
         | 
| 1527 | 
            +
                  end
         | 
| 1528 | 
            +
             | 
| 1427 1529 | 
             
                  private
         | 
| 1428 1530 |  | 
| 1429 1531 | 
             
                  def initialize(data)
         | 
| @@ -1435,7 +1537,6 @@ module Net | |
| 1435 1537 | 
             
                    when "*"
         | 
| 1436 1538 | 
             
                      return data
         | 
| 1437 1539 | 
             
                    when Integer
         | 
| 1438 | 
            -
                      ensure_nz_number(data)
         | 
| 1439 1540 | 
             
                      if data == -1
         | 
| 1440 1541 | 
             
                        return "*"
         | 
| 1441 1542 | 
             
                      else
         | 
| @@ -1449,6 +1550,23 @@ module Net | |
| 1449 1550 | 
             
                    when ThreadMember
         | 
| 1450 1551 | 
             
                      return data.seqno.to_s +
         | 
| 1451 1552 | 
             
                        ":" + data.children.collect {|i| format_internal(i).join(",")}
         | 
| 1553 | 
            +
                    end
         | 
| 1554 | 
            +
                  end
         | 
| 1555 | 
            +
             | 
| 1556 | 
            +
                  def validate_internal(data)
         | 
| 1557 | 
            +
                    case data
         | 
| 1558 | 
            +
                    when "*"
         | 
| 1559 | 
            +
                    when Integer
         | 
| 1560 | 
            +
                      ensure_nz_number(data)
         | 
| 1561 | 
            +
                    when Range
         | 
| 1562 | 
            +
                    when Array
         | 
| 1563 | 
            +
                      data.each do |i|
         | 
| 1564 | 
            +
                        validate_internal(i)
         | 
| 1565 | 
            +
                      end
         | 
| 1566 | 
            +
                    when ThreadMember
         | 
| 1567 | 
            +
                      data.children.each do |i|
         | 
| 1568 | 
            +
                        validate_internal(i)
         | 
| 1569 | 
            +
                      end
         | 
| 1452 1570 | 
             
                    else
         | 
| 1453 1571 | 
             
                      raise DataFormatError, data.inspect
         | 
| 1454 1572 | 
             
                    end
         | 
| @@ -1464,109 +1582,109 @@ module Net | |
| 1464 1582 | 
             
                end
         | 
| 1465 1583 |  | 
| 1466 1584 | 
             
                # Net::IMAP::ContinuationRequest represents command continuation requests.
         | 
| 1467 | 
            -
                # | 
| 1585 | 
            +
                #
         | 
| 1468 1586 | 
             
                # The command continuation request response is indicated by a "+" token
         | 
| 1469 1587 | 
             
                # instead of a tag.  This form of response indicates that the server is
         | 
| 1470 1588 | 
             
                # ready to accept the continuation of a command from the client.  The
         | 
| 1471 1589 | 
             
                # remainder of this response is a line of text.
         | 
| 1472 | 
            -
                # | 
| 1590 | 
            +
                #
         | 
| 1473 1591 | 
             
                #   continue_req    ::= "+" SPACE (resp_text / base64)
         | 
| 1474 | 
            -
                # | 
| 1592 | 
            +
                #
         | 
| 1475 1593 | 
             
                # ==== Fields:
         | 
| 1476 | 
            -
                # | 
| 1594 | 
            +
                #
         | 
| 1477 1595 | 
             
                # data:: Returns the data (Net::IMAP::ResponseText).
         | 
| 1478 | 
            -
                # | 
| 1596 | 
            +
                #
         | 
| 1479 1597 | 
             
                # raw_data:: Returns the raw data string.
         | 
| 1480 1598 | 
             
                ContinuationRequest = Struct.new(:data, :raw_data)
         | 
| 1481 1599 |  | 
| 1482 1600 | 
             
                # Net::IMAP::UntaggedResponse represents untagged responses.
         | 
| 1483 | 
            -
                # | 
| 1601 | 
            +
                #
         | 
| 1484 1602 | 
             
                # Data transmitted by the server to the client and status responses
         | 
| 1485 1603 | 
             
                # that do not indicate command completion are prefixed with the token
         | 
| 1486 1604 | 
             
                # "*", and are called untagged responses.
         | 
| 1487 | 
            -
                # | 
| 1605 | 
            +
                #
         | 
| 1488 1606 | 
             
                #   response_data   ::= "*" SPACE (resp_cond_state / resp_cond_bye /
         | 
| 1489 1607 | 
             
                #                       mailbox_data / message_data / capability_data)
         | 
| 1490 | 
            -
                # | 
| 1608 | 
            +
                #
         | 
| 1491 1609 | 
             
                # ==== Fields:
         | 
| 1492 | 
            -
                # | 
| 1610 | 
            +
                #
         | 
| 1493 1611 | 
             
                # name:: Returns the name such as "FLAGS", "LIST", "FETCH"....
         | 
| 1494 | 
            -
                # | 
| 1612 | 
            +
                #
         | 
| 1495 1613 | 
             
                # data:: Returns the data such as an array of flag symbols,
         | 
| 1496 1614 | 
             
                #         a ((<Net::IMAP::MailboxList>)) object....
         | 
| 1497 | 
            -
                # | 
| 1615 | 
            +
                #
         | 
| 1498 1616 | 
             
                # raw_data:: Returns the raw data string.
         | 
| 1499 1617 | 
             
                UntaggedResponse = Struct.new(:name, :data, :raw_data)
         | 
| 1500 | 
            -
             | 
| 1618 | 
            +
             | 
| 1501 1619 | 
             
                # Net::IMAP::TaggedResponse represents tagged responses.
         | 
| 1502 | 
            -
                # | 
| 1620 | 
            +
                #
         | 
| 1503 1621 | 
             
                # The server completion result response indicates the success or
         | 
| 1504 1622 | 
             
                # failure of the operation.  It is tagged with the same tag as the
         | 
| 1505 1623 | 
             
                # client command which began the operation.
         | 
| 1506 | 
            -
                # | 
| 1624 | 
            +
                #
         | 
| 1507 1625 | 
             
                #   response_tagged ::= tag SPACE resp_cond_state CRLF
         | 
| 1508 | 
            -
                # | 
| 1626 | 
            +
                #
         | 
| 1509 1627 | 
             
                #   tag             ::= 1*<any ATOM_CHAR except "+">
         | 
| 1510 | 
            -
                # | 
| 1628 | 
            +
                #
         | 
| 1511 1629 | 
             
                #   resp_cond_state ::= ("OK" / "NO" / "BAD") SPACE resp_text
         | 
| 1512 | 
            -
                # | 
| 1630 | 
            +
                #
         | 
| 1513 1631 | 
             
                # ==== Fields:
         | 
| 1514 | 
            -
                # | 
| 1632 | 
            +
                #
         | 
| 1515 1633 | 
             
                # tag:: Returns the tag.
         | 
| 1516 | 
            -
                # | 
| 1634 | 
            +
                #
         | 
| 1517 1635 | 
             
                # name:: Returns the name. the name is one of "OK", "NO", "BAD".
         | 
| 1518 | 
            -
                # | 
| 1636 | 
            +
                #
         | 
| 1519 1637 | 
             
                # data:: Returns the data. See ((<Net::IMAP::ResponseText>)).
         | 
| 1520 | 
            -
                # | 
| 1638 | 
            +
                #
         | 
| 1521 1639 | 
             
                # raw_data:: Returns the raw data string.
         | 
| 1522 1640 | 
             
                #
         | 
| 1523 1641 | 
             
                TaggedResponse = Struct.new(:tag, :name, :data, :raw_data)
         | 
| 1524 | 
            -
             | 
| 1642 | 
            +
             | 
| 1525 1643 | 
             
                # Net::IMAP::ResponseText represents texts of responses.
         | 
| 1526 1644 | 
             
                # The text may be prefixed by the response code.
         | 
| 1527 | 
            -
                # | 
| 1645 | 
            +
                #
         | 
| 1528 1646 | 
             
                #   resp_text       ::= ["[" resp_text_code "]" SPACE] (text_mime2 / text)
         | 
| 1529 1647 | 
             
                #                       ;; text SHOULD NOT begin with "[" or "="
         | 
| 1530 | 
            -
                # | 
| 1648 | 
            +
                #
         | 
| 1531 1649 | 
             
                # ==== Fields:
         | 
| 1532 | 
            -
                # | 
| 1650 | 
            +
                #
         | 
| 1533 1651 | 
             
                # code:: Returns the response code. See ((<Net::IMAP::ResponseCode>)).
         | 
| 1534 | 
            -
                # | 
| 1652 | 
            +
                #
         | 
| 1535 1653 | 
             
                # text:: Returns the text.
         | 
| 1536 | 
            -
                # | 
| 1654 | 
            +
                #
         | 
| 1537 1655 | 
             
                ResponseText = Struct.new(:code, :text)
         | 
| 1538 1656 |  | 
| 1539 | 
            -
                # | 
| 1657 | 
            +
                #
         | 
| 1540 1658 | 
             
                # Net::IMAP::ResponseCode represents response codes.
         | 
| 1541 | 
            -
                # | 
| 1659 | 
            +
                #
         | 
| 1542 1660 | 
             
                #   resp_text_code  ::= "ALERT" / "PARSE" /
         | 
| 1543 1661 | 
             
                #                       "PERMANENTFLAGS" SPACE "(" #(flag / "\*") ")" /
         | 
| 1544 1662 | 
             
                #                       "READ-ONLY" / "READ-WRITE" / "TRYCREATE" /
         | 
| 1545 1663 | 
             
                #                       "UIDVALIDITY" SPACE nz_number /
         | 
| 1546 1664 | 
             
                #                       "UNSEEN" SPACE nz_number /
         | 
| 1547 1665 | 
             
                #                       atom [SPACE 1*<any TEXT_CHAR except "]">]
         | 
| 1548 | 
            -
                # | 
| 1666 | 
            +
                #
         | 
| 1549 1667 | 
             
                # ==== Fields:
         | 
| 1550 | 
            -
                # | 
| 1668 | 
            +
                #
         | 
| 1551 1669 | 
             
                # name:: Returns the name such as "ALERT", "PERMANENTFLAGS", "UIDVALIDITY"....
         | 
| 1552 | 
            -
                # | 
| 1670 | 
            +
                #
         | 
| 1553 1671 | 
             
                # data:: Returns the data if it exists.
         | 
| 1554 1672 | 
             
                #
         | 
| 1555 1673 | 
             
                ResponseCode = Struct.new(:name, :data)
         | 
| 1556 1674 |  | 
| 1557 1675 | 
             
                # Net::IMAP::MailboxList represents contents of the LIST response.
         | 
| 1558 | 
            -
                # | 
| 1676 | 
            +
                #
         | 
| 1559 1677 | 
             
                #   mailbox_list    ::= "(" #("\Marked" / "\Noinferiors" /
         | 
| 1560 1678 | 
             
                #                       "\Noselect" / "\Unmarked" / flag_extension) ")"
         | 
| 1561 1679 | 
             
                #                       SPACE (<"> QUOTED_CHAR <"> / nil) SPACE mailbox
         | 
| 1562 | 
            -
                # | 
| 1680 | 
            +
                #
         | 
| 1563 1681 | 
             
                # ==== Fields:
         | 
| 1564 | 
            -
                # | 
| 1682 | 
            +
                #
         | 
| 1565 1683 | 
             
                # attr:: Returns the name attributes. Each name attribute is a symbol
         | 
| 1566 1684 | 
             
                #        capitalized by String#capitalize, such as :Noselect (not :NoSelect).
         | 
| 1567 | 
            -
                # | 
| 1685 | 
            +
                #
         | 
| 1568 1686 | 
             
                # delim:: Returns the hierarchy delimiter
         | 
| 1569 | 
            -
                # | 
| 1687 | 
            +
                #
         | 
| 1570 1688 | 
             
                # name:: Returns the mailbox name.
         | 
| 1571 1689 | 
             
                #
         | 
| 1572 1690 | 
             
                MailboxList = Struct.new(:attr, :delim, :name)
         | 
| @@ -1575,78 +1693,78 @@ module Net | |
| 1575 1693 | 
             
                # This object can also be a response to GETQUOTAROOT.  In the syntax
         | 
| 1576 1694 | 
             
                # specification below, the delimiter used with the "#" construct is a
         | 
| 1577 1695 | 
             
                # single space (SPACE).
         | 
| 1578 | 
            -
                # | 
| 1696 | 
            +
                #
         | 
| 1579 1697 | 
             
                #    quota_list      ::= "(" #quota_resource ")"
         | 
| 1580 | 
            -
                # | 
| 1698 | 
            +
                #
         | 
| 1581 1699 | 
             
                #    quota_resource  ::= atom SPACE number SPACE number
         | 
| 1582 | 
            -
                # | 
| 1700 | 
            +
                #
         | 
| 1583 1701 | 
             
                #    quota_response  ::= "QUOTA" SPACE astring SPACE quota_list
         | 
| 1584 | 
            -
                # | 
| 1702 | 
            +
                #
         | 
| 1585 1703 | 
             
                # ==== Fields:
         | 
| 1586 | 
            -
                # | 
| 1704 | 
            +
                #
         | 
| 1587 1705 | 
             
                # mailbox:: The mailbox with the associated quota.
         | 
| 1588 | 
            -
                # | 
| 1706 | 
            +
                #
         | 
| 1589 1707 | 
             
                # usage:: Current storage usage of mailbox.
         | 
| 1590 | 
            -
                # | 
| 1708 | 
            +
                #
         | 
| 1591 1709 | 
             
                # quota:: Quota limit imposed on mailbox.
         | 
| 1592 1710 | 
             
                #
         | 
| 1593 1711 | 
             
                MailboxQuota = Struct.new(:mailbox, :usage, :quota)
         | 
| 1594 1712 |  | 
| 1595 1713 | 
             
                # Net::IMAP::MailboxQuotaRoot represents part of the GETQUOTAROOT
         | 
| 1596 1714 | 
             
                # response. (GETQUOTAROOT can also return Net::IMAP::MailboxQuota.)
         | 
| 1597 | 
            -
                # | 
| 1715 | 
            +
                #
         | 
| 1598 1716 | 
             
                #    quotaroot_response ::= "QUOTAROOT" SPACE astring *(SPACE astring)
         | 
| 1599 | 
            -
                # | 
| 1717 | 
            +
                #
         | 
| 1600 1718 | 
             
                # ==== Fields:
         | 
| 1601 | 
            -
                # | 
| 1719 | 
            +
                #
         | 
| 1602 1720 | 
             
                # mailbox:: The mailbox with the associated quota.
         | 
| 1603 | 
            -
                # | 
| 1721 | 
            +
                #
         | 
| 1604 1722 | 
             
                # quotaroots:: Zero or more quotaroots that effect the quota on the
         | 
| 1605 1723 | 
             
                #              specified mailbox.
         | 
| 1606 1724 | 
             
                #
         | 
| 1607 1725 | 
             
                MailboxQuotaRoot = Struct.new(:mailbox, :quotaroots)
         | 
| 1608 1726 |  | 
| 1609 1727 | 
             
                # Net::IMAP::MailboxACLItem represents response from GETACL.
         | 
| 1610 | 
            -
                # | 
| 1728 | 
            +
                #
         | 
| 1611 1729 | 
             
                #    acl_data        ::= "ACL" SPACE mailbox *(SPACE identifier SPACE rights)
         | 
| 1612 | 
            -
                # | 
| 1730 | 
            +
                #
         | 
| 1613 1731 | 
             
                #    identifier      ::= astring
         | 
| 1614 | 
            -
                # | 
| 1732 | 
            +
                #
         | 
| 1615 1733 | 
             
                #    rights          ::= astring
         | 
| 1616 | 
            -
                # | 
| 1734 | 
            +
                #
         | 
| 1617 1735 | 
             
                # ==== Fields:
         | 
| 1618 | 
            -
                # | 
| 1736 | 
            +
                #
         | 
| 1619 1737 | 
             
                # user:: Login name that has certain rights to the mailbox
         | 
| 1620 1738 | 
             
                #        that was specified with the getacl command.
         | 
| 1621 | 
            -
                # | 
| 1739 | 
            +
                #
         | 
| 1622 1740 | 
             
                # rights:: The access rights the indicated user has to the
         | 
| 1623 1741 | 
             
                #          mailbox.
         | 
| 1624 1742 | 
             
                #
         | 
| 1625 | 
            -
                MailboxACLItem = Struct.new(:user, :rights)
         | 
| 1743 | 
            +
                MailboxACLItem = Struct.new(:user, :rights, :mailbox)
         | 
| 1626 1744 |  | 
| 1627 1745 | 
             
                # Net::IMAP::StatusData represents contents of the STATUS response.
         | 
| 1628 | 
            -
                # | 
| 1746 | 
            +
                #
         | 
| 1629 1747 | 
             
                # ==== Fields:
         | 
| 1630 | 
            -
                # | 
| 1748 | 
            +
                #
         | 
| 1631 1749 | 
             
                # mailbox:: Returns the mailbox name.
         | 
| 1632 | 
            -
                # | 
| 1750 | 
            +
                #
         | 
| 1633 1751 | 
             
                # attr:: Returns a hash. Each key is one of "MESSAGES", "RECENT", "UIDNEXT",
         | 
| 1634 1752 | 
             
                #        "UIDVALIDITY", "UNSEEN". Each value is a number.
         | 
| 1635 | 
            -
                # | 
| 1753 | 
            +
                #
         | 
| 1636 1754 | 
             
                StatusData = Struct.new(:mailbox, :attr)
         | 
| 1637 1755 |  | 
| 1638 1756 | 
             
                # Net::IMAP::FetchData represents contents of the FETCH response.
         | 
| 1639 | 
            -
                # | 
| 1757 | 
            +
                #
         | 
| 1640 1758 | 
             
                # ==== Fields:
         | 
| 1641 | 
            -
                # | 
| 1759 | 
            +
                #
         | 
| 1642 1760 | 
             
                # seqno:: Returns the message sequence number.
         | 
| 1643 1761 | 
             
                #         (Note: not the unique identifier, even for the UID command response.)
         | 
| 1644 | 
            -
                # | 
| 1762 | 
            +
                #
         | 
| 1645 1763 | 
             
                # attr:: Returns a hash. Each key is a data item name, and each value is
         | 
| 1646 1764 | 
             
                #        its value.
         | 
| 1647 | 
            -
                # | 
| 1765 | 
            +
                #
         | 
| 1648 1766 | 
             
                #        The current data items are:
         | 
| 1649 | 
            -
                # | 
| 1767 | 
            +
                #
         | 
| 1650 1768 | 
             
                #        [BODY]
         | 
| 1651 1769 | 
             
                #           A form of BODYSTRUCTURE without extension data.
         | 
| 1652 1770 | 
             
                #        [BODY[<section>]<<origin_octet>>]
         | 
| @@ -1673,67 +1791,67 @@ module Net | |
| 1673 1791 | 
             
                #           Equivalent to BODY[TEXT].
         | 
| 1674 1792 | 
             
                #        [UID]
         | 
| 1675 1793 | 
             
                #           A number expressing the unique identifier of the message.
         | 
| 1676 | 
            -
                # | 
| 1794 | 
            +
                #
         | 
| 1677 1795 | 
             
                FetchData = Struct.new(:seqno, :attr)
         | 
| 1678 1796 |  | 
| 1679 1797 | 
             
                # Net::IMAP::Envelope represents envelope structures of messages.
         | 
| 1680 | 
            -
                # | 
| 1798 | 
            +
                #
         | 
| 1681 1799 | 
             
                # ==== Fields:
         | 
| 1682 | 
            -
                # | 
| 1800 | 
            +
                #
         | 
| 1683 1801 | 
             
                # date:: Returns a string that represents the date.
         | 
| 1684 | 
            -
                # | 
| 1802 | 
            +
                #
         | 
| 1685 1803 | 
             
                # subject:: Returns a string that represents the subject.
         | 
| 1686 | 
            -
                # | 
| 1804 | 
            +
                #
         | 
| 1687 1805 | 
             
                # from:: Returns an array of Net::IMAP::Address that represents the from.
         | 
| 1688 | 
            -
                # | 
| 1806 | 
            +
                #
         | 
| 1689 1807 | 
             
                # sender:: Returns an array of Net::IMAP::Address that represents the sender.
         | 
| 1690 | 
            -
                # | 
| 1808 | 
            +
                #
         | 
| 1691 1809 | 
             
                # reply_to:: Returns an array of Net::IMAP::Address that represents the reply-to.
         | 
| 1692 | 
            -
                # | 
| 1810 | 
            +
                #
         | 
| 1693 1811 | 
             
                # to:: Returns an array of Net::IMAP::Address that represents the to.
         | 
| 1694 | 
            -
                # | 
| 1812 | 
            +
                #
         | 
| 1695 1813 | 
             
                # cc:: Returns an array of Net::IMAP::Address that represents the cc.
         | 
| 1696 | 
            -
                # | 
| 1814 | 
            +
                #
         | 
| 1697 1815 | 
             
                # bcc:: Returns an array of Net::IMAP::Address that represents the bcc.
         | 
| 1698 | 
            -
                # | 
| 1816 | 
            +
                #
         | 
| 1699 1817 | 
             
                # in_reply_to:: Returns a string that represents the in-reply-to.
         | 
| 1700 | 
            -
                # | 
| 1818 | 
            +
                #
         | 
| 1701 1819 | 
             
                # message_id:: Returns a string that represents the message-id.
         | 
| 1702 | 
            -
                # | 
| 1820 | 
            +
                #
         | 
| 1703 1821 | 
             
                Envelope = Struct.new(:date, :subject, :from, :sender, :reply_to,
         | 
| 1704 1822 | 
             
                                      :to, :cc, :bcc, :in_reply_to, :message_id)
         | 
| 1705 1823 |  | 
| 1706 | 
            -
                # | 
| 1824 | 
            +
                #
         | 
| 1707 1825 | 
             
                # Net::IMAP::Address represents electronic mail addresses.
         | 
| 1708 | 
            -
                # | 
| 1826 | 
            +
                #
         | 
| 1709 1827 | 
             
                # ==== Fields:
         | 
| 1710 | 
            -
                # | 
| 1828 | 
            +
                #
         | 
| 1711 1829 | 
             
                # name:: Returns the phrase from [RFC-822] mailbox.
         | 
| 1712 | 
            -
                # | 
| 1830 | 
            +
                #
         | 
| 1713 1831 | 
             
                # route:: Returns the route from [RFC-822] route-addr.
         | 
| 1714 | 
            -
                # | 
| 1832 | 
            +
                #
         | 
| 1715 1833 | 
             
                # mailbox:: nil indicates end of [RFC-822] group.
         | 
| 1716 1834 | 
             
                #           If non-nil and host is nil, returns [RFC-822] group name.
         | 
| 1717 1835 | 
             
                #           Otherwise, returns [RFC-822] local-part
         | 
| 1718 | 
            -
                # | 
| 1836 | 
            +
                #
         | 
| 1719 1837 | 
             
                # host:: nil indicates [RFC-822] group syntax.
         | 
| 1720 1838 | 
             
                #        Otherwise, returns [RFC-822] domain name.
         | 
| 1721 1839 | 
             
                #
         | 
| 1722 1840 | 
             
                Address = Struct.new(:name, :route, :mailbox, :host)
         | 
| 1723 1841 |  | 
| 1724 | 
            -
                # | 
| 1842 | 
            +
                #
         | 
| 1725 1843 | 
             
                # Net::IMAP::ContentDisposition represents Content-Disposition fields.
         | 
| 1726 | 
            -
                # | 
| 1844 | 
            +
                #
         | 
| 1727 1845 | 
             
                # ==== Fields:
         | 
| 1728 | 
            -
                # | 
| 1846 | 
            +
                #
         | 
| 1729 1847 | 
             
                # dsp_type:: Returns the disposition type.
         | 
| 1730 | 
            -
                # | 
| 1848 | 
            +
                #
         | 
| 1731 1849 | 
             
                # param:: Returns a hash that represents parameters of the Content-Disposition
         | 
| 1732 1850 | 
             
                #         field.
         | 
| 1733 | 
            -
                # | 
| 1851 | 
            +
                #
         | 
| 1734 1852 | 
             
                ContentDisposition = Struct.new(:dsp_type, :param)
         | 
| 1735 1853 |  | 
| 1736 | 
            -
                # Net::IMAP::ThreadMember represents a thread-node returned | 
| 1854 | 
            +
                # Net::IMAP::ThreadMember represents a thread-node returned
         | 
| 1737 1855 | 
             
                # by Net::IMAP#thread
         | 
| 1738 1856 | 
             
                #
         | 
| 1739 1857 | 
             
                # ==== Fields:
         | 
| @@ -1746,37 +1864,37 @@ module Net | |
| 1746 1864 | 
             
                ThreadMember = Struct.new(:seqno, :children)
         | 
| 1747 1865 |  | 
| 1748 1866 | 
             
                # Net::IMAP::BodyTypeBasic represents basic body structures of messages.
         | 
| 1749 | 
            -
                # | 
| 1867 | 
            +
                #
         | 
| 1750 1868 | 
             
                # ==== Fields:
         | 
| 1751 | 
            -
                # | 
| 1869 | 
            +
                #
         | 
| 1752 1870 | 
             
                # media_type:: Returns the content media type name as defined in [MIME-IMB].
         | 
| 1753 | 
            -
                # | 
| 1871 | 
            +
                #
         | 
| 1754 1872 | 
             
                # subtype:: Returns the content subtype name as defined in [MIME-IMB].
         | 
| 1755 | 
            -
                # | 
| 1873 | 
            +
                #
         | 
| 1756 1874 | 
             
                # param:: Returns a hash that represents parameters as defined in [MIME-IMB].
         | 
| 1757 | 
            -
                # | 
| 1875 | 
            +
                #
         | 
| 1758 1876 | 
             
                # content_id:: Returns a string giving the content id as defined in [MIME-IMB].
         | 
| 1759 | 
            -
                # | 
| 1877 | 
            +
                #
         | 
| 1760 1878 | 
             
                # description:: Returns a string giving the content description as defined in
         | 
| 1761 1879 | 
             
                #               [MIME-IMB].
         | 
| 1762 | 
            -
                # | 
| 1880 | 
            +
                #
         | 
| 1763 1881 | 
             
                # encoding:: Returns a string giving the content transfer encoding as defined in
         | 
| 1764 1882 | 
             
                #            [MIME-IMB].
         | 
| 1765 | 
            -
                # | 
| 1883 | 
            +
                #
         | 
| 1766 1884 | 
             
                # size:: Returns a number giving the size of the body in octets.
         | 
| 1767 | 
            -
                # | 
| 1885 | 
            +
                #
         | 
| 1768 1886 | 
             
                # md5:: Returns a string giving the body MD5 value as defined in [MD5].
         | 
| 1769 | 
            -
                # | 
| 1887 | 
            +
                #
         | 
| 1770 1888 | 
             
                # disposition:: Returns a Net::IMAP::ContentDisposition object giving
         | 
| 1771 1889 | 
             
                #               the content disposition.
         | 
| 1772 | 
            -
                # | 
| 1890 | 
            +
                #
         | 
| 1773 1891 | 
             
                # language:: Returns a string or an array of strings giving the body
         | 
| 1774 1892 | 
             
                #            language value as defined in [LANGUAGE-TAGS].
         | 
| 1775 | 
            -
                # | 
| 1893 | 
            +
                #
         | 
| 1776 1894 | 
             
                # extension:: Returns extension data.
         | 
| 1777 | 
            -
                # | 
| 1895 | 
            +
                #
         | 
| 1778 1896 | 
             
                # multipart?:: Returns false.
         | 
| 1779 | 
            -
                # | 
| 1897 | 
            +
                #
         | 
| 1780 1898 | 
             
                class BodyTypeBasic < Struct.new(:media_type, :subtype,
         | 
| 1781 1899 | 
             
                                                 :param, :content_id,
         | 
| 1782 1900 | 
             
                                                 :description, :encoding, :size,
         | 
| @@ -1787,7 +1905,7 @@ module Net | |
| 1787 1905 | 
             
                  end
         | 
| 1788 1906 |  | 
| 1789 1907 | 
             
                  # Obsolete: use +subtype+ instead.  Calling this will
         | 
| 1790 | 
            -
                  # generate a warning message to +stderr+, then return | 
| 1908 | 
            +
                  # generate a warning message to +stderr+, then return
         | 
| 1791 1909 | 
             
                  # the value of +subtype+.
         | 
| 1792 1910 | 
             
                  def media_subtype
         | 
| 1793 1911 | 
             
                    $stderr.printf("warning: media_subtype is obsolete.\n")
         | 
| @@ -1797,13 +1915,13 @@ module Net | |
| 1797 1915 | 
             
                end
         | 
| 1798 1916 |  | 
| 1799 1917 | 
             
                # Net::IMAP::BodyTypeText represents TEXT body structures of messages.
         | 
| 1800 | 
            -
                # | 
| 1918 | 
            +
                #
         | 
| 1801 1919 | 
             
                # ==== Fields:
         | 
| 1802 | 
            -
                # | 
| 1920 | 
            +
                #
         | 
| 1803 1921 | 
             
                # lines:: Returns the size of the body in text lines.
         | 
| 1804 | 
            -
                # | 
| 1922 | 
            +
                #
         | 
| 1805 1923 | 
             
                # And Net::IMAP::BodyTypeText has all fields of Net::IMAP::BodyTypeBasic.
         | 
| 1806 | 
            -
                # | 
| 1924 | 
            +
                #
         | 
| 1807 1925 | 
             
                class BodyTypeText < Struct.new(:media_type, :subtype,
         | 
| 1808 1926 | 
             
                                                :param, :content_id,
         | 
| 1809 1927 | 
             
                                                :description, :encoding, :size,
         | 
| @@ -1815,7 +1933,7 @@ module Net | |
| 1815 1933 | 
             
                  end
         | 
| 1816 1934 |  | 
| 1817 1935 | 
             
                  # Obsolete: use +subtype+ instead.  Calling this will
         | 
| 1818 | 
            -
                  # generate a warning message to +stderr+, then return | 
| 1936 | 
            +
                  # generate a warning message to +stderr+, then return
         | 
| 1819 1937 | 
             
                  # the value of +subtype+.
         | 
| 1820 1938 | 
             
                  def media_subtype
         | 
| 1821 1939 | 
             
                    $stderr.printf("warning: media_subtype is obsolete.\n")
         | 
| @@ -1825,13 +1943,13 @@ module Net | |
| 1825 1943 | 
             
                end
         | 
| 1826 1944 |  | 
| 1827 1945 | 
             
                # Net::IMAP::BodyTypeMessage represents MESSAGE/RFC822 body structures of messages.
         | 
| 1828 | 
            -
                # | 
| 1946 | 
            +
                #
         | 
| 1829 1947 | 
             
                # ==== Fields:
         | 
| 1830 | 
            -
                # | 
| 1948 | 
            +
                #
         | 
| 1831 1949 | 
             
                # envelope:: Returns a Net::IMAP::Envelope giving the envelope structure.
         | 
| 1832 | 
            -
                # | 
| 1950 | 
            +
                #
         | 
| 1833 1951 | 
             
                # body:: Returns an object giving the body structure.
         | 
| 1834 | 
            -
                # | 
| 1952 | 
            +
                #
         | 
| 1835 1953 | 
             
                # And Net::IMAP::BodyTypeMessage has all methods of Net::IMAP::BodyTypeText.
         | 
| 1836 1954 | 
             
                #
         | 
| 1837 1955 | 
             
                class BodyTypeMessage < Struct.new(:media_type, :subtype,
         | 
| @@ -1845,7 +1963,7 @@ module Net | |
| 1845 1963 | 
             
                  end
         | 
| 1846 1964 |  | 
| 1847 1965 | 
             
                  # Obsolete: use +subtype+ instead.  Calling this will
         | 
| 1848 | 
            -
                  # generate a warning message to +stderr+, then return | 
| 1966 | 
            +
                  # generate a warning message to +stderr+, then return
         | 
| 1849 1967 | 
             
                  # the value of +subtype+.
         | 
| 1850 1968 | 
             
                  def media_subtype
         | 
| 1851 1969 | 
             
                    $stderr.printf("warning: media_subtype is obsolete.\n")
         | 
| @@ -1854,29 +1972,49 @@ module Net | |
| 1854 1972 | 
             
                  end
         | 
| 1855 1973 | 
             
                end
         | 
| 1856 1974 |  | 
| 1857 | 
            -
                # Net::IMAP:: | 
| 1975 | 
            +
                # Net::IMAP::BodyTypeAttachment represents attachment body structures
         | 
| 1976 | 
            +
                # of messages.
         | 
| 1977 | 
            +
                #
         | 
| 1978 | 
            +
                # ==== Fields:
         | 
| 1979 | 
            +
                #
         | 
| 1980 | 
            +
                # media_type:: Returns the content media type name.
         | 
| 1981 | 
            +
                #
         | 
| 1982 | 
            +
                # subtype:: Returns +nil+.
         | 
| 1983 | 
            +
                #
         | 
| 1984 | 
            +
                # param:: Returns a hash that represents parameters.
         | 
| 1985 | 
            +
                #
         | 
| 1986 | 
            +
                # multipart?:: Returns false.
         | 
| 1987 | 
            +
                #
         | 
| 1988 | 
            +
                class BodyTypeAttachment < Struct.new(:media_type, :subtype,
         | 
| 1989 | 
            +
                                                      :param)
         | 
| 1990 | 
            +
                  def multipart?
         | 
| 1991 | 
            +
                    return false
         | 
| 1992 | 
            +
                  end
         | 
| 1993 | 
            +
                end
         | 
| 1994 | 
            +
             | 
| 1995 | 
            +
                # Net::IMAP::BodyTypeMultipart represents multipart body structures
         | 
| 1858 1996 | 
             
                # of messages.
         | 
| 1859 | 
            -
                # | 
| 1997 | 
            +
                #
         | 
| 1860 1998 | 
             
                # ==== Fields:
         | 
| 1861 | 
            -
                # | 
| 1999 | 
            +
                #
         | 
| 1862 2000 | 
             
                # media_type:: Returns the content media type name as defined in [MIME-IMB].
         | 
| 1863 | 
            -
                # | 
| 2001 | 
            +
                #
         | 
| 1864 2002 | 
             
                # subtype:: Returns the content subtype name as defined in [MIME-IMB].
         | 
| 1865 | 
            -
                # | 
| 2003 | 
            +
                #
         | 
| 1866 2004 | 
             
                # parts:: Returns multiple parts.
         | 
| 1867 | 
            -
                # | 
| 2005 | 
            +
                #
         | 
| 1868 2006 | 
             
                # param:: Returns a hash that represents parameters as defined in [MIME-IMB].
         | 
| 1869 | 
            -
                # | 
| 2007 | 
            +
                #
         | 
| 1870 2008 | 
             
                # disposition:: Returns a Net::IMAP::ContentDisposition object giving
         | 
| 1871 2009 | 
             
                #               the content disposition.
         | 
| 1872 | 
            -
                # | 
| 2010 | 
            +
                #
         | 
| 1873 2011 | 
             
                # language:: Returns a string or an array of strings giving the body
         | 
| 1874 2012 | 
             
                #            language value as defined in [LANGUAGE-TAGS].
         | 
| 1875 | 
            -
                # | 
| 2013 | 
            +
                #
         | 
| 1876 2014 | 
             
                # extension:: Returns extension data.
         | 
| 1877 | 
            -
                # | 
| 2015 | 
            +
                #
         | 
| 1878 2016 | 
             
                # multipart?:: Returns true.
         | 
| 1879 | 
            -
                # | 
| 2017 | 
            +
                #
         | 
| 1880 2018 | 
             
                class BodyTypeMultipart < Struct.new(:media_type, :subtype,
         | 
| 1881 2019 | 
             
                                                     :parts,
         | 
| 1882 2020 | 
             
                                                     :param, :disposition, :language,
         | 
| @@ -1886,7 +2024,7 @@ module Net | |
| 1886 2024 | 
             
                  end
         | 
| 1887 2025 |  | 
| 1888 2026 | 
             
                  # Obsolete: use +subtype+ instead.  Calling this will
         | 
| 1889 | 
            -
                  # generate a warning message to +stderr+, then return | 
| 2027 | 
            +
                  # generate a warning message to +stderr+, then return
         | 
| 1890 2028 | 
             
                  # the value of +subtype+.
         | 
| 1891 2029 | 
             
                  def media_subtype
         | 
| 1892 2030 | 
             
                    $stderr.printf("warning: media_subtype is obsolete.\n")
         | 
| @@ -1895,7 +2033,23 @@ module Net | |
| 1895 2033 | 
             
                  end
         | 
| 1896 2034 | 
             
                end
         | 
| 1897 2035 |  | 
| 2036 | 
            +
                class BodyTypeExtension < Struct.new(:media_type, :subtype,
         | 
| 2037 | 
            +
                                                     :params, :content_id,
         | 
| 2038 | 
            +
                                                     :description, :encoding, :size)
         | 
| 2039 | 
            +
                  def multipart?
         | 
| 2040 | 
            +
                    return false
         | 
| 2041 | 
            +
                  end
         | 
| 2042 | 
            +
                end
         | 
| 2043 | 
            +
             | 
| 1898 2044 | 
             
                class ResponseParser # :nodoc:
         | 
| 2045 | 
            +
                  def initialize
         | 
| 2046 | 
            +
                    @str = nil
         | 
| 2047 | 
            +
                    @pos = nil
         | 
| 2048 | 
            +
                    @lex_state = nil
         | 
| 2049 | 
            +
                    @token = nil
         | 
| 2050 | 
            +
                    @flag_symbols = {}
         | 
| 2051 | 
            +
                  end
         | 
| 2052 | 
            +
             | 
| 1899 2053 | 
             
                  def parse(str)
         | 
| 1900 2054 | 
             
                    @str = str
         | 
| 1901 2055 | 
             
                    @pos = 0
         | 
| @@ -1932,9 +2086,9 @@ module Net | |
| 1932 2086 |  | 
| 1933 2087 | 
             
                  BEG_REGEXP = /\G(?:\
         | 
| 1934 2088 | 
             
            (?# 1:  SPACE   )( +)|\
         | 
| 1935 | 
            -
            (?# 2:  NIL     )(NIL)(?=[\x80-\xff(){ \x00-\x1f\x7f | 
| 1936 | 
            -
            (?# 3:  NUMBER  )(\d+)(?=[\x80-\xff(){ \x00-\x1f\x7f | 
| 1937 | 
            -
            (?# 4:  ATOM    )([^\x80-\xff(){ \x00-\x1f\x7f | 
| 2089 | 
            +
            (?# 2:  NIL     )(NIL)(?=[\x80-\xff(){ \x00-\x1f\x7f%*#{'"'}\\\[\]+])|\
         | 
| 2090 | 
            +
            (?# 3:  NUMBER  )(\d+)(?=[\x80-\xff(){ \x00-\x1f\x7f%*#{'"'}\\\[\]+])|\
         | 
| 2091 | 
            +
            (?# 4:  ATOM    )([^\x80-\xff(){ \x00-\x1f\x7f%*#{'"'}\\\[\]+]+)|\
         | 
| 1938 2092 | 
             
            (?# 5:  QUOTED  )"((?:[^\x00\r\n"\\]|\\["\\])*)"|\
         | 
| 1939 2093 | 
             
            (?# 6:  LPAR    )(\()|\
         | 
| 1940 2094 | 
             
            (?# 7:  RPAR    )(\))|\
         | 
| @@ -2002,7 +2156,7 @@ module Net | |
| 2002 2156 | 
             
                        return response_cond
         | 
| 2003 2157 | 
             
                      when /\A(?:FLAGS)\z/ni
         | 
| 2004 2158 | 
             
                        return flags_response
         | 
| 2005 | 
            -
                      when /\A(?:LIST|LSUB)\z/ni
         | 
| 2159 | 
            +
                      when /\A(?:LIST|LSUB|XLIST)\z/ni
         | 
| 2006 2160 | 
             
                        return list_response
         | 
| 2007 2161 | 
             
                      when /\A(?:QUOTA)\z/ni
         | 
| 2008 2162 | 
             
                        return getquota_response
         | 
| @@ -2053,12 +2207,12 @@ module Net | |
| 2053 2207 | 
             
                    when "FETCH"
         | 
| 2054 2208 | 
             
                      shift_token
         | 
| 2055 2209 | 
             
                      match(T_SPACE)
         | 
| 2056 | 
            -
                      data = FetchData.new(n, msg_att)
         | 
| 2210 | 
            +
                      data = FetchData.new(n, msg_att(n))
         | 
| 2057 2211 | 
             
                      return UntaggedResponse.new(name, data, @str)
         | 
| 2058 2212 | 
             
                    end
         | 
| 2059 2213 | 
             
                  end
         | 
| 2060 2214 |  | 
| 2061 | 
            -
                  def msg_att
         | 
| 2215 | 
            +
                  def msg_att(n)
         | 
| 2062 2216 | 
             
                    match(T_LPAR)
         | 
| 2063 2217 | 
             
                    attr = {}
         | 
| 2064 2218 | 
             
                    while true
         | 
| @@ -2069,7 +2223,7 @@ module Net | |
| 2069 2223 | 
             
                        break
         | 
| 2070 2224 | 
             
                      when T_SPACE
         | 
| 2071 2225 | 
             
                        shift_token
         | 
| 2072 | 
            -
                         | 
| 2226 | 
            +
                        next
         | 
| 2073 2227 | 
             
                      end
         | 
| 2074 2228 | 
             
                      case token.value
         | 
| 2075 2229 | 
             
                      when /\A(?:ENVELOPE)\z/ni
         | 
| @@ -2087,7 +2241,7 @@ module Net | |
| 2087 2241 | 
             
                      when /\A(?:UID)\z/ni
         | 
| 2088 2242 | 
             
                        name, val = uid_data
         | 
| 2089 2243 | 
             
                      else
         | 
| 2090 | 
            -
                        parse_error("unknown attribute `%s'", token.value)
         | 
| 2244 | 
            +
                        parse_error("unknown attribute `%s' for {%d}", token.value, n)
         | 
| 2091 2245 | 
             
                      end
         | 
| 2092 2246 | 
             
                      attr[name] = val
         | 
| 2093 2247 | 
             
                    end
         | 
| @@ -2154,6 +2308,11 @@ module Net | |
| 2154 2308 | 
             
                  def rfc822_text
         | 
| 2155 2309 | 
             
                    token = match(T_ATOM)
         | 
| 2156 2310 | 
             
                    name = token.value.upcase
         | 
| 2311 | 
            +
                    token = lookahead
         | 
| 2312 | 
            +
                    if token.symbol == T_LBRA
         | 
| 2313 | 
            +
                      shift_token
         | 
| 2314 | 
            +
                      match(T_RBRA)
         | 
| 2315 | 
            +
                    end
         | 
| 2157 2316 | 
             
                    match(T_SPACE)
         | 
| 2158 2317 | 
             
                    return name, nstring
         | 
| 2159 2318 | 
             
                  end
         | 
| @@ -2211,6 +2370,8 @@ module Net | |
| 2211 2370 | 
             
                      return body_type_text
         | 
| 2212 2371 | 
             
                    when /\A(?:MESSAGE)\z/ni
         | 
| 2213 2372 | 
             
                      return body_type_msg
         | 
| 2373 | 
            +
                    when /\A(?:ATTACHMENT)\z/ni
         | 
| 2374 | 
            +
                      return body_type_attachment
         | 
| 2214 2375 | 
             
                    else
         | 
| 2215 2376 | 
             
                      return body_type_basic
         | 
| 2216 2377 | 
             
                    end
         | 
| @@ -2249,6 +2410,29 @@ module Net | |
| 2249 2410 | 
             
                    mtype, msubtype = media_type
         | 
| 2250 2411 | 
             
                    match(T_SPACE)
         | 
| 2251 2412 | 
             
                    param, content_id, desc, enc, size = body_fields
         | 
| 2413 | 
            +
             | 
| 2414 | 
            +
                    token = lookahead
         | 
| 2415 | 
            +
                    if token.symbol == T_RPAR
         | 
| 2416 | 
            +
                      # If this is not message/rfc822, we shouldn't apply the RFC822
         | 
| 2417 | 
            +
                      # spec to it.  We should handle anything other than
         | 
| 2418 | 
            +
                      # message/rfc822 using multipart extension data [rfc3501] (i.e.
         | 
| 2419 | 
            +
                      # the data itself won't be returned, we would have to retrieve it
         | 
| 2420 | 
            +
                      # with BODYSTRUCTURE instead of with BODY
         | 
| 2421 | 
            +
             | 
| 2422 | 
            +
                      # Also, sometimes a message/rfc822 is included as a large
         | 
| 2423 | 
            +
                      # attachment instead of having all of the other details
         | 
| 2424 | 
            +
                      # (e.g. attaching a .eml file to an email)
         | 
| 2425 | 
            +
                      if msubtype == "RFC822"
         | 
| 2426 | 
            +
                        return BodyTypeMessage.new(mtype, msubtype, param, content_id,
         | 
| 2427 | 
            +
                                                   desc, enc, size, nil, nil, nil, nil,
         | 
| 2428 | 
            +
                                                   nil, nil, nil)
         | 
| 2429 | 
            +
                      else
         | 
| 2430 | 
            +
                        return BodyTypeExtension.new(mtype, msubtype,
         | 
| 2431 | 
            +
                                                     param, content_id,
         | 
| 2432 | 
            +
                                                     desc, enc, size)
         | 
| 2433 | 
            +
                      end
         | 
| 2434 | 
            +
                    end
         | 
| 2435 | 
            +
             | 
| 2252 2436 | 
             
                    match(T_SPACE)
         | 
| 2253 2437 | 
             
                    env = envelope
         | 
| 2254 2438 | 
             
                    match(T_SPACE)
         | 
| @@ -2263,6 +2447,13 @@ module Net | |
| 2263 2447 | 
             
                                               md5, disposition, language, extension)
         | 
| 2264 2448 | 
             
                  end
         | 
| 2265 2449 |  | 
| 2450 | 
            +
                  def body_type_attachment
         | 
| 2451 | 
            +
                    mtype = case_insensitive_string
         | 
| 2452 | 
            +
                    match(T_SPACE)
         | 
| 2453 | 
            +
                    param = body_fld_param
         | 
| 2454 | 
            +
                    return BodyTypeAttachment.new(mtype, nil, param)
         | 
| 2455 | 
            +
                  end
         | 
| 2456 | 
            +
             | 
| 2266 2457 | 
             
                  def body_type_mpart
         | 
| 2267 2458 | 
             
                    parts = []
         | 
| 2268 2459 | 
             
                    while true
         | 
| @@ -2283,6 +2474,10 @@ module Net | |
| 2283 2474 |  | 
| 2284 2475 | 
             
                  def media_type
         | 
| 2285 2476 | 
             
                    mtype = case_insensitive_string
         | 
| 2477 | 
            +
                    token = lookahead
         | 
| 2478 | 
            +
                    if token.symbol != T_SPACE
         | 
| 2479 | 
            +
                      return mtype, nil
         | 
| 2480 | 
            +
                    end
         | 
| 2286 2481 | 
             
                    match(T_SPACE)
         | 
| 2287 2482 | 
             
                    msubtype = case_insensitive_string
         | 
| 2288 2483 | 
             
                    return mtype, msubtype
         | 
| @@ -2502,7 +2697,7 @@ module Net | |
| 2502 2697 | 
             
                      return '""'
         | 
| 2503 2698 | 
             
                    when /[\x80-\xff\r\n]/n
         | 
| 2504 2699 | 
             
                      # literal
         | 
| 2505 | 
            -
                      return "{" + str. | 
| 2700 | 
            +
                      return "{" + str.bytesize.to_s + "}" + CRLF + str
         | 
| 2506 2701 | 
             
                    when /[(){ \x00-\x1f\x7f%*"\\]/n
         | 
| 2507 2702 | 
             
                      # quoted string
         | 
| 2508 2703 | 
             
                      return '"' + str.gsub(/["\\]/n, "\\\\\\&") + '"'
         | 
| @@ -2627,8 +2822,7 @@ module Net | |
| 2627 2822 | 
             
                        user = astring
         | 
| 2628 2823 | 
             
                        match(T_SPACE)
         | 
| 2629 2824 | 
             
                        rights = astring
         | 
| 2630 | 
            -
                         | 
| 2631 | 
            -
                        data.push(MailboxACLItem.new(user, rights))
         | 
| 2825 | 
            +
                        data.push(MailboxACLItem.new(user, rights, mailbox))
         | 
| 2632 2826 | 
             
                      end
         | 
| 2633 2827 | 
             
                    end
         | 
| 2634 2828 | 
             
                    return UntaggedResponse.new(name, data, @str)
         | 
| @@ -2648,8 +2842,9 @@ module Net | |
| 2648 2842 | 
             
                          break
         | 
| 2649 2843 | 
             
                        when T_SPACE
         | 
| 2650 2844 | 
             
                          shift_token
         | 
| 2845 | 
            +
                        else
         | 
| 2846 | 
            +
                          data.push(number)
         | 
| 2651 2847 | 
             
                        end
         | 
| 2652 | 
            -
                        data.push(number)
         | 
| 2653 2848 | 
             
                      end
         | 
| 2654 2849 | 
             
                    else
         | 
| 2655 2850 | 
             
                      data = []
         | 
| @@ -2687,35 +2882,35 @@ module Net | |
| 2687 2882 | 
             
                  def thread_branch(token)
         | 
| 2688 2883 | 
             
                    rootmember = nil
         | 
| 2689 2884 | 
             
                    lastmember = nil
         | 
| 2690 | 
            -
             | 
| 2885 | 
            +
             | 
| 2691 2886 | 
             
                    while true
         | 
| 2692 2887 | 
             
                      shift_token    # ignore first T_LPAR
         | 
| 2693 2888 | 
             
                      token = lookahead
         | 
| 2694 | 
            -
             | 
| 2889 | 
            +
             | 
| 2695 2890 | 
             
                      case token.symbol
         | 
| 2696 2891 | 
             
                      when T_NUMBER
         | 
| 2697 2892 | 
             
                        # new member
         | 
| 2698 2893 | 
             
                        newmember = ThreadMember.new(number, [])
         | 
| 2699 2894 | 
             
                        if rootmember.nil?
         | 
| 2700 2895 | 
             
                          rootmember = newmember
         | 
| 2701 | 
            -
                        else | 
| 2896 | 
            +
                        else
         | 
| 2702 2897 | 
             
                          lastmember.children << newmember
         | 
| 2703 | 
            -
                        end | 
| 2898 | 
            +
                        end
         | 
| 2704 2899 | 
             
                        lastmember = newmember
         | 
| 2705 | 
            -
                      when T_SPACE | 
| 2706 | 
            -
                        # do nothing | 
| 2900 | 
            +
                      when T_SPACE
         | 
| 2901 | 
            +
                        # do nothing
         | 
| 2707 2902 | 
             
                      when T_LPAR
         | 
| 2708 2903 | 
             
                        if rootmember.nil?
         | 
| 2709 2904 | 
             
                          # dummy member
         | 
| 2710 2905 | 
             
                          lastmember = rootmember = ThreadMember.new(nil, [])
         | 
| 2711 | 
            -
                        end | 
| 2712 | 
            -
             | 
| 2906 | 
            +
                        end
         | 
| 2907 | 
            +
             | 
| 2713 2908 | 
             
                        lastmember.children << thread_branch(token)
         | 
| 2714 2909 | 
             
                      when T_RPAR
         | 
| 2715 | 
            -
                        break | 
| 2716 | 
            -
                      end | 
| 2910 | 
            +
                        break
         | 
| 2911 | 
            +
                      end
         | 
| 2717 2912 | 
             
                    end
         | 
| 2718 | 
            -
             | 
| 2913 | 
            +
             | 
| 2719 2914 | 
             
                    return rootmember
         | 
| 2720 2915 | 
             
                  end
         | 
| 2721 2916 |  | 
| @@ -2758,6 +2953,7 @@ module Net | |
| 2758 2953 | 
             
                        break
         | 
| 2759 2954 | 
             
                      when T_SPACE
         | 
| 2760 2955 | 
             
                        shift_token
         | 
| 2956 | 
            +
                        next
         | 
| 2761 2957 | 
             
                      end
         | 
| 2762 2958 | 
             
                      data.push(atom.upcase)
         | 
| 2763 2959 | 
             
                    end
         | 
| @@ -2906,7 +3102,16 @@ module Net | |
| 2906 3102 | 
             
                    if @str.index(/\(([^)]*)\)/ni, @pos)
         | 
| 2907 3103 | 
             
                      @pos = $~.end(0)
         | 
| 2908 3104 | 
             
                      return $1.scan(FLAG_REGEXP).collect { |flag, atom|
         | 
| 2909 | 
            -
                        atom | 
| 3105 | 
            +
                        if atom
         | 
| 3106 | 
            +
                          atom
         | 
| 3107 | 
            +
                        else
         | 
| 3108 | 
            +
                          symbol = flag.capitalize.untaint.intern
         | 
| 3109 | 
            +
                          @flag_symbols[symbol] = true
         | 
| 3110 | 
            +
                          if @flag_symbols.length > IMAP.max_flag_count
         | 
| 3111 | 
            +
                            raise FlagCountError, "number of flag symbols exceeded"
         | 
| 3112 | 
            +
                          end
         | 
| 3113 | 
            +
                          symbol
         | 
| 3114 | 
            +
                        end
         | 
| 2910 3115 | 
             
                      }
         | 
| 2911 3116 | 
             
                    else
         | 
| 2912 3117 | 
             
                      parse_error("invalid flag list")
         | 
| @@ -3140,7 +3345,7 @@ module Net | |
| 3140 3345 | 
             
                        parse_error("unknown token - %s", $&.dump)
         | 
| 3141 3346 | 
             
                      end
         | 
| 3142 3347 | 
             
                    else
         | 
| 3143 | 
            -
                      parse_error(" | 
| 3348 | 
            +
                      parse_error("invalid @lex_state - %s", @lex_state.inspect)
         | 
| 3144 3349 | 
             
                    end
         | 
| 3145 3350 | 
             
                  end
         | 
| 3146 3351 |  | 
| @@ -3184,6 +3389,22 @@ module Net | |
| 3184 3389 | 
             
                end
         | 
| 3185 3390 | 
             
                add_authenticator "LOGIN", LoginAuthenticator
         | 
| 3186 3391 |  | 
| 3392 | 
            +
                # Authenticator for the "PLAIN" authentication type.  See
         | 
| 3393 | 
            +
                # #authenticate().
         | 
| 3394 | 
            +
                class PlainAuthenticator
         | 
| 3395 | 
            +
                  def process(data)
         | 
| 3396 | 
            +
                    return "\0#{@user}\0#{@password}"
         | 
| 3397 | 
            +
                  end
         | 
| 3398 | 
            +
             | 
| 3399 | 
            +
                  private
         | 
| 3400 | 
            +
             | 
| 3401 | 
            +
                  def initialize(user, password)
         | 
| 3402 | 
            +
                    @user = user
         | 
| 3403 | 
            +
                    @password = password
         | 
| 3404 | 
            +
                  end
         | 
| 3405 | 
            +
                end
         | 
| 3406 | 
            +
                add_authenticator "PLAIN", PlainAuthenticator
         | 
| 3407 | 
            +
             | 
| 3187 3408 | 
             
                # Authenticator for the "CRAM-MD5" authentication type.  See
         | 
| 3188 3409 | 
             
                # #authenticate().
         | 
| 3189 3410 | 
             
                class CramMD5Authenticator
         | 
| @@ -3207,8 +3428,8 @@ module Net | |
| 3207 3428 | 
             
                    k_ipad = key + "\0" * (64 - key.length)
         | 
| 3208 3429 | 
             
                    k_opad = key + "\0" * (64 - key.length)
         | 
| 3209 3430 | 
             
                    for i in 0..63
         | 
| 3210 | 
            -
                      k_ipad[i]  | 
| 3211 | 
            -
                      k_opad[i]  | 
| 3431 | 
            +
                      k_ipad[i] = (k_ipad[i].ord ^ 0x36).chr
         | 
| 3432 | 
            +
                      k_opad[i] = (k_opad[i].ord ^ 0x5c).chr
         | 
| 3212 3433 | 
             
                    end
         | 
| 3213 3434 |  | 
| 3214 3435 | 
             
                    digest = Digest::MD5.digest(k_ipad + text)
         | 
| @@ -3218,6 +3439,106 @@ module Net | |
| 3218 3439 | 
             
                end
         | 
| 3219 3440 | 
             
                add_authenticator "CRAM-MD5", CramMD5Authenticator
         | 
| 3220 3441 |  | 
| 3442 | 
            +
                # Authenticator for the "DIGEST-MD5" authentication type.  See
         | 
| 3443 | 
            +
                # #authenticate().
         | 
| 3444 | 
            +
                class DigestMD5Authenticator
         | 
| 3445 | 
            +
                  def process(challenge)
         | 
| 3446 | 
            +
                    case @stage
         | 
| 3447 | 
            +
                    when STAGE_ONE
         | 
| 3448 | 
            +
                      @stage = STAGE_TWO
         | 
| 3449 | 
            +
                      sparams = {}
         | 
| 3450 | 
            +
                      c = StringScanner.new(challenge)
         | 
| 3451 | 
            +
                      while c.scan(/(?:\s*,)?\s*(\w+)=("(?:[^\\"]+|\\.)*"|[^,]+)\s*/)
         | 
| 3452 | 
            +
                        k, v = c[1], c[2]
         | 
| 3453 | 
            +
                        if v =~ /^"(.*)"$/
         | 
| 3454 | 
            +
                          v = $1
         | 
| 3455 | 
            +
                          if v =~ /,/
         | 
| 3456 | 
            +
                            v = v.split(',')
         | 
| 3457 | 
            +
                          end
         | 
| 3458 | 
            +
                        end
         | 
| 3459 | 
            +
                        sparams[k] = v
         | 
| 3460 | 
            +
                      end
         | 
| 3461 | 
            +
             | 
| 3462 | 
            +
                      raise DataFormatError, "Bad Challenge: '#{challenge}'" unless c.rest.size == 0
         | 
| 3463 | 
            +
                      raise Error, "Server does not support auth (qop = #{sparams['qop'].join(',')})" unless sparams['qop'].include?("auth")
         | 
| 3464 | 
            +
             | 
| 3465 | 
            +
                      response = {
         | 
| 3466 | 
            +
                        :nonce => sparams['nonce'],
         | 
| 3467 | 
            +
                        :username => @user,
         | 
| 3468 | 
            +
                        :realm => sparams['realm'],
         | 
| 3469 | 
            +
                        :cnonce => Digest::MD5.hexdigest("%.15f:%.15f:%d" % [Time.now.to_f, rand, Process.pid.to_s]),
         | 
| 3470 | 
            +
                        :'digest-uri' => 'imap/' + sparams['realm'],
         | 
| 3471 | 
            +
                        :qop => 'auth',
         | 
| 3472 | 
            +
                        :maxbuf => 65535,
         | 
| 3473 | 
            +
                        :nc => "%08d" % nc(sparams['nonce']),
         | 
| 3474 | 
            +
                        :charset => sparams['charset'],
         | 
| 3475 | 
            +
                      }
         | 
| 3476 | 
            +
             | 
| 3477 | 
            +
                      response[:authzid] = @authname unless @authname.nil?
         | 
| 3478 | 
            +
             | 
| 3479 | 
            +
                      # now, the real thing
         | 
| 3480 | 
            +
                      a0 = Digest::MD5.digest( [ response.values_at(:username, :realm), @password ].join(':') )
         | 
| 3481 | 
            +
             | 
| 3482 | 
            +
                      a1 = [ a0, response.values_at(:nonce,:cnonce) ].join(':')
         | 
| 3483 | 
            +
                      a1 << ':' + response[:authzid] unless response[:authzid].nil?
         | 
| 3484 | 
            +
             | 
| 3485 | 
            +
                      a2 = "AUTHENTICATE:" + response[:'digest-uri']
         | 
| 3486 | 
            +
                      a2 << ":00000000000000000000000000000000" if response[:qop] and response[:qop] =~ /^auth-(?:conf|int)$/
         | 
| 3487 | 
            +
             | 
| 3488 | 
            +
                      response[:response] = Digest::MD5.hexdigest(
         | 
| 3489 | 
            +
                        [
         | 
| 3490 | 
            +
                         Digest::MD5.hexdigest(a1),
         | 
| 3491 | 
            +
                         response.values_at(:nonce, :nc, :cnonce, :qop),
         | 
| 3492 | 
            +
                         Digest::MD5.hexdigest(a2)
         | 
| 3493 | 
            +
                        ].join(':')
         | 
| 3494 | 
            +
                      )
         | 
| 3495 | 
            +
             | 
| 3496 | 
            +
                      return response.keys.map {|key| qdval(key.to_s, response[key]) }.join(',')
         | 
| 3497 | 
            +
                    when STAGE_TWO
         | 
| 3498 | 
            +
                      @stage = nil
         | 
| 3499 | 
            +
                      # if at the second stage, return an empty string
         | 
| 3500 | 
            +
                      if challenge =~ /rspauth=/
         | 
| 3501 | 
            +
                        return ''
         | 
| 3502 | 
            +
                      else
         | 
| 3503 | 
            +
                        raise ResponseParseError, challenge
         | 
| 3504 | 
            +
                      end
         | 
| 3505 | 
            +
                    else
         | 
| 3506 | 
            +
                      raise ResponseParseError, challenge
         | 
| 3507 | 
            +
                    end
         | 
| 3508 | 
            +
                  end
         | 
| 3509 | 
            +
             | 
| 3510 | 
            +
                  def initialize(user, password, authname = nil)
         | 
| 3511 | 
            +
                    @user, @password, @authname = user, password, authname
         | 
| 3512 | 
            +
                    @nc, @stage = {}, STAGE_ONE
         | 
| 3513 | 
            +
                  end
         | 
| 3514 | 
            +
             | 
| 3515 | 
            +
                  private
         | 
| 3516 | 
            +
             | 
| 3517 | 
            +
                  STAGE_ONE = :stage_one
         | 
| 3518 | 
            +
                  STAGE_TWO = :stage_two
         | 
| 3519 | 
            +
             | 
| 3520 | 
            +
                  def nc(nonce)
         | 
| 3521 | 
            +
                    if @nc.has_key? nonce
         | 
| 3522 | 
            +
                      @nc[nonce] = @nc[nonce] + 1
         | 
| 3523 | 
            +
                    else
         | 
| 3524 | 
            +
                      @nc[nonce] = 1
         | 
| 3525 | 
            +
                    end
         | 
| 3526 | 
            +
                    return @nc[nonce]
         | 
| 3527 | 
            +
                  end
         | 
| 3528 | 
            +
             | 
| 3529 | 
            +
                  # some responses need quoting
         | 
| 3530 | 
            +
                  def qdval(k, v)
         | 
| 3531 | 
            +
                    return if k.nil? or v.nil?
         | 
| 3532 | 
            +
                    if %w"username authzid realm nonce cnonce digest-uri qop".include? k
         | 
| 3533 | 
            +
                      v.gsub!(/([\\"])/, "\\\1")
         | 
| 3534 | 
            +
                      return '%s="%s"' % [k, v]
         | 
| 3535 | 
            +
                    else
         | 
| 3536 | 
            +
                      return '%s=%s' % [k, v]
         | 
| 3537 | 
            +
                    end
         | 
| 3538 | 
            +
                  end
         | 
| 3539 | 
            +
                end
         | 
| 3540 | 
            +
                add_authenticator "DIGEST-MD5", DigestMD5Authenticator
         | 
| 3541 | 
            +
             | 
| 3221 3542 | 
             
                # Superclass of IMAP errors.
         | 
| 3222 3543 | 
             
                class Error < StandardError
         | 
| 3223 3544 | 
             
                end
         | 
| @@ -3233,6 +3554,16 @@ module Net | |
| 3233 3554 | 
             
                # Superclass of all errors used to encapsulate "fail" responses
         | 
| 3234 3555 | 
             
                # from the server.
         | 
| 3235 3556 | 
             
                class ResponseError < Error
         | 
| 3557 | 
            +
             | 
| 3558 | 
            +
                  # The response that caused this error
         | 
| 3559 | 
            +
                  attr_accessor :response
         | 
| 3560 | 
            +
             | 
| 3561 | 
            +
                  def initialize(response)
         | 
| 3562 | 
            +
                    @response = response
         | 
| 3563 | 
            +
             | 
| 3564 | 
            +
                    super @response.data.text
         | 
| 3565 | 
            +
                  end
         | 
| 3566 | 
            +
             | 
| 3236 3567 | 
             
                end
         | 
| 3237 3568 |  | 
| 3238 3569 | 
             
                # Error raised upon a "NO" response from the server, indicating
         | 
| @@ -3246,11 +3577,15 @@ module Net | |
| 3246 3577 | 
             
                class BadResponseError < ResponseError
         | 
| 3247 3578 | 
             
                end
         | 
| 3248 3579 |  | 
| 3249 | 
            -
                # Error raised upon a "BYE" response from the server, indicating | 
| 3580 | 
            +
                # Error raised upon a "BYE" response from the server, indicating
         | 
| 3250 3581 | 
             
                # that the client is not being allowed to login, or has been timed
         | 
| 3251 3582 | 
             
                # out due to inactivity.
         | 
| 3252 3583 | 
             
                class ByeResponseError < ResponseError
         | 
| 3253 3584 | 
             
                end
         | 
| 3585 | 
            +
             | 
| 3586 | 
            +
                # Error raised when too many flags are interned to symbols.
         | 
| 3587 | 
            +
                class FlagCountError < Error
         | 
| 3588 | 
            +
                end
         | 
| 3254 3589 | 
             
              end
         | 
| 3255 3590 | 
             
            end
         | 
| 3256 3591 |  | 
| @@ -3263,27 +3598,44 @@ if __FILE__ == $0 | |
| 3263 3598 | 
             
              $user = ENV["USER"] || ENV["LOGNAME"]
         | 
| 3264 3599 | 
             
              $auth = "login"
         | 
| 3265 3600 | 
             
              $ssl = false
         | 
| 3601 | 
            +
              $starttls = false
         | 
| 3266 3602 |  | 
| 3267 3603 | 
             
              def usage
         | 
| 3268 | 
            -
                 | 
| 3604 | 
            +
                <<EOF
         | 
| 3269 3605 | 
             
            usage: #{$0} [options] <host>
         | 
| 3270 3606 |  | 
| 3271 3607 | 
             
              --help                        print this message
         | 
| 3272 3608 | 
             
              --port=PORT                   specifies port
         | 
| 3273 3609 | 
             
              --user=USER                   specifies user
         | 
| 3274 3610 | 
             
              --auth=AUTH                   specifies auth type
         | 
| 3611 | 
            +
              --starttls                    use starttls
         | 
| 3275 3612 | 
             
              --ssl                         use ssl
         | 
| 3276 3613 | 
             
            EOF
         | 
| 3277 3614 | 
             
              end
         | 
| 3278 3615 |  | 
| 3616 | 
            +
              begin
         | 
| 3617 | 
            +
                require 'io/console'
         | 
| 3618 | 
            +
              rescue LoadError
         | 
| 3619 | 
            +
                def _noecho(&block)
         | 
| 3620 | 
            +
                  system("stty", "-echo")
         | 
| 3621 | 
            +
                  begin
         | 
| 3622 | 
            +
                    yield STDIN
         | 
| 3623 | 
            +
                  ensure
         | 
| 3624 | 
            +
                    system("stty", "echo")
         | 
| 3625 | 
            +
                  end
         | 
| 3626 | 
            +
                end
         | 
| 3627 | 
            +
              else
         | 
| 3628 | 
            +
                def _noecho(&block)
         | 
| 3629 | 
            +
                  STDIN.noecho(&block)
         | 
| 3630 | 
            +
                end
         | 
| 3631 | 
            +
              end
         | 
| 3632 | 
            +
             | 
| 3279 3633 | 
             
              def get_password
         | 
| 3280 3634 | 
             
                print "password: "
         | 
| 3281 | 
            -
                system("stty", "-echo")
         | 
| 3282 3635 | 
             
                begin
         | 
| 3283 | 
            -
                  return gets. | 
| 3636 | 
            +
                  return _noecho(&:gets).chomp
         | 
| 3284 3637 | 
             
                ensure
         | 
| 3285 | 
            -
                   | 
| 3286 | 
            -
                  print "\n"
         | 
| 3638 | 
            +
                  puts
         | 
| 3287 3639 | 
             
                end
         | 
| 3288 3640 | 
             
              end
         | 
| 3289 3641 |  | 
| @@ -3302,6 +3654,7 @@ EOF | |
| 3302 3654 | 
             
                                 ['--port', GetoptLong::REQUIRED_ARGUMENT],
         | 
| 3303 3655 | 
             
                                 ['--user', GetoptLong::REQUIRED_ARGUMENT],
         | 
| 3304 3656 | 
             
                                 ['--auth', GetoptLong::REQUIRED_ARGUMENT],
         | 
| 3657 | 
            +
                                 ['--starttls', GetoptLong::NO_ARGUMENT],
         | 
| 3305 3658 | 
             
                                 ['--ssl', GetoptLong::NO_ARGUMENT])
         | 
| 3306 3659 | 
             
              begin
         | 
| 3307 3660 | 
             
                parser.each_option do |name, arg|
         | 
| @@ -3314,28 +3667,30 @@ EOF | |
| 3314 3667 | 
             
                    $auth = arg
         | 
| 3315 3668 | 
             
                  when "--ssl"
         | 
| 3316 3669 | 
             
                    $ssl = true
         | 
| 3670 | 
            +
                  when "--starttls"
         | 
| 3671 | 
            +
                    $starttls = true
         | 
| 3317 3672 | 
             
                  when "--debug"
         | 
| 3318 3673 | 
             
                    Net::IMAP.debug = true
         | 
| 3319 3674 | 
             
                  when "--help"
         | 
| 3320 3675 | 
             
                    usage
         | 
| 3321 | 
            -
                    exit | 
| 3676 | 
            +
                    exit
         | 
| 3322 3677 | 
             
                  end
         | 
| 3323 3678 | 
             
                end
         | 
| 3324 3679 | 
             
              rescue
         | 
| 3325 | 
            -
                usage
         | 
| 3326 | 
            -
                exit(1)
         | 
| 3680 | 
            +
                abort usage
         | 
| 3327 3681 | 
             
              end
         | 
| 3328 3682 |  | 
| 3329 3683 | 
             
              $host = ARGV.shift
         | 
| 3330 3684 | 
             
              unless $host
         | 
| 3331 | 
            -
                usage
         | 
| 3332 | 
            -
                exit(1)
         | 
| 3685 | 
            +
                abort usage
         | 
| 3333 3686 | 
             
              end
         | 
| 3334 | 
            -
             | 
| 3335 | 
            -
             | 
| 3336 | 
            -
              imap = Net::IMAP.new($host, $port, $ssl)
         | 
| 3687 | 
            +
             | 
| 3688 | 
            +
              imap = Net::IMAP.new($host, :port => $port, :ssl => $ssl)
         | 
| 3337 3689 | 
             
              begin
         | 
| 3338 | 
            -
                 | 
| 3690 | 
            +
                imap.starttls if $starttls
         | 
| 3691 | 
            +
                class << password = method(:get_password)
         | 
| 3692 | 
            +
                  alias to_str call
         | 
| 3693 | 
            +
                end
         | 
| 3339 3694 | 
             
                imap.authenticate($auth, $user, password)
         | 
| 3340 3695 | 
             
                while true
         | 
| 3341 3696 | 
             
                  cmd, *args = get_command
         |