Results 1 to 5 of 5

Thread: Restore mail to another user's mailbox

  1. #1
    Join Date
    Jan 2006
    Location
    Lafayette, LA
    Posts
    81
    Rep Power
    9

    Default Restore mail to another user's mailbox

    I have a situation where a user account left the company, but management wants that user's email to be placed in a subfolder of another mailbox. For example, We want the contents of jsmith's Zimbra mailbox to be placed into a folder named jsmith in jdoe's account.

    Is there a good way of going about this?

    I thought restoring from Backups into jdoe's folder would work, but apparently you can only restore into either the original mailbox or into a new one. The only route I can think of is to log into the old user's mailbox with the Exchange connector (or IMAP client or some such), do a full export, then log into the destination user's mailbox and restore into a folder. It just seems like there should be a quicker way of doing it

  2. #2
    Join Date
    Aug 2005
    Posts
    1,433
    Rep Power
    12

    Default restore & imapsync

    Restore into a new mailbox, then use imapsync to copy the restored messages to the existing user's subdirectory? I believe that imapsync supports a target folder...
    Bugzilla - Wiki - Downloads - Before posting... Search!

  3. #3
    Join Date
    Nov 2005
    Location
    London, ON
    Posts
    255
    Rep Power
    9

    Default

    I'm not sure of the backup/restore option, but IMAP sync may be a good way to go.

  4. #4
    Join Date
    Jun 2007
    Location
    Athabasca, Alberta, Canada
    Posts
    8
    Rep Power
    8

    Default

    I ran into a situation where a service recipient had deleted their Zimbra sent folder. Zimbra NE allows me to restore the user’s account to another account (restored_username) just fine but then what do I do to get the mail folder over to the other (production) account?

    As suggested, imapsync is one option. It won't do tags though.

    To fix my problem, I wrote something in perl to copy entire folders to another account. It's limited by a zmmailbox limitation of 1000 messages but you can use the move option and run it more than once. It's also a little slow so I implemented forking to do some of the grunt work in parallel.

    For what it's worth, here is how I am copying folders to other mailboxes:
    Code:
    #!/usr/bin/perl
    
    # Name:      zimbracopyfolder
    # Purpose:   The original intent was to help users out when they cry 
    #            "Help! I deleted all of my Sent items!" and the like.
    # Version:   0.88 (alpha)
    # Author:    Troy Adams (troy { at } troyadams { dot } ca)
    # Date:      Fri Dec 21 12:50:33 MST 2007
    # License:   GPL-3
    # Usage:     zimbracopyfolder --help
    # Resources: http://www.zimbra.com/forums/administrators/10393-solved-how-use-zmmailbox-show-original.html
    #            http://www.zimbra.com/forums/administrators/5065-restore-mail-another-users-mailbox.html
    #            http://blog.troyadams.ca/?p=3
    # Warning:   No warranty!  This is program is for academic analysis only. 
    #            Read the code yourself!
    # 
    # Example:   
    #            Step 1) restore from zimbra backup
    #               # zmrestore --createAccount --prefix restored_ \
    #                           --restoreFullBackupOnly --account test@mydomain.ca
    #            Step 2) copy the required folder from the restored_ account to the production account:
    #               # zimbracopyfolder --source restored_test@mydomain.ca:/Sent \
    #                              --destination test@mydomain.ca:/restore_Sent \
    #                              --noverify --verbose --forks 16
    #  Limitations:
    #            It seems that, due to a limitation of zmmailbox, this program can
    #            only handle folders with a maximum of 1000 messages.
    #
    # Log: zimbracopyfolder,v
    #
    # Revision 1.1  2007/10/29 15:39:21  troy
    # Initial revision
    # 
    
    # benchmarks (tested in the Athabasca University data center):
    #   (1) 19 minutes for 129 messages with NUM_FORKS=8;  6.78 msgs/minute
    #   (2) 43 minutes for 185 messages with NUM_FORKS=16; 4.30 msgs/minute
    #   (3)  6 minutes for  36 messages with NUM_FORKS=1;  6    msgs/minute
    #   bypass extra shell call to $zmMAILBOX by calling java direct
    #   (4) 5m08s      for  29 messages with NUM_FORKS=1;  5.64 msgs/minute
    #   (5) 1m59s      for  16 messages with NUM_FORKS=1;  8.07 msgs/minute
    #   (6) 1m50s      for  16 messages with NUM_FORKS=4;  8.73 msgs/minute
    #   (7) 1m45s      for  16 messages with NUM_FORKS=8;  9.14 msgs/minute
    #   (8) 1m20s      for  16 messages with NUM_FORKS=16; 12.0 msgs/minute
    #   (9) 3m08s      for  32 messages with NUM_FORKS=16; 10.2 msgs/minute
    #   back to calling /opt/zimbra/bin/zmmailbox (no more bypass)
    #  (10) 3m08s      for  32 messages with NUM_FORKS=16; 10.2 msgs/minute
    #   best we have so far is 12.0 msgs/minute; that isn't too good!
    #   I modified the thread queue handler to shift off the top instead of
    #   pop off the bottom - silly me.  Now we get:
    #  (11) 3m36s      for  49 messages with NUM_FORKS=16; 13.6 msgs/minute
    #
    
    
    # Plan:
    #   (1) aquire and verify cmd-line args such as source and destination
    #   (2) split out and verify src/dst components
    #   (3) enumerate source target contents
    #   (4) construct destination target
    #   (5) test destination target for content
    #   (6) export->import full messages
    #   (7) test destination target again for content
    
    # Errors/features that we don't yet code for:
    #   (1)   ERROR: zclient.CLIENT_ERROR (unknown tag: 71)
    #         We need to build support for on-the-fly tag creation?
    #   (2)   we can only do 1000 msgs at a time; to do a folder with
    #         more than 1000 msgs then just use the --move option 
    #         and keep executing until all the msgs are moved.
    #   (3)   the hostname portion of the source and destination are
    #         for future implementation of cross-host copying
    #
    
    use Env;
    our $id=0;          # don't think we need this globally
    my $NUM_FORKS=1;
    my $preservetags=0;
    
    my $i = 0;
    my $TEMP_FILE = "/tmp/zimbracopyfoldertmp.$$";
    my $TEMP_FILE2 = "/tmp/zimbracopyfoldertmp-metadata.$$";
    my $EXEC_FILE = "/tmp/zimbracopyfolder.execute.$$";
    my $MAX_SEARCH = 1000;                           # seems like $zmMAILBOX will only return 1000 hits at most
    
    #my $zmMAILBOX="/opt/zimbra/bin/zmjava com.zimbra.cs.zclient.ZMailboxUtil";
    my $zmMAILBOX="/opt/zimbra/bin/zmmailbox";
    
    sub quit
    {
      if (-f $TEMP_FILE) { unlink $TEMP_FILE or die "ERROR: cannot remove '$TEMP_FILE'.\n" }
      if (-f $EXEC_FILE) { unlink $EXEC_FILE or die "ERROR: cannot remove '$EXEC_FILE'.\n" }
      exit shift;
    }
    
    sub giveup
    {
      print STDERR "@_\n";
      quit 1
    }
    
    
    sub execute 
    {
      my @exec_cmd = @_;
      push @exec_cmd, ">$EXEC_FILE";
      my $x = "@exec_cmd";
      my @system = ($x);
      my @output = ();
    
      print "executing '$x'\n" unless $DEBUG<2;
      if ($commandlog)
      {
        open  CMDLOG, ">>$commandlog";
        print CMDLOG  "$x\n";
        close CMDLOG;
      }
    
      system($x) == 0 or return 1;
      open EXEC_TMP, "$EXEC_FILE" or giveup "ERROR: failed to retrieve execution output\n";
      while (<EXEC_TMP>)
      {
        push @output, $_;
      }
      close EXEC_TMP;
    
      unlink "$EXEC_FILE" or giveup "ERROR: failed to cleanup execution output\n"; 
    
      return (0, @output);
    }
    
    sub usage
    {
      print "$0 --source username\@host:folderpath --destination username\@host:folderpath\n";
      print "            --move         Move the data after the copy (optional).\n";
      print "            --noverify     Shut off some verification (optional).\n";
      print "            --forks num    The number of parallel copy processes.\n";
      print "            --preservetags Preserve the message tags.\n";
      print "            --verbose      Be more noisy.\n";
      print "\n";
      print "$0 does not currently support copying across hosts but that is fine.\n";
      print "\n";
      print "example usage:\n";
      print "  $0 --source restored_username\@mydomain.com:/Sent --destination username\@mydomain.com:/Sent --noverify --verbose --forks 16\n";
      print "\n";
      print "Warning:   No warranty!  Read the code yourself!\n";
      print "\n";
      print "\n";
    }
    
    # (1) aquire and verify command-line options
    use Getopt::Long;
    GetOptions("verbose!"=>\$verbose,            # optional flag verbose
               "source=s"=>\$source,             # source is mandatory
               "destination=s"=>\$destination,   # destination is mandatory
               "commandlog:s"=>\$commandlog,     # optional command log file
               "help!"=>\$help,                  # option help
               "forks:i"=>\$forks,               # optional number of forks
               "preservetags!"=>\$preservetags,  # optional flag preservetags
               "noverify!"=>\$noverify,          # optional flag noverify
               "move!"=>\$move);                 # optional flag move
    
    if ($move) { print "WARNING: move enabled! source contents will be deleted upon successful copy!\n"; sleep 3 }
    if ($ARGV[0])
    {
      print "I didn't understand the following command-line options:\n";
      foreach (@ARGV) 
      {
        print "  $_\n";
      }
    }
    
    if ($forks > 1) { $NUM_FORKS = $forks }
    
    if ($help) { usage ; quit 0 }
    print "source:      $source\n" unless !$verbose;
    print "destination: $destination\n" unless !$verbose;
    
    
    # (2) split out and verify src/dst components
    (my $source_user, my $source_host, my $source_path) = split /[\@\:]/, $source;
    (my $destination_user, my $destination_host, my $destination_path) = split /[\@\:]/, $destination;
    if ($source_user eq $destination_user) { $preservetags=1; }
    
    print STDERR "DEBUG: $source_user, $source_host, $source_path\n" unless !$DEBUG;
    print STDERR "DEBUG: $destination_user, $destination_host, $destination_path\n" unless !$DEBUG;
    
    
    # (3) enumerate source target contents
    ($code, @output) = execute "$zmMAILBOX -z -m $source_user search -l $MAX_SEARCH -t message 'in:\"$source_path\"' 2>&1";
    $code == 0 or giveup "ERROR: enumerate source target contents failure";
    $i = 0; # source target message counter
    foreach (@output)
    {
      next unless (/  mess   /);
      $i++;
      #//s/^.*\. //;
      s/^.{1,4}\.\s+([\d ]{1,6}) /$1 /;
      push @source_id_list, (split / +/)[0];
    }
    print "zimbracopyfolder: source target contains $i ";
    print "or more " unless $i <1000;
    print "messages\n";
    if ($i == $MAX_SEARCH)
    {
      print "WARNING: It seems that, due to a limitation of zmmailbox, this program can\n";
      print "WARNING: only handle folders with a maximum of 1000 messages.\n"
    }
    
    # (4) construct destination target
    ($code, @output) = execute "$zmMAILBOX -z -m $destination_user getFolder $destination_path >/dev/null 2>&1";
    if ($code != 0) 
    { 
      ($code, @output) = execute "$zmMAILBOX -z -m $destination_user createFolder $destination_path";
    }
    $code == 0 or giveup "ERROR: construct destination target failure";
    
    # (5) test destination target for content
    ($code, @output) = execute "$zmMAILBOX -z -m $destination_user search -l 10 -t message 'in:\"$destination_path\"' 2>&1";
    $code == 0 or giveup "ERROR: test destination target for content failure";
    $i = 0; # destination target message counter
    foreach (@output)
    {
      next unless /  mess   /;    #/
      $i++;
      push @destination_list, $_;
    }
    if ($i>0)
    {
      print STDERR "ERROR: destination target contains messages!\n";
      foreach (@destination_list)
      {
        print STDERR "$_" unless $DEBUG <2;
      }
      quit 1 unless $noverify;
    }
    
    # (6) export->import full messages
    print "zimbracopyfolder: copying...\n";
    my $num_queued=0;
    my $num_dequeued=0;
    foreach $id (@source_id_list)
    {
      print " FORK QUEUE: \n@children\n" unless !$DEBUG;
    
      # (6.0) fork off up to $NUM_FORKS forks
       if ($#children+1 >= $NUM_FORKS)
          {
            # hold up, we got too many forks!
            $child = shift @children;
              { 
                print "   BLOCKING  -- $pid\n" unless !$DEBUG; 
                waitpid($child, 0);       # Hold off on further execution until this child exits
                $num_dequeued++;
                print "   DEQUEUED  <- $pid\n" unless !$DEBUG; 
                print "   DEQUEUED  $num_dequeued of ",$#source_id_list+1,"\n" unless !$verbose; 
                print " FORK QUEUE: \n@children\n" unless !$DEBUG;
              }
            #print "PUSH -> $pid\n";
          }
      print "   FORKED:   $pid\n" unless !$DEBUG;
      my $pid = fork();
      if ($pid)
        {
          # begin parent code
          # just make record and keep on moving
          push(@children, $pid);
          $num_queued++;
          print "   QUEUED   -> $pid to handle ($id)\n" unless !$DEBUG;
          print "   QUEUED   $num_queued of ",$#source_id_list+1,"\n" unless !$verbose;
          # end is parent code
        }
      else
        {
          # begin child code (the forked proc)
          $EXEC_FILE .= ".$$";
          print "   copy message ID='$id'; exec file = '$EXEC_FILE'\n" unless !$DEBUG;
    
          # (6.1) export content
          ($code, @output) = execute "$zmMAILBOX -z -m $source_user getMessage $id";
          if ($code != 0)
          {
            print STDERR "ERROR: export message failure:\n";
            foreach (@output) { print; }
            quit 1;
          }
          open TMPMSG, ">$TEMP_FILE.$$";
          foreach (@output) { print TMPMSG }
          close TMPMSG;
        
          # (6.2) export metadata
          ($code, @output) = execute "$zmMAILBOX -z -m $source_user getMessage --verbose $id";
          if ($code != 0)
          {
            print STDERR "ERROR: export metadata failure:\n";
            foreach (@output) { print; }
            quit 1;
          }
    
          my $metadata_date="";
          my $metadata_tags="";
          foreach (@output) 
            { 
              next unless /tags\":|receivedDate:/;
              chomp;       # trailing newline
              s/^\s+//;    # leading whitespace
              s/,$//;      # trailing comma
              if (/"tags":/) { ($nothing,$metadata_tags) = split / / }
              if (/receivedDate:/) { ($nothing,$metadata_date) = split / / }
              #print
            }
          if ($metadata_tags =~ /null/ || !$preservetags ) { $metadata_tags="" }
        
          # (6.3) import content and metadata
          if ($metadata_tags && $metadata_date) 
            {
              ($code, @output) = execute "$zmMAILBOX -z -m $destination_user addMessage --date $metadata_date --tags $metadata_tags $destination_path $TEMP_FILE.$$";
            }
          else
            {
              ($code, @output) = execute "$zmMAILBOX -z -m $destination_user addMessage --date $metadata_date $destination_path $TEMP_FILE.$$";
            }
                  unlink "$TEMP_FILE.$$" or print "ERROR: failed to cleanup temporary message file\n";
                  if ($code != 0)
                  {
                    print STDERR "ERROR: import messages failure:\n";
                    foreach (@output) { print; }
            quit 1;
          }
          if ($move)
          {
            ($code, @output) = execute "$zmMAILBOX -z -m $source_user deleteMessage $id";
            if ($code != 0)
            {
              print STDERR "ERROR: delete messages failure:\n";
              foreach (@output) { print; }
              quit 1;
            }
          }
          exit 0
        }
    }
    
    foreach $child (@children) 
              { 
                print "   BLOCKING  -- $pid\n" unless !$DEBUG; 
                waitpid($child, 0);       # Hold off on further execution until this child exits
                print "   DEQUEUED  <- $pid\n" unless !$DEBUG; 
                print " FORK QUEUE: \n@children\n" unless !$DEBUG;
              }
    print "zimbracopyfolder: done copying.\n";
    
    # (7) test destination target again for content
    ($code, @output) = execute "$zmMAILBOX -z -m $destination_user search -l $MAX_SEARCH -t message 'in:\"$destination_path\"' 2>&1";
    $code == 0 or giveup "ERROR: test destination target again for content failure";
    $i = 0; # destination target message counter
    foreach (@output)
    {
      next unless /  mess   /;    #/
      $i++;
      push @destination_list, $_;
    }
    if ($i>0)
    {
      print "zimbracopyfolder: destination target contains $i messages:\n";
      foreach (@destination_list) { print unless $DEBUG <2 } 
    }
    else
    {
      print STDERR "ERROR: destination target contains $i messages!\n";
      quit 1;
    }
    
    quit 0;
    Last edited by troy; 01-16-2008 at 03:07 PM.

  5. #5
    Join Date
    Dec 2006
    Posts
    184
    Rep Power
    8

    Default

    Nice... will definetly give it a shot...

    Now can someone make an admin zimlet for it ?

Similar Threads

  1. Problems with port 25
    By yogiman in forum Installation
    Replies: 57
    Last Post: 06-13-2011, 01:55 PM
  2. Replies: 7
    Last Post: 02-03-2011, 06:01 AM
  3. fresh install down may be due to tomcat
    By gon in forum Installation
    Replies: 10
    Last Post: 07-25-2007, 08:09 AM
  4. DynDNS and Zimbra
    By afterwego in forum Installation
    Replies: 30
    Last Post: 04-01-2007, 03:34 PM
  5. receiveing mail
    By maybethistime in forum Administrators
    Replies: 15
    Last Post: 12-09-2005, 03:55 PM

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •