Zimbra offers Open Source email server software and shared calendar for Linux and the Mac
 
Go Back   Zimbra - Forums > Zimbra Collaboration Suite > Administrators

Welcome to the Zimbra - Forums!
Welcome, if you would like to post a comment please register. We also encourage you to explore all things Zimbra with our team and members of the community.

Reply
 
LinkBack Thread Tools Display Modes
  #1 (permalink)  
Old 10-23-2006, 08:47 AM
zaf zaf is offline
Partner (VAR/HSP)
 
Posts: 71
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
Reply With Quote
  #2 (permalink)  
Old 10-23-2006, 09:42 AM
Zimbra Employee
 
Posts: 1,434
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!
Reply With Quote
  #3 (permalink)  
Old 10-23-2006, 09:44 AM
Elite Member & Volunteer
 
Posts: 255
Default

I'm not sure of the backup/restore option, but IMAP sync may be a good way to go.
Reply With Quote
  #4 (permalink)  
Old 01-16-2008, 03:35 PM
Junior Member
 
Posts: 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 04:07 PM.
Reply With Quote
  #5 (permalink)  
Old 02-08-2008, 08:26 AM
Special Member
 
Posts: 105
Default

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

Now can someone make an admin zimlet for it ?
Reply With Quote
Reply


Thread Tools
Display Modes


Similar Threads

Why Join?

Registering let's you ask questions, makes it easier to search, displays any files attached to posts, and notifies you about replies.

Zimbrablog.com




 

Search Engine Optimization by vBSEO 3.1.0