larch 1.1.0.dev.20100120 → 1.1.0.dev.20100206
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.
- data/HISTORY +3 -0
 - data/README.rdoc +14 -13
 - data/bin/larch +4 -2
 - data/lib/larch.rb +44 -19
 - data/lib/larch/config.rb +2 -0
 - data/lib/larch/imap.rb +3 -3
 - data/lib/larch/imap/mailbox.rb +54 -24
 - data/lib/larch/logger.rb +7 -6
 - data/lib/larch/version.rb +2 -2
 - metadata +3 -3
 
    
        data/HISTORY
    CHANGED
    
    | 
         @@ -18,6 +18,9 @@ Version 1.1.0 (git) 
     | 
|
| 
       18 
18 
     | 
    
         
             
              * Folders are now copied recursively by default.
         
     | 
| 
       19 
19 
     | 
    
         
             
              * Progress information is now displayed regularly while scanning large
         
     | 
| 
       20 
20 
     | 
    
         
             
                mailboxes.
         
     | 
| 
      
 21 
     | 
    
         
            +
              * Added --delete option to delete messages from the source after copying them
         
     | 
| 
      
 22 
     | 
    
         
            +
                to the destination, or if they already exist at the destination.
         
     | 
| 
      
 23 
     | 
    
         
            +
              * Added --expunge option to expunge deleted messages from the source.
         
     | 
| 
       21 
24 
     | 
    
         
             
              * Added --sync-flags option to synchronize message flags (like Seen, Flagged,
         
     | 
| 
       22 
25 
     | 
    
         
             
                etc.) from the source server to the destination server for messages that
         
     | 
| 
       23 
26 
     | 
    
         
             
                already exist on the destination.
         
     | 
    
        data/README.rdoc
    CHANGED
    
    | 
         @@ -10,7 +10,7 @@ accounts. 
     | 
|
| 
       10 
10 
     | 
    
         | 
| 
       11 
11 
     | 
    
         
             
            *Author*::    Ryan Grove (mailto:ryan@wonko.com)
         
     | 
| 
       12 
12 
     | 
    
         
             
            *Version*::   1.1.0 (git)
         
     | 
| 
       13 
     | 
    
         
            -
            *Copyright*:: Copyright (c)  
     | 
| 
      
 13 
     | 
    
         
            +
            *Copyright*:: Copyright (c) 2010 Ryan Grove. All rights reserved.
         
     | 
| 
       14 
14 
     | 
    
         
             
            *License*::   GPL 2.0 (http://opensource.org/licenses/gpl-2.0.php)
         
     | 
| 
       15 
15 
     | 
    
         
             
            *Website*::   http://github.com/rgrove/larch
         
     | 
| 
       16 
16 
     | 
    
         | 
| 
         @@ -28,7 +28,7 @@ Latest development version: 
     | 
|
| 
       28 
28 
     | 
    
         | 
| 
       29 
29 
     | 
    
         
             
              larch [config section] [options]
         
     | 
| 
       30 
30 
     | 
    
         
             
              larch --from <uri> --to <uri> [options]
         
     | 
| 
       31 
     | 
    
         
            -
             
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
       32 
32 
     | 
    
         
             
              Server Options:
         
     | 
| 
       33 
33 
     | 
    
         
             
                       --from, -f <s>:   URI of the source IMAP server
         
     | 
| 
       34 
34 
     | 
    
         
             
                --from-folder, -F <s>:   Source folder to copy from (default: INBOX)
         
     | 
| 
         @@ -39,13 +39,16 @@ Latest development version: 
     | 
|
| 
       39 
39 
     | 
    
         
             
                    --to-pass, -P <s>:   Destination server password (default: prompt)
         
     | 
| 
       40 
40 
     | 
    
         
             
                    --to-user, -U <s>:   Destination server username (default: prompt)
         
     | 
| 
       41 
41 
     | 
    
         | 
| 
       42 
     | 
    
         
            -
               
     | 
| 
      
 42 
     | 
    
         
            +
              Copy Options:
         
     | 
| 
       43 
43 
     | 
    
         
             
                            --all, -a:   Copy all folders recursively
         
     | 
| 
       44 
44 
     | 
    
         
             
                 --all-subscribed, -s:   Copy all subscribed folders recursively
         
     | 
| 
      
 45 
     | 
    
         
            +
                         --delete, -d:   Delete messages from the source after copying
         
     | 
| 
      
 46 
     | 
    
         
            +
                                         them, or if they already exist at the destination
         
     | 
| 
       45 
47 
     | 
    
         
             
                       --exclude <s+>:   List of mailbox names/patterns that shouldn't be
         
     | 
| 
       46 
48 
     | 
    
         
             
                                         copied
         
     | 
| 
       47 
49 
     | 
    
         
             
                   --exclude-file <s>:   Filename containing mailbox names/patterns that
         
     | 
| 
       48 
50 
     | 
    
         
             
                                         shouldn't be copied
         
     | 
| 
      
 51 
     | 
    
         
            +
                        --expunge, -x:   Expunge deleted messages from the source
         
     | 
| 
       49 
52 
     | 
    
         
             
                     --sync-flags, -S:   Sync message flags from the source to the
         
     | 
| 
       50 
53 
     | 
    
         
             
                                         destination for messages that already exist at the
         
     | 
| 
       51 
54 
     | 
    
         
             
                                         destination
         
     | 
| 
         @@ -270,18 +273,16 @@ fairly well-known trick. That said, as with anything tricky, there are caveats. 
     | 
|
| 
       270 
273 
     | 
    
         | 
| 
       271 
274 
     | 
    
         
             
            ==== No hierarchical folders
         
     | 
| 
       272 
275 
     | 
    
         | 
| 
       273 
     | 
    
         
            -
            Similar to Gmail, Yahoo! 
     | 
| 
       274 
     | 
    
         
            -
             
     | 
| 
       275 
     | 
    
         
            -
             
     | 
| 
       276 
     | 
    
         
            -
             
     | 
| 
      
 276 
     | 
    
         
            +
            Similar to Gmail, Yahoo! Mail doesn't allow hierarchical (nested) folders. If
         
     | 
| 
      
 277 
     | 
    
         
            +
            you try to copy a folder hierarchy to Yahoo!, it will work, but you'll end up
         
     | 
| 
      
 278 
     | 
    
         
            +
            with a set of folders named "folder" and "folder.subfolder" rather than seeing
         
     | 
| 
      
 279 
     | 
    
         
            +
            "subfolder" as an actual subfolder of "folder".
         
     | 
| 
       277 
280 
     | 
    
         | 
| 
       278 
281 
     | 
    
         
             
            ==== No custom flags
         
     | 
| 
       279 
282 
     | 
    
         | 
| 
       280 
     | 
    
         
            -
            Yahoo! Mail IMAP doesn't  
     | 
| 
       281 
     | 
    
         
            -
             
     | 
| 
       282 
     | 
    
         
            -
             
     | 
| 
       283 
     | 
    
         
            -
            Also, Larch's <tt>sync-flags</tt> option will not work correctly if you have
         
     | 
| 
       284 
     | 
    
         
            -
            messages with custom flags.
         
     | 
| 
      
 283 
     | 
    
         
            +
            Yahoo! Mail IMAP doesn't support custom message flags, such as the tags and
         
     | 
| 
      
 284 
     | 
    
         
            +
            junk/not junk flags used by Thunderbird. When transferring messages with custom
         
     | 
| 
      
 285 
     | 
    
         
            +
            flags to a Yahoo! Mail IMAP account, the custom flags will be lost.
         
     | 
| 
       285 
286 
     | 
    
         | 
| 
       286 
287 
     | 
    
         
             
            ==== Here there be dragons
         
     | 
| 
       287 
288 
     | 
    
         | 
| 
         @@ -336,7 +337,7 @@ Gray II). 
     | 
|
| 
       336 
337 
     | 
    
         | 
| 
       337 
338 
     | 
    
         
             
            == License
         
     | 
| 
       338 
339 
     | 
    
         | 
| 
       339 
     | 
    
         
            -
            Copyright (c)  
     | 
| 
      
 340 
     | 
    
         
            +
            Copyright (c) 2010 Ryan Grove <ryan@wonko.com>
         
     | 
| 
       340 
341 
     | 
    
         | 
| 
       341 
342 
     | 
    
         
             
            Licensed under the GNU General Public License version 2.0.
         
     | 
| 
       342 
343 
     | 
    
         | 
    
        data/bin/larch
    CHANGED
    
    | 
         @@ -12,7 +12,7 @@ module Larch 
     | 
|
| 
       12 
12 
     | 
    
         
             
              options = Trollop.options do
         
     | 
| 
       13 
13 
     | 
    
         
             
                version "Larch #{APP_VERSION}\n" << APP_COPYRIGHT
         
     | 
| 
       14 
14 
     | 
    
         
             
                banner <<-EOS
         
     | 
| 
       15 
     | 
    
         
            -
            Larch  
     | 
| 
      
 15 
     | 
    
         
            +
            Larch copies messages from one IMAP server to another. Awesomely.
         
     | 
| 
       16 
16 
     | 
    
         | 
| 
       17 
17 
     | 
    
         
             
            Usage:
         
     | 
| 
       18 
18 
     | 
    
         
             
              larch [config section] [options]
         
     | 
| 
         @@ -29,11 +29,13 @@ EOS 
     | 
|
| 
       29 
29 
     | 
    
         
             
                opt :to_pass,     "Destination server password (default: prompt)", :short => '-P', :type => :string
         
     | 
| 
       30 
30 
     | 
    
         
             
                opt :to_user,     "Destination server username (default: prompt)", :short => '-U', :type => :string
         
     | 
| 
       31 
31 
     | 
    
         | 
| 
       32 
     | 
    
         
            -
                text "\ 
     | 
| 
      
 32 
     | 
    
         
            +
                text "\nCopy Options:"
         
     | 
| 
       33 
33 
     | 
    
         
             
                opt :all,              "Copy all folders recursively", :short => '-a'
         
     | 
| 
       34 
34 
     | 
    
         
             
                opt :all_subscribed,   "Copy all subscribed folders recursively", :short => '-s'
         
     | 
| 
      
 35 
     | 
    
         
            +
                opt :delete,           "Delete messages from the source after copying them, or if they already exist at the destination", :short => '-d'
         
     | 
| 
       35 
36 
     | 
    
         
             
                opt :exclude,          "List of mailbox names/patterns that shouldn't be copied", :short => :none, :type => :strings, :multi => true
         
     | 
| 
       36 
37 
     | 
    
         
             
                opt :exclude_file,     "Filename containing mailbox names/patterns that shouldn't be copied", :short => :none, :type => :string
         
     | 
| 
      
 38 
     | 
    
         
            +
                opt :expunge,          "Expunge deleted messages from the source", :short => '-x'
         
     | 
| 
       37 
39 
     | 
    
         
             
                opt :sync_flags,       "Sync message flags from the source to the destination for messages that already exist at the destination", :short => '-S'
         
     | 
| 
       38 
40 
     | 
    
         | 
| 
       39 
41 
     | 
    
         
             
                text "\nGeneral Options:"
         
     | 
    
        data/lib/larch.rb
    CHANGED
    
    | 
         @@ -38,9 +38,10 @@ module Larch 
     | 
|
| 
       38 
38 
     | 
    
         
             
                  Net::IMAP.debug = true if @log.level == :insane
         
     | 
| 
       39 
39 
     | 
    
         | 
| 
       40 
40 
     | 
    
         
             
                  # Stats
         
     | 
| 
       41 
     | 
    
         
            -
                  @copied 
     | 
| 
       42 
     | 
    
         
            -
                  @ 
     | 
| 
       43 
     | 
    
         
            -
                  @ 
     | 
| 
      
 41 
     | 
    
         
            +
                  @copied  = 0
         
     | 
| 
      
 42 
     | 
    
         
            +
                  @deleted = 0
         
     | 
| 
      
 43 
     | 
    
         
            +
                  @failed  = 0
         
     | 
| 
      
 44 
     | 
    
         
            +
                  @total   = 0
         
     | 
| 
       44 
45 
     | 
    
         
             
                end
         
     | 
| 
       45 
46 
     | 
    
         | 
| 
       46 
47 
     | 
    
         
             
                # Recursively copies all messages in all folders from the source to the
         
     | 
| 
         @@ -49,9 +50,10 @@ module Larch 
     | 
|
| 
       49 
50 
     | 
    
         
             
                  raise ArgumentError, "imap_from must be a Larch::IMAP instance" unless imap_from.is_a?(IMAP)
         
     | 
| 
       50 
51 
     | 
    
         
             
                  raise ArgumentError, "imap_to must be a Larch::IMAP instance" unless imap_to.is_a?(IMAP)
         
     | 
| 
       51 
52 
     | 
    
         | 
| 
       52 
     | 
    
         
            -
                  @copied 
     | 
| 
       53 
     | 
    
         
            -
                  @ 
     | 
| 
       54 
     | 
    
         
            -
                  @ 
     | 
| 
      
 53 
     | 
    
         
            +
                  @copied  = 0
         
     | 
| 
      
 54 
     | 
    
         
            +
                  @deleted = 0
         
     | 
| 
      
 55 
     | 
    
         
            +
                  @failed  = 0
         
     | 
| 
      
 56 
     | 
    
         
            +
                  @total   = 0
         
     | 
| 
       55 
57 
     | 
    
         | 
| 
       56 
58 
     | 
    
         
             
                  imap_from.each_mailbox do |mailbox_from|
         
     | 
| 
       57 
59 
     | 
    
         
             
                    next if excluded?(mailbox_from.name)
         
     | 
| 
         @@ -82,9 +84,10 @@ module Larch 
     | 
|
| 
       82 
84 
     | 
    
         
             
                  raise ArgumentError, "imap_from must be a Larch::IMAP instance" unless imap_from.is_a?(IMAP)
         
     | 
| 
       83 
85 
     | 
    
         
             
                  raise ArgumentError, "imap_to must be a Larch::IMAP instance" unless imap_to.is_a?(IMAP)
         
     | 
| 
       84 
86 
     | 
    
         | 
| 
       85 
     | 
    
         
            -
                  @copied 
     | 
| 
       86 
     | 
    
         
            -
                  @ 
     | 
| 
       87 
     | 
    
         
            -
                  @ 
     | 
| 
      
 87 
     | 
    
         
            +
                  @copied  = 0
         
     | 
| 
      
 88 
     | 
    
         
            +
                  @deleted = 0
         
     | 
| 
      
 89 
     | 
    
         
            +
                  @failed  = 0
         
     | 
| 
      
 90 
     | 
    
         
            +
                  @total   = 0
         
     | 
| 
       88 
91 
     | 
    
         | 
| 
       89 
92 
     | 
    
         
             
                  mailbox_from = imap_from.mailbox(imap_from.uri_mailbox || 'INBOX')
         
     | 
| 
       90 
93 
     | 
    
         
             
                  mailbox_to   = imap_to.mailbox(imap_to.uri_mailbox || 'INBOX')
         
     | 
| 
         @@ -144,7 +147,7 @@ module Larch 
     | 
|
| 
       144 
147 
     | 
    
         
             
                end
         
     | 
| 
       145 
148 
     | 
    
         | 
| 
       146 
149 
     | 
    
         
             
                def summary
         
     | 
| 
       147 
     | 
    
         
            -
                  @log.info "#{@copied} message(s) copied, #{@failed} failed, #{@ 
     | 
| 
      
 150 
     | 
    
         
            +
                  @log.info "#{@copied} message(s) copied, #{@failed} failed, #{@deleted} deleted out of #{@total} total"
         
     | 
| 
       148 
151 
     | 
    
         
             
                end
         
     | 
| 
       149 
152 
     | 
    
         | 
| 
       150 
153 
     | 
    
         | 
| 
         @@ -182,19 +185,26 @@ module Larch 
     | 
|
| 
       182 
185 
     | 
    
         | 
| 
       183 
186 
     | 
    
         
             
                  mailbox_from.each_db_message do |from_db_message|
         
     | 
| 
       184 
187 
     | 
    
         
             
                    guid = from_db_message.guid
         
     | 
| 
      
 188 
     | 
    
         
            +
                    uid  = from_db_message.uid
         
     | 
| 
       185 
189 
     | 
    
         | 
| 
       186 
190 
     | 
    
         
             
                    if mailbox_to.has_guid?(guid)
         
     | 
| 
       187 
     | 
    
         
            -
                      next unless @config['sync_flags']
         
     | 
| 
       188 
     | 
    
         
            -
             
     | 
| 
       189 
191 
     | 
    
         
             
                      begin
         
     | 
| 
       190 
     | 
    
         
            -
                         
     | 
| 
      
 192 
     | 
    
         
            +
                        if @config['sync_flags']
         
     | 
| 
      
 193 
     | 
    
         
            +
                          to_db_message = mailbox_to.fetch_db_message(guid)
         
     | 
| 
      
 194 
     | 
    
         
            +
             
     | 
| 
      
 195 
     | 
    
         
            +
                          if to_db_message.flags != from_db_message.flags
         
     | 
| 
      
 196 
     | 
    
         
            +
                            new_flags = from_db_message.flags_str
         
     | 
| 
      
 197 
     | 
    
         
            +
                            new_flags = '(none)' if new_flags.empty?
         
     | 
| 
       191 
198 
     | 
    
         | 
| 
       192 
     | 
    
         
            -
             
     | 
| 
       193 
     | 
    
         
            -
             
     | 
| 
       194 
     | 
    
         
            -
                           
     | 
| 
      
 199 
     | 
    
         
            +
                            @log.info "[>] syncing flags: uid #{uid}: #{new_flags}"
         
     | 
| 
      
 200 
     | 
    
         
            +
                            mailbox_to.set_flags(guid, from_db_message.flags)
         
     | 
| 
      
 201 
     | 
    
         
            +
                          end
         
     | 
| 
      
 202 
     | 
    
         
            +
                        end
         
     | 
| 
       195 
203 
     | 
    
         | 
| 
       196 
     | 
    
         
            -
             
     | 
| 
       197 
     | 
    
         
            -
                           
     | 
| 
      
 204 
     | 
    
         
            +
                        if @config['delete'] && !from_db_message.flags.include?(:Deleted)
         
     | 
| 
      
 205 
     | 
    
         
            +
                          @log.info "[<] deleting uid #{uid} (already exists at destination)"
         
     | 
| 
      
 206 
     | 
    
         
            +
                          mailbox_from.set_flags(guid, [:Deleted], true)
         
     | 
| 
      
 207 
     | 
    
         
            +
                          @deleted += 1
         
     | 
| 
       198 
208 
     | 
    
         
             
                        end
         
     | 
| 
       199 
209 
     | 
    
         
             
                      rescue Larch::IMAP::Error => e
         
     | 
| 
       200 
210 
     | 
    
         
             
                        @log.error e.message
         
     | 
| 
         @@ -213,17 +223,32 @@ module Larch 
     | 
|
| 
       213 
223 
     | 
    
         
             
                        from = '?'
         
     | 
| 
       214 
224 
     | 
    
         
             
                      end
         
     | 
| 
       215 
225 
     | 
    
         | 
| 
       216 
     | 
    
         
            -
                      @log.info "copying: #{from} - #{msg.envelope.subject}"
         
     | 
| 
      
 226 
     | 
    
         
            +
                      @log.info "[>] copying uid #{uid}: #{from} - #{msg.envelope.subject}"
         
     | 
| 
       217 
227 
     | 
    
         | 
| 
       218 
228 
     | 
    
         
             
                      mailbox_to << msg
         
     | 
| 
       219 
229 
     | 
    
         
             
                      @copied += 1
         
     | 
| 
       220 
230 
     | 
    
         | 
| 
      
 231 
     | 
    
         
            +
                      if @config['delete']
         
     | 
| 
      
 232 
     | 
    
         
            +
                        @log.info "[<] deleting uid #{uid}"
         
     | 
| 
      
 233 
     | 
    
         
            +
                        mailbox_from.set_flags(guid, [:Deleted], true)
         
     | 
| 
      
 234 
     | 
    
         
            +
                        @deleted += 1
         
     | 
| 
      
 235 
     | 
    
         
            +
                      end
         
     | 
| 
      
 236 
     | 
    
         
            +
             
     | 
| 
       221 
237 
     | 
    
         
             
                    rescue Larch::IMAP::Error => e
         
     | 
| 
       222 
238 
     | 
    
         
             
                      @failed += 1
         
     | 
| 
       223 
239 
     | 
    
         
             
                      @log.error e.message
         
     | 
| 
       224 
240 
     | 
    
         
             
                      next
         
     | 
| 
       225 
241 
     | 
    
         
             
                    end
         
     | 
| 
       226 
242 
     | 
    
         
             
                  end
         
     | 
| 
      
 243 
     | 
    
         
            +
             
     | 
| 
      
 244 
     | 
    
         
            +
                  if @config['expunge']
         
     | 
| 
      
 245 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 246 
     | 
    
         
            +
                      @log.debug "[<] expunging deleted messages"
         
     | 
| 
      
 247 
     | 
    
         
            +
                      mailbox_from.expunge
         
     | 
| 
      
 248 
     | 
    
         
            +
                    rescue Larch::IMAP::Error => e
         
     | 
| 
      
 249 
     | 
    
         
            +
                      @log.error e.message
         
     | 
| 
      
 250 
     | 
    
         
            +
                    end
         
     | 
| 
      
 251 
     | 
    
         
            +
                  end
         
     | 
| 
       227 
252 
     | 
    
         
             
                end
         
     | 
| 
       228 
253 
     | 
    
         | 
| 
       229 
254 
     | 
    
         
             
                def db_maintenance
         
     | 
    
        data/lib/larch/config.rb
    CHANGED
    
    | 
         @@ -8,9 +8,11 @@ class Config 
     | 
|
| 
       8 
8 
     | 
    
         
             
                'all-subscribed'   => false,
         
     | 
| 
       9 
9 
     | 
    
         
             
                'config'           => File.join('~', '.larch', 'config.yaml'),
         
     | 
| 
       10 
10 
     | 
    
         
             
                'database'         => File.join('~', '.larch', 'larch.db'),
         
     | 
| 
      
 11 
     | 
    
         
            +
                'delete'           => false,
         
     | 
| 
       11 
12 
     | 
    
         
             
                'dry-run'          => false,
         
     | 
| 
       12 
13 
     | 
    
         
             
                'exclude'          => [],
         
     | 
| 
       13 
14 
     | 
    
         
             
                'exclude-file'     => nil,
         
     | 
| 
      
 15 
     | 
    
         
            +
                'expunge'          => false,
         
     | 
| 
       14 
16 
     | 
    
         
             
                'from'             => nil,
         
     | 
| 
       15 
17 
     | 
    
         
             
                'from-folder'      => nil, # actually INBOX; see validate()
         
     | 
| 
       16 
18 
     | 
    
         
             
                'from-pass'        => nil,
         
     | 
    
        data/lib/larch/imap.rb
    CHANGED
    
    | 
         @@ -198,7 +198,7 @@ class IMAP 
     | 
|
| 
       198 
198 
     | 
    
         | 
| 
       199 
199 
     | 
    
         
             
                  raise unless (retries += 1) <= @options[:max_retries]
         
     | 
| 
       200 
200 
     | 
    
         | 
| 
       201 
     | 
    
         
            -
                   
     | 
| 
      
 201 
     | 
    
         
            +
                  warning "#{e.class.name}: #{e.message} (reconnecting)"
         
     | 
| 
       202 
202 
     | 
    
         | 
| 
       203 
203 
     | 
    
         
             
                  reset
         
     | 
| 
       204 
204 
     | 
    
         
             
                  sleep 1 * retries
         
     | 
| 
         @@ -211,7 +211,7 @@ class IMAP 
     | 
|
| 
       211 
211 
     | 
    
         | 
| 
       212 
212 
     | 
    
         
             
                  raise unless (retries += 1) <= @options[:max_retries]
         
     | 
| 
       213 
213 
     | 
    
         | 
| 
       214 
     | 
    
         
            -
                   
     | 
| 
      
 214 
     | 
    
         
            +
                  warning "#{e.class.name}: #{e.message} (will retry)"
         
     | 
| 
       215 
215 
     | 
    
         | 
| 
       216 
216 
     | 
    
         
             
                  sleep 1 * retries
         
     | 
| 
       217 
217 
     | 
    
         
             
                  retry
         
     | 
| 
         @@ -292,7 +292,7 @@ class IMAP 
     | 
|
| 
       292 
292 
     | 
    
         
             
                  # verification errors.
         
     | 
| 
       293 
293 
     | 
    
         
             
                  raise if e.is_a?(OpenSSL::SSL::SSLError) && e.message =~ /certificate verify failed/
         
     | 
| 
       294 
294 
     | 
    
         | 
| 
       295 
     | 
    
         
            -
                   
     | 
| 
      
 295 
     | 
    
         
            +
                  warning "#{e.class.name}: #{e.message} (will retry)"
         
     | 
| 
       296 
296 
     | 
    
         | 
| 
       297 
297 
     | 
    
         
             
                  reset
         
     | 
| 
       298 
298 
     | 
    
         
             
                  sleep 1 * retries
         
     | 
    
        data/lib/larch/imap/mailbox.rb
    CHANGED
    
    | 
         @@ -74,21 +74,8 @@ class Mailbox 
     | 
|
| 
       74 
74 
     | 
    
         
             
                    raise Larch::IMAP::Error, "mailbox cannot contain messages: #{@name}"
         
     | 
| 
       75 
75 
     | 
    
         
             
                  end
         
     | 
| 
       76 
76 
     | 
    
         | 
| 
       77 
     | 
    
         
            -
                  flags = message.flags.dup
         
     | 
| 
       78 
     | 
    
         
            -
             
     | 
| 
       79 
     | 
    
         
            -
                  # Don't set any flags that aren't supported on the destination mailbox.
         
     | 
| 
       80 
     | 
    
         
            -
                  flags.delete_if do |flag|
         
     | 
| 
       81 
     | 
    
         
            -
                    # The \Recent flag is read-only, so we shouldn't try to set it.
         
     | 
| 
       82 
     | 
    
         
            -
                    next true if flag == :Recent
         
     | 
| 
       83 
     | 
    
         
            -
             
     | 
| 
       84 
     | 
    
         
            -
                    unless @flags.include?(flag) || @perm_flags.include?(:*) || @perm_flags.include?(flag)
         
     | 
| 
       85 
     | 
    
         
            -
                      info "flag not supported on destination: #{flag}"
         
     | 
| 
       86 
     | 
    
         
            -
                      true
         
     | 
| 
       87 
     | 
    
         
            -
                    end
         
     | 
| 
       88 
     | 
    
         
            -
                  end
         
     | 
| 
       89 
     | 
    
         
            -
             
     | 
| 
       90 
77 
     | 
    
         
             
                  debug "appending message: #{message.guid}"
         
     | 
| 
       91 
     | 
    
         
            -
                  @imap.conn.append(@name_utf7, message.rfc822, flags, message.internaldate) unless @imap.options[:dry_run]
         
     | 
| 
      
 78 
     | 
    
         
            +
                  @imap.conn.append(@name_utf7, message.rfc822, get_supported_flags(message.flags), message.internaldate) unless @imap.options[:dry_run]
         
     | 
| 
       92 
79 
     | 
    
         
             
                end
         
     | 
| 
       93 
80 
     | 
    
         | 
| 
       94 
81 
     | 
    
         
             
                true
         
     | 
| 
         @@ -114,6 +101,19 @@ class Mailbox 
     | 
|
| 
       114 
101 
     | 
    
         
             
                mailboxes.each {|mb| yield mb }
         
     | 
| 
       115 
102 
     | 
    
         
             
              end
         
     | 
| 
       116 
103 
     | 
    
         | 
| 
      
 104 
     | 
    
         
            +
              # Expunges this mailbox, permanently removing all messages with the \Deleted
         
     | 
| 
      
 105 
     | 
    
         
            +
              # flag.
         
     | 
| 
      
 106 
     | 
    
         
            +
              def expunge
         
     | 
| 
      
 107 
     | 
    
         
            +
                return false unless imap_select
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
                @imap.safely do
         
     | 
| 
      
 110 
     | 
    
         
            +
                  debug "expunging deleted messages"
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
                  @last_scan = nil
         
     | 
| 
      
 113 
     | 
    
         
            +
                  @imap.conn.expunge unless @imap.options[:dry_run]
         
     | 
| 
      
 114 
     | 
    
         
            +
                end
         
     | 
| 
      
 115 
     | 
    
         
            +
              end
         
     | 
| 
      
 116 
     | 
    
         
            +
             
     | 
| 
       117 
117 
     | 
    
         
             
              # Returns a Larch::IMAP::Message struct representing the message with the
         
     | 
| 
       118 
118 
     | 
    
         
             
              # specified Larch _guid_, or +nil+ if the specified guid was not found in this
         
     | 
| 
       119 
119 
     | 
    
         
             
              # mailbox.
         
     | 
| 
         @@ -121,7 +121,7 @@ class Mailbox 
     | 
|
| 
       121 
121 
     | 
    
         
             
                scan
         
     | 
| 
       122 
122 
     | 
    
         | 
| 
       123 
123 
     | 
    
         
             
                unless db_message = fetch_db_message(guid)
         
     | 
| 
       124 
     | 
    
         
            -
                   
     | 
| 
      
 124 
     | 
    
         
            +
                  warning "message not found in local db: #{guid}"
         
     | 
| 
       125 
125 
     | 
    
         
             
                  return nil
         
     | 
| 
       126 
126 
     | 
    
         
             
                end
         
     | 
| 
       127 
127 
     | 
    
         | 
| 
         @@ -135,7 +135,7 @@ class Mailbox 
     | 
|
| 
       135 
135 
     | 
    
         
             
                      data.attr['FLAGS'], Time.parse(data.attr['INTERNALDATE']))
         
     | 
| 
       136 
136 
     | 
    
         
             
                end
         
     | 
| 
       137 
137 
     | 
    
         | 
| 
       138 
     | 
    
         
            -
                 
     | 
| 
      
 138 
     | 
    
         
            +
                warning "message not found on server: #{guid}"
         
     | 
| 
       139 
139 
     | 
    
         
             
                return nil
         
     | 
| 
       140 
140 
     | 
    
         
             
              end
         
     | 
| 
       141 
141 
     | 
    
         
             
              alias [] fetch
         
     | 
| 
         @@ -242,16 +242,28 @@ class Mailbox 
     | 
|
| 
       242 
242 
     | 
    
         
             
                return
         
     | 
| 
       243 
243 
     | 
    
         
             
              end
         
     | 
| 
       244 
244 
     | 
    
         | 
| 
       245 
     | 
    
         
            -
              # Sets the IMAP flags for the message specified by _guid_ 
     | 
| 
       246 
     | 
    
         
            -
              #  
     | 
| 
       247 
     | 
    
         
            -
              # 
     | 
| 
       248 
     | 
    
         
            -
               
     | 
| 
      
 245 
     | 
    
         
            +
              # Sets the IMAP flags for the message specified by _guid_. _flags_ should be
         
     | 
| 
      
 246 
     | 
    
         
            +
              # an array of symbols for standard flags, strings for custom flags.
         
     | 
| 
      
 247 
     | 
    
         
            +
              #
         
     | 
| 
      
 248 
     | 
    
         
            +
              # If _merge_ is +true+, the specified flags will be merged with the message's
         
     | 
| 
      
 249 
     | 
    
         
            +
              # existing flags. Otherwise, all existing flags will be cleared and replaced
         
     | 
| 
      
 250 
     | 
    
         
            +
              # with the specified flags.
         
     | 
| 
      
 251 
     | 
    
         
            +
              #
         
     | 
| 
      
 252 
     | 
    
         
            +
              # Note that the :Recent flag cannot be manually set or removed.
         
     | 
| 
      
 253 
     | 
    
         
            +
              #
         
     | 
| 
      
 254 
     | 
    
         
            +
              # Returns +true+ on success, +false+ on failure.
         
     | 
| 
      
 255 
     | 
    
         
            +
              def set_flags(guid, flags, merge = false)
         
     | 
| 
       249 
256 
     | 
    
         
             
                raise ArgumentError, "flags must be an Array" unless flags.is_a?(Array)
         
     | 
| 
       250 
257 
     | 
    
         | 
| 
       251 
     | 
    
         
            -
                db_message = fetch_db_message(guid)
         
     | 
| 
       252 
     | 
    
         
            -
             
     | 
| 
      
 258 
     | 
    
         
            +
                return false unless db_message = fetch_db_message(guid)
         
     | 
| 
      
 259 
     | 
    
         
            +
             
     | 
| 
      
 260 
     | 
    
         
            +
                merged_flags    = merge ? (db_message.flags + flags).uniq : flags
         
     | 
| 
      
 261 
     | 
    
         
            +
                supported_flags = get_supported_flags(merged_flags)
         
     | 
| 
       253 
262 
     | 
    
         | 
| 
       254 
     | 
    
         
            -
                 
     | 
| 
      
 263 
     | 
    
         
            +
                return true if db_message.flags == supported_flags
         
     | 
| 
      
 264 
     | 
    
         
            +
             
     | 
| 
      
 265 
     | 
    
         
            +
                return false if !imap_select
         
     | 
| 
      
 266 
     | 
    
         
            +
                @imap.safely { @imap.conn.uid_store(db_message.uid, 'FLAGS.SILENT', supported_flags) } unless @imap.options[:dry_run]
         
     | 
| 
       255 
267 
     | 
    
         | 
| 
       256 
268 
     | 
    
         
             
                true
         
     | 
| 
       257 
269 
     | 
    
         
             
              end
         
     | 
| 
         @@ -317,6 +329,24 @@ class Mailbox 
     | 
|
| 
       317 
329 
     | 
    
         
             
                end
         
     | 
| 
       318 
330 
     | 
    
         
             
              end
         
     | 
| 
       319 
331 
     | 
    
         | 
| 
      
 332 
     | 
    
         
            +
              # Returns only the flags from the specified _flags_ array that can be set in
         
     | 
| 
      
 333 
     | 
    
         
            +
              # this mailbox. Emits a warning message for any unsupported flags.
         
     | 
| 
      
 334 
     | 
    
         
            +
              def get_supported_flags(flags)
         
     | 
| 
      
 335 
     | 
    
         
            +
                supported_flags = flags.dup
         
     | 
| 
      
 336 
     | 
    
         
            +
             
     | 
| 
      
 337 
     | 
    
         
            +
                supported_flags.delete_if do |flag|
         
     | 
| 
      
 338 
     | 
    
         
            +
                  # The \Recent flag is read-only, so we shouldn't try to set it.
         
     | 
| 
      
 339 
     | 
    
         
            +
                  next true if flag == :Recent
         
     | 
| 
      
 340 
     | 
    
         
            +
             
     | 
| 
      
 341 
     | 
    
         
            +
                  unless @flags.include?(flag) || @perm_flags.include?(:*) || @perm_flags.include?(flag)
         
     | 
| 
      
 342 
     | 
    
         
            +
                    warning "flag not supported on destination: #{flag}"
         
     | 
| 
      
 343 
     | 
    
         
            +
                    true
         
     | 
| 
      
 344 
     | 
    
         
            +
                  end
         
     | 
| 
      
 345 
     | 
    
         
            +
                end
         
     | 
| 
      
 346 
     | 
    
         
            +
             
     | 
| 
      
 347 
     | 
    
         
            +
                supported_flags
         
     | 
| 
      
 348 
     | 
    
         
            +
              end
         
     | 
| 
      
 349 
     | 
    
         
            +
             
     | 
| 
       320 
350 
     | 
    
         
             
              # Fetches the latest flags from the server for the specified range of message
         
     | 
| 
       321 
351 
     | 
    
         
             
              # UIDs.
         
     | 
| 
       322 
352 
     | 
    
         
             
              def fetch_flags(flag_range)
         
     | 
| 
         @@ -563,7 +593,7 @@ class Mailbox 
     | 
|
| 
       563 
593 
     | 
    
         
             
                      raise unless e.message == 'Some messages could not be FETCHed (Failure)'
         
     | 
| 
       564 
594 
     | 
    
         | 
| 
       565 
595 
     | 
    
         
             
                      # Workaround for stupid Gmail shenanigans.
         
     | 
| 
       566 
     | 
    
         
            -
                       
     | 
| 
      
 596 
     | 
    
         
            +
                      warning "Gmail error: '#{e.message}'; continuing anyway"
         
     | 
| 
       567 
597 
     | 
    
         
             
                    end
         
     | 
| 
       568 
598 
     | 
    
         | 
| 
       569 
599 
     | 
    
         
             
                    next data
         
     | 
    
        data/lib/larch/logger.rb
    CHANGED
    
    | 
         @@ -4,12 +4,13 @@ class Logger 
     | 
|
| 
       4 
4 
     | 
    
         
             
              attr_reader :level, :output
         
     | 
| 
       5 
5 
     | 
    
         | 
| 
       6 
6 
     | 
    
         
             
              LEVELS = {
         
     | 
| 
       7 
     | 
    
         
            -
                :fatal 
     | 
| 
       8 
     | 
    
         
            -
                :error 
     | 
| 
       9 
     | 
    
         
            -
                :warn 
     | 
| 
       10 
     | 
    
         
            -
                : 
     | 
| 
       11 
     | 
    
         
            -
                : 
     | 
| 
       12 
     | 
    
         
            -
                : 
     | 
| 
      
 7 
     | 
    
         
            +
                :fatal   => 0,
         
     | 
| 
      
 8 
     | 
    
         
            +
                :error   => 1,
         
     | 
| 
      
 9 
     | 
    
         
            +
                :warn    => 2,
         
     | 
| 
      
 10 
     | 
    
         
            +
                :warning => 2,
         
     | 
| 
      
 11 
     | 
    
         
            +
                :info    => 3,
         
     | 
| 
      
 12 
     | 
    
         
            +
                :debug   => 4,
         
     | 
| 
      
 13 
     | 
    
         
            +
                :insane  => 5
         
     | 
| 
       13 
14 
     | 
    
         
             
              }
         
     | 
| 
       14 
15 
     | 
    
         | 
| 
       15 
16 
     | 
    
         
             
              def initialize(level = :info, output = $stdout)
         
     | 
    
        data/lib/larch/version.rb
    CHANGED
    
    | 
         @@ -1,9 +1,9 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module Larch
         
     | 
| 
       2 
2 
     | 
    
         
             
              APP_NAME      = 'Larch'
         
     | 
| 
       3 
     | 
    
         
            -
              APP_VERSION   = '1.1.0.dev. 
     | 
| 
      
 3 
     | 
    
         
            +
              APP_VERSION   = '1.1.0.dev.20100206'
         
     | 
| 
       4 
4 
     | 
    
         
             
              APP_AUTHOR    = 'Ryan Grove'
         
     | 
| 
       5 
5 
     | 
    
         
             
              APP_EMAIL     = 'ryan@wonko.com'
         
     | 
| 
       6 
6 
     | 
    
         
             
              APP_URL       = 'http://github.com/rgrove/larch/'
         
     | 
| 
       7 
     | 
    
         
            -
              APP_COPYRIGHT = 'Copyright (c)  
     | 
| 
      
 7 
     | 
    
         
            +
              APP_COPYRIGHT = 'Copyright (c) 2010 Ryan Grove <ryan@wonko.com>. All ' <<
         
     | 
| 
       8 
8 
     | 
    
         
             
                              'rights reserved.'
         
     | 
| 
       9 
9 
     | 
    
         
             
            end
         
     | 
    
        metadata
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification 
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: larch
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version 
         
     | 
| 
       4 
     | 
    
         
            -
              version: 1.1.0.dev. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 1.1.0.dev.20100206
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors: 
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Ryan Grove
         
     | 
| 
         @@ -9,7 +9,7 @@ autorequire: 
     | 
|
| 
       9 
9 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       10 
10 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
       12 
     | 
    
         
            -
            date: 2010- 
     | 
| 
      
 12 
     | 
    
         
            +
            date: 2010-02-06 00:00:00 -08:00
         
     | 
| 
       13 
13 
     | 
    
         
             
            default_executable: 
         
     | 
| 
       14 
14 
     | 
    
         
             
            dependencies: 
         
     | 
| 
       15 
15 
     | 
    
         
             
            - !ruby/object:Gem::Dependency 
         
     | 
| 
         @@ -104,6 +104,6 @@ rubyforge_project: 
     | 
|
| 
       104 
104 
     | 
    
         
             
            rubygems_version: 1.3.5
         
     | 
| 
       105 
105 
     | 
    
         
             
            signing_key: 
         
     | 
| 
       106 
106 
     | 
    
         
             
            specification_version: 3
         
     | 
| 
       107 
     | 
    
         
            -
            summary: Larch  
     | 
| 
      
 107 
     | 
    
         
            +
            summary: Larch copies messages from one IMAP server to another. Awesomely.
         
     | 
| 
       108 
108 
     | 
    
         
             
            test_files: []
         
     | 
| 
       109 
109 
     | 
    
         |