/*
* Contains: Cyrus to Dovecot maildir mail migration
* Written by: Michale Dasenbrock
* Copyright: © 2008-2011 Apple Inc., All rights reserved.
*
* To Do:
* - Gather stats for messages migrated
* - Use syslog to log to dovecot info & error files
* - May want to do auto-migration of messages found in mailboxes without cyrus index
*
* Not For Open Source
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <dirent.h>
#include <unistd.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <sys/syslog.h>
#include <getopt.h>
#import <Foundation/NSObject.h>
#include <Foundation/Foundation.h>
#include <Foundation/NSString.h>
#include <OpenDirectory/NSOpenDirectory.h>
#include <OpenDirectory/OpenDirectoryPriv.h>
#include <DirectoryService/DirServicesTypes.h>
#include <DirectoryService/DirServicesConst.h>
#include "cvt_mail.h"
#define VERSION "OS X 2.0"
// -- Function Prototypes -------------------------------------------
int cvt_mail_data ( int argc, char **argv );
int set_user_mail_opts ( int argc, char **argv );
int set_dovecot_keywords ( const char *in_cy_header_path, char *in_dest_path );
const char *map_guid( const char *in_user );
const char *map_userid( const char *in_guid );
NSDictionary *get_mail_attribute ( const char *in_guid );
void rename_mailboxes( const char *g_dest_dir );
void fix_mailboxes ( const char *g_dest_dir );
void fix_message_dates ( NSString *in_dir );
void map_mailbox_to_guid ( const char *in_path, const char *in_user );
void set_migration_flag ( const char *in_guid );
void set_migration_flags ( BOOL in_set );
void log_message ( int in_log_lvl, NSString *in_msg, NSError *in_ns_err );
NSDate *get_message_date( NSString *nsStr_in_path );
NSDictionary *get_alt_data_stores ( int in_print );
void list_auto_forwards( const char *in_guid );
void set_auto_forward( const char *guid, const char *fwd_addr );
void reset_auto_forward( const char *in_guid );
void list_alt_data_stores( const char *in_guid );
void set_alt_data_store( const char *guid, const char *fwd_addr );
void reset_alt_data_stores( const char *in_guid );
void set_alt_data_store_tag ( const char *tag, const char *path );
void reset_alt_data_store_tag ( const char *in_tag );
void set_opts_usage ( int in_exit_code );
// ------------------------------------------------------------------
int g_debug = 0;
long g_count = 0;
long long g_size = 0LL;
char *g_account = NULL;
char *g_cyrus_db_dir = "/var/imap";
char *g_dest_dir = "/Library/Server/Mail/Data/mail";
char *g_user_dir = "/Library/Server/Mail/Data/users";
FILE *g_maildirsize = NULL;
NSAutoreleasePool *gPool = nil;
// Global cyrus spool path, will append "/user"
char g_cyrus_spool_dir[ PATH_MAX + 1 ];
// Global flags
int c_flag = 0;
int e_flag = 0;
int f_flag = 0;
int i_flag = 0;
int j_flag = 0;
int k_flag = 0;
int m_flag = 0;
int r_flag = 0;
int u_flag = 0;
int x_flag = 0;
// Global seen file data
struct s_seen_data *g_seen_file = NULL;
// ------------------------------------------------------------------
//
int main ( int argc, char **argv )
{
gPool = [[NSAutoreleasePool alloc] init];
if ( geteuid() != 0 ) {
fprintf( stdout, " exit(0);
}
if ( strcasestr(argv[0], "cvt_mail_data" ) ) {
cvt_mail_data( argc, argv );
exit(0);
} else if ( strcasestr(argv[0], "set_user_mail_opts" ) ) {
set_user_mail_opts( argc, argv );
exit(0);
}
return( 0 );
[gPool release];
}
int set_user_mail_opts ( int argc, char **argv )
{
char ch;
int opt_index = 0;
int long_val = 0;
char *tag = NULL;
char *path = NULL;
char *user_id = NULL;
char *user_guid = NULL;
char *fwd_addr = NULL;
char *alt_store = NULL;
struct option long_options[] = {
{"alt_store", 1, &long_val, 'a'},
{"auto_fwd", 1, &long_val, 'f'},
{"user_guid", 1, &long_val, 'g'},
{"path", 1, &long_val, 'p'},
{"tag", 1, &long_val, 't'},
{"user_id", 1, &long_val, 'u'},
{0, 0, 0, 0 }
};
if ( argc == 1 )
set_opts_usage(0);
while ((ch = getopt_long(argc, argv, "ha:f:g:p:t:u:", long_options, &opt_index)) != -1) {
switch (ch) {
case 'a':
alt_store = optarg;
break;
case 'f':
fwd_addr = optarg;
break;
case 'g':
user_guid = optarg;
break;
case 'p':
path = optarg;
break;
case 't':
tag = optarg;
break;
case 'u':
user_id = optarg;
break;
case 0:
switch ( long_val ) {
case 'a':
alt_store = optarg;
break;
case 'f':
fwd_addr = optarg;
break;
case 'g':
user_guid = optarg;
break;
case 'p':
path = optarg;
break;
case 't':
tag = optarg;
break;
case 'u':
user_id = optarg;
break;
}
break;
case 'h':
default:
set_opts_usage(0);
}
}
if ( fwd_addr ) {
const char *guid = NULL;
if ( !strcasecmp(fwd_addr, "list") ) {
if ( user_id )
guid = map_guid( user_id );
else if ( user_guid )
guid = user_guid;
list_auto_forwards( guid );
return(0);
} else if ( !strcasecmp(fwd_addr, "reset") ) {
if ( user_id )
guid = map_guid( user_id );
else if ( user_guid )
guid = user_guid;
if ( !guid )
set_opts_usage(1);
reset_auto_forward( guid );
} else if (user_id || user_guid) {
if ( user_id )
guid = map_guid( user_id );
else if ( user_guid )
guid = user_guid;
if ( !guid )
set_opts_usage(1);
set_auto_forward( guid, fwd_addr );
} else
set_opts_usage(1);
} else if ( alt_store ) {
const char *guid = NULL;
if ( !strcasecmp(alt_store, "list") ) {
if ( user_id )
guid = map_guid( user_id );
else if ( user_guid )
guid = user_guid;
list_alt_data_stores( guid );
return(0);
} else if ( !strcasecmp(alt_store, "list-tags") ) {
get_alt_data_stores(1);
} else if ( !strcasecmp(alt_store, "set-tag") ) {
if ( !path || !tag )
set_opts_usage(1);
set_alt_data_store_tag( tag, path );
return(0);
} else if ( !strcasecmp(alt_store, "reset-tag") ) {
if ( !tag )
set_opts_usage(1);
reset_alt_data_store_tag( tag );
return(0);
} else if ( !strcasecmp(alt_store, "reset") ) {
if ( user_id )
guid = map_guid( user_id );
else if ( user_guid )
guid = user_guid;
if ( !guid )
set_opts_usage(1);
reset_alt_data_stores( guid );
} else if (user_id || user_guid) {
if ( user_id )
guid = map_guid( user_id );
else if ( user_guid )
guid = user_guid;
if ( !guid )
set_opts_usage(1);
set_alt_data_store( guid, alt_store );
} else
set_opts_usage(1);
}
return(0);
}
int cvt_mail_data ( int argc, char **argv )
{
int ch;
char *value = NULL;
char *tmp_cyrus_spool_dir = "/var/spool/imap";
gPool = [[NSAutoreleasePool alloc] init];
if ( geteuid() != 0 ) {
fprintf( stdout, " exit(0);
}
while ( (ch = getopt(argc, argv, "cemgva:d:f:j:k:r:s:i:t:u:x:")) != EOF ) {
switch( ch ) {
case 'a':
// User account ID
g_account = optarg;
break;
case 'd':
// Cyrus imap database directory
g_cyrus_db_dir = optarg;
break;
case 'i':
// User account ID
g_account = optarg;
i_flag++;
break;
case 'r':
// User account ID
g_dest_dir = optarg;
r_flag++;
break;
case 's':
// Cyrus imap data spool directory
tmp_cyrus_spool_dir = optarg;
break;
case 't':
// Destination maildir data directory
g_dest_dir = optarg;
break;
case 'u':
// User account ID
g_account = optarg;
u_flag++;
break;
case 'c':
// 'C'opy flag
c_flag = 1;
break;
case 'e':
// D'e'lete flag
e_flag = 1;
break;
case 'm':
// 'M'ove flag
m_flag = 1;
break;
case 'f':
// Fix directory UIDs
g_dest_dir = optarg;
f_flag = 1;
break;
case 'v':
fprintf( stdout, " [gPool release];
exit( 0 );
break;
case 'g':
g_debug++;
break;
case 'j':
value = optarg;
j_flag++;
break;
case 'k':
value = optarg;
k_flag++;
break;
case 'x':
// Fix message received dates
g_dest_dir = optarg;
x_flag = 1;
break;
case '?':
case 'h':
default:
usage( 0 );
}
}
argc -= optind;
argv += optind;
// ------------------------------------------------------------------
// Print GUID for user account
if ( i_flag ) {
if ( g_account != NULL ) {
const char *map = map_guid( g_account );
if ( map != NULL )
fprintf( stdout, " else
fprintf( stdout, "No GUID found for:
[gPool release];
exit( 0 );
} else {
fprintf( stdout, "*** Error: Empty user account ID\n" );
usage(1);
}
}
// ------------------------------------------------------------------
// Print GUID for user account
if ( u_flag ) {
if ( g_account != NULL ) {
const char *map = map_userid( g_account );
if ( map != NULL )
fprintf( stdout, " else
fprintf( stdout, "No user id found for:
[gPool release];
exit( 0 );
} else {
fprintf( stdout, "*** Error: Empty user account ID\n" );
usage(1);
}
}
// ------------------------------------------------------------------
// Rename user directories to associated GUIDs
if ( r_flag ) {
if ( g_dest_dir != NULL ) {
rename_mailboxes( g_dest_dir );
[gPool release];
exit( 0 );
} else {
fprintf( stdout, "*** Error: Empty directory\n" );
usage(1);
}
}
// ------------------------------------------------------------------
// Rename user directories to associated GUIDs
if ( f_flag ) {
if ( g_dest_dir != NULL ) {
fix_mailboxes( g_dest_dir );
[gPool release];
exit( 0 );
} else {
fprintf( stdout, "*** Error: empty directory\n" );
usage(1);
}
}
// ------------------------------------------------------------------
// Set user migration flag
if ( j_flag ) {
if ( !value ) {
fprintf( stdout, "*** Error: missing required argument to \'j\' option\n" );
usage(1);
}
set_migration_flag( value );
[gPool release];
exit( 0 );
}
// ------------------------------------------------------------------
// Reset user migration flags
if ( k_flag ) {
int flag = 0;
if ( !value ) {
fprintf( stdout, "*** Error: missing required argument to \'k\' option\n" );
usage(1);
}
if ( !strcmp( value, "set" ) )
flag = 1;
else if ( strcmp( value, "reset" ) ) {
fprintf( stdout, "*** Error: bad argument to \'k\' option\n" );
usage(1);
}
set_migration_flags( flag );
[gPool release];
exit( 0 );
}
// ------------------------------------------------------------------
// Rename user directories to associated GUIDs
if ( x_flag ) {
if ( g_dest_dir != NULL ) {
fix_message_dates( [NSString stringWithUTF8String: g_dest_dir] );
[gPool release];
exit( 0 );
} else {
fprintf( stdout, "*** Error: Empty directory\n" );
usage(1);
}
}
// ------------------------------------------------------------------
// Do arg verification
// Verify cyrus database path
if ( g_cyrus_db_dir == NULL ) {
fprintf( stdout, "*** Error: Empty cyrus database path\n" );
usage(1);
} else {
if ( verify_path( g_cyrus_db_dir ) != 0 ) {
fprintf( stdout, "*** Error: Invalid cyrus database path: %s\n", g_cyrus_db_dir );
usage(1);
}
}
// Verify cyrus spool path
if ( tmp_cyrus_spool_dir == NULL ) {
fprintf( stdout, "*** Error: Empty cyrus data spool path\n" );
usage(1);
} else {
if ( verify_path( tmp_cyrus_spool_dir ) != 0 ) {
fprintf( stdout, "*** Error: Invalid cyrus database path: %s\n", tmp_cyrus_spool_dir );
usage(1);
}
}
// Verify maildir destination spool path
if ( g_dest_dir == NULL ) {
fprintf( stdout, "*** Error: Empty destination directory\n" );
usage(1);
} else {
if ( verify_path( g_dest_dir ) != 0 ) {
fprintf( stdout, "*** Error: Invalid destination directory: %s\n", g_dest_dir );
usage(1);
}
}
// Verify user account ID
if ( g_account == NULL )
{
fprintf( stdout, "*** Error: Empty user account ID\n" );
usage(1);
} else {
// Verify user path by appending /user/account to spool path
snprintf( g_cyrus_spool_dir, PATH_MAX, "%s/user/%s", tmp_cyrus_spool_dir, g_account );
if ( verify_path( g_cyrus_spool_dir ) != 0 ) {
fprintf( stdout, "*** Error: Could not verify user account path: %s\n", g_cyrus_spool_dir );
usage(1);
}
// Set global spool path to <spool path>/user
snprintf( g_cyrus_spool_dir, PATH_MAX, "%s/user", tmp_cyrus_spool_dir );
}
if ( (c_flag + e_flag + m_flag) == 0 )
c_flag = 1;
else if ( (c_flag + e_flag + m_flag) != 1 ) {
fprintf( stdout, "*** Error: You can only choose of these options [-c, -e or -m]\n" );
usage(1);
}
chdir( g_cyrus_spool_dir );
scan_account( g_cyrus_spool_dir, g_dest_dir, g_account );
free_seen_file();
fprintf( stdout, "-------------\n" );
fprintf( stdout, "- totals for: %s\n", g_account );
fprintf( stdout, " total: messages: %lu size: %llu bytes\n", g_count, g_size );
if ( g_maildirsize != NULL ) {
fclose( g_maildirsize );
g_maildirsize = NULL;
}
if ( g_debug ) {
fprintf( stdout, "Finished migrating user account\n" );
}
return( 0 );
} // main
// ------------------------------------------------------------------
//
void set_opts_usage ( int in_exit_code )
{
if ( in_exit_code )
fprintf( stdout, "Error: Missing required argument\n");
fprintf( stdout, "\nUsage:\n");
fprintf( stdout, " set_user_mail_opts [options]\n");
fprintf( stdout, " set_user_mail_opts -a, --alt_store list\n");
fprintf( stdout, " set_user_mail_opts -a, --alt_store list-tags\n");
fprintf( stdout, " set_user_mail_opts -a, --alt_store set-tag -t, --tag <tag> -p, --path <path>\n");
fprintf( stdout, " set_user_mail_opts -a, --alt_store reset-tag -t, --tag <tag> \n");
fprintf( stdout, " set_user_mail_opts -a, --alt_store <store tag> -u, --user_id <user id>\n");
fprintf( stdout, " set_user_mail_opts -a, --alt_store <store tag> -g, --user_guid <user guid>\n");
fprintf( stdout, " set_user_mail_opts -a, --alt_store reset -u, --user_id <user id>\n");
fprintf( stdout, " set_user_mail_opts -a, --alt_store reset -g, --user_guid <user guid>\n");
fprintf( stdout, " set_user_mail_opts -f, --auto_fwd list\n");
fprintf( stdout, " set_user_mail_opts -f, --auto_fwd <email addr> -u, --user_id <user id>\n");
fprintf( stdout, " set_user_mail_opts -f, --auto_fwd <email addr> -g, --user_guid <user guid>\n");
fprintf( stdout, " set_user_mail_opts -f, --auto_fwd reset -u, --user_id <user id>\n");
fprintf( stdout, " set_user_mail_opts -f, --auto_fwd reset -g, --user_guid <user guid>\n");
fprintf( stdout, "\nOptions:\n");
fprintf( stdout, " -a, --alt_store list list all account with alternate mail store locations set\n");
fprintf( stdout, " -a, --alt_store list-tags list all alternate mail store locations with tags\n");
fprintf( stdout, " -a, --alt_store <store tag> -u, --user_id <user id>\n");
fprintf( stdout, " set <user id> to alternate mail store <store tag>\n");
fprintf( stdout, " -a, --alt_store <store tag> -g, --user_guid <user guid>\n");
fprintf( stdout, " set <user guid> to alternate mail store <store tag>\n");
fprintf( stdout, " -a, --alt_store reset -u, --user_id <user id>\n");
fprintf( stdout, " reset <user id> to default mail store <store tag>\n");
fprintf( stdout, " -a, --alt_store reset -g, --user_guid <user guid>\n");
fprintf( stdout, " reset <user guid> to default mail store <store tag>\n");
fprintf( stdout, " -f, --auto_fwd list list all account with email autoforwarding enabled\n");
fprintf( stdout, " -f, --auto_fwd <email addr> -u, --user_id <user id>\n");
fprintf( stdout, " set <user id> to autoforward to <email addr>\n");
fprintf( stdout, " -f, --auto_fwd <email addr> -g, --user_guid <user guid>\n");
fprintf( stdout, " set <user guid> to autoforward to <email addr>\n");
fprintf( stdout, " -f, --auto_fwd reset -u, --user_id <user id>\n");
fprintf( stdout, " reset <user id> to no email autoforwarding\n");
fprintf( stdout, " -f, --auto_fwd reset -g, --user_guid <user guid>\n");
fprintf( stdout, " reset <user guid> to no email autoforwarding\n");
[gPool release];
exit( in_exit_code );
} // set_opts_usage
// ------------------------------------------------------------------
//
void usage ( int in_exit_code )
{
fprintf( stdout, "\nUsage: cvt_mail_data -a <acct_id> [options]\n");
fprintf( stdout, " Required\n" );
fprintf( stdout, " -a <user account> user ID of account to be migrated\n" );
fprintf( stdout, "\n" );
fprintf( stdout, " Default: (override if necessary)\n" );
fprintf( stdout, " -d <db dir> path to cyrus imap database (default: %s)\n", g_cyrus_db_dir );
fprintf( stdout, " -s <spool dir> path to cyrus imap data spool (default: /var/spool/imap)\n" );
fprintf( stdout, " -t <destination dir> path to dovecot maildir directory (default: %s)\n", g_dest_dir );
fprintf( stdout, "\n" );
fprintf( stdout, " Choose one:\n" );
fprintf( stdout, " -c copy mail messages to new destination leaving original (default)\n" );
fprintf( stdout, " -e copy mail messages to new destination deleting original\n" );
fprintf( stdout, " -m move mail messages to new destination\n" );
fprintf( stdout, "\n" );
fprintf( stdout, " Optional:\n" );
fprintf( stdout, " -g debug mode\n\n" );
fprintf( stdout, " Single Function:\n" );
fprintf( stdout, " -i <user account> print GUID for user account\n" );
fprintf( stdout, " -u <GUID> print user account for GUID\n" );
fprintf( stdout, " -r <dir> rename mailboxes to GUID in directory <dir>\n" );
fprintf( stdout, " -V print version\n" );
fprintf( stdout, " -h print this message\n\n" );
[gPool release];
exit( in_exit_code );
}
// ------------------------------------------------------------------
//
// verify_path ()
int verify_path ( const char *in_path )
{
DIR *dir = NULL;
if ( dir = opendir( in_path ) ) {
closedir( dir );
return( 0 );
}
return( 1 );
} // verify_path
// ------------------------------------------------------------------
// fts_escape ()
static void fts_escape ( char *out_str, const char *in_orig )
{
char *p = out_str;
static const char *hexchars = "0123456789abcdef";
while ( *in_orig != '\0' ) {
unsigned char c = *in_orig;
if ((c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z') ||
(c >= '0' && c <= '9'))
*p++ = c;
else {
*p++ = '%';
*p++ = hexchars[(c >> 4) & 0xf];
*p++ = hexchars[c & 0xf];
}
++in_orig;
}
} // fts_escape
// ------------------------------------------------------------------
// set_fts_update_index_file ()
static void set_fts_update_index_file ( const char * in_src_path, const char *in_dst_path )
{
static char path[PATH_MAX];
static char account[PATH_MAX];
static char mailbox[PATH_MAX];
// get the user ID from the source path
NSString *ns_str = [NSString stringWithCString: in_src_path encoding: NSUTF8StringEncoding];
NSArray *ns_arry = [ns_str componentsSeparatedByCharactersInSet: [NSCharacterSet characterSetWithCharactersInString: @"/"]];
memset( account, 0, PATH_MAX );
fts_escape( account, [[ns_arry objectAtIndex: 0]UTF8String] );
// get the '.' delimited mailbox name
ns_str = [NSString stringWithCString: in_dst_path encoding: NSUTF8StringEncoding];
ns_str = [ns_str stringByTrimmingCharactersInSet: [NSCharacterSet characterSetWithCharactersInString: @"/"]];
ns_arry = [ns_str componentsSeparatedByCharactersInSet: [NSCharacterSet characterSetWithCharactersInString: @"/"]];
memset( mailbox, 0, PATH_MAX );
if ( [ns_arry count] <= 1 )
strlcpy( mailbox, "INBOX", PATH_MAX );
else
fts_escape( mailbox, [[[ns_arry objectAtIndex: 1] stringByTrimmingCharactersInSet: [NSCharacterSet characterSetWithCharactersInString: @"."]] UTF8String] );
snprintf( path, PATH_MAX, "/var/db/dovecot.fts.update/
if ( g_debug )
fprintf( stdout, "creating fts update index file:
int fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0600);
if ( (fd < 0) && (errno != EEXIST) )
fprintf( stdout, "fts: open(} // set_fts_update_index_file
// ------------------------------------------------------------------
// scan_account ()
int scan_account ( char *in_cy_spool_dir, char *in_dst_root, char *in_acct_dir )
{
DIR *p_dir = NULL;
struct dirent *dir_entry;
struct stat stat_buf;
static int is_root_mailbox = 1;
static char src_path [ PATH_MAX + 1 ] = "";
static char dst_path [ PATH_MAX + 1 ] = "";
static char full_path [ PATH_MAX + 1 ] = "";
static char src_mb_path [ PATH_MAX + 1 ] = "";
char *cc = NULL;
int rc = 0;
const char *guid_map = NULL;
const char *dst_acct = NULL;
if ( is_root_mailbox ) {
if ( verify_path( in_acct_dir ) != 0 ) {
fprintf( stdout, "*** Warning: unable to verify mailbox path: [gPool release];
exit(1);
}
char seen_path[ PATH_MAX + 1 ];
snprintf( seen_path, PATH_MAX, "
read_seen_file( seen_path );
if ( !g_seen_file ) {
if ( g_debug ) {
fprintf( stdout, "*** Warning: No seen file found for for: }
}
// Drop in if it's a directory
if ( (stat( in_acct_dir, &stat_buf ) == 0) &&
((stat_buf.st_mode & S_IFMT) == S_IFDIR) )
{
// Add '/' if we now have a path name
if ( strlen( src_path ) != 0 )
strcat( src_path, "/" );
// Append source mailbox to path
strcat( src_path, in_acct_dir );
// Mailboxes are separate with a '.'
if ( strlen( dst_path ) != 0 )
strcat( dst_path, "." );
// Append destination mailbox name, either account ID or GUID if it exists
if ( is_root_mailbox && (guid_map = map_guid( in_acct_dir )) != NULL )
dst_acct = guid_map;
else
dst_acct = in_acct_dir;
strcat( dst_path, dst_acct );
// Append a '/' to the base mailbox name
// We should only hit this once per account
if ( !strcmp( dst_path, dst_acct) )
strcat( dst_path, "/" );
src_mb_path[0]='\0';
strcat( src_mb_path, in_cy_spool_dir );
strcat( src_mb_path, "/" );
strcat( src_mb_path, src_path );
// Make the full path
snprintf( full_path, PATH_MAX, "
// create maildir directories
create_maildir_dirs( in_dst_root, dst_path, is_root_mailbox );
// Migrate cyrus quota info
if ( is_root_mailbox )
set_quota( in_dst_root, dst_path );
// Set subscribed mailboxes
if ( is_root_mailbox )
set_subscribe( in_dst_root, dst_path );
// copy and/or migrate the mailfiles
migrate_mail( src_mb_path, full_path, is_root_mailbox );
// create update index file for background processing
set_fts_update_index_file( src_path, dst_path );
if ( is_root_mailbox )
is_root_mailbox = 0;
// Scan mailboxes
if ( chdir( in_acct_dir ) == 0 ) {
if ( p_dir = opendir( "." ) ) {
// Recursively scan across all mailboxes
while( (dir_entry = readdir(p_dir)) ) {
// Skip the '.' & '..' directories
if ( strcmp( dir_entry->d_name, "." ) && strcmp( dir_entry->d_name, "..") )
scan_account( in_cy_spool_dir, in_dst_root, dir_entry->d_name );
}
closedir( p_dir );
}
// Backing out, terminate source mailbox path
if ( cc = strrchr( src_path, '/' ) )
*cc = '\0';
// Backing out, terminate destination mailbox path
if ( cc = strrchr( dst_path, '.') )
*cc='\0';
chdir( ".." );
}
}
return( rc );
} // scan_account
// ------------------------------------------------------------------
//
// set_quota ()
int set_quota ( char *in_dst_root, char *in_dir )
{
FILE *src_file = NULL;
long long max_quota = 0;
char *p_line = NULL;
char src_path[ PATH_MAX + 1 ];
char dst_path[ PATH_MAX + 1 ];
// Get source path from base path + first letter of account name + account name
// ie. /var/imap/quota/j/user.joe
snprintf( src_path, PATH_MAX, "
// Create destination path from target directory + directory name
snprintf( dst_path, PATH_MAX, "
// Open soruce file
src_file = fopen( src_path, "r" );
if ( src_file == NULL ) {
fprintf( stdout, "*** Warning: Could not open quota file: return( -1 );
}
g_maildirsize = fopen( dst_path, "w" );
if ( g_maildirsize == NULL ) {
fprintf( stdout, "*** Warning: Could not open quota file: fclose( src_file );
return( -1 );
}
// read "quota used"
// - this value is not used, will calculate from actual messages migrated
p_line = read_line( src_file );
// Read "max quota"
p_line = read_line( src_file );
if ( p_line != NULL ) {
// convert K to bytes
if ( atol( p_line ) != 2147483647 )
max_quota = atoll( p_line ) * 1024LL;
free( p_line );
p_line = NULL;
}
// set max quota in .../maildirsize file
fprintf( stdout, "Setting mail quota to: fprintf( g_maildirsize, "
fclose( src_file );
return( 0 );
} // set_quota
// ------------------------------------------------------------------
//
// set_subscribe ()
int set_subscribe ( char *in_dst_root, char *in_dir )
{
int len = 0;
char *tab_char = NULL;
FILE *src_file;
FILE *dst_file;
char *line_buf = NULL;
char src_path[ PATH_MAX + 1 ];
char dst_path[ PATH_MAX + 1 ];
// Get source path from base path + first letter of account name + account name
// ie. /var/imap/user/j/joe.sub
snprintf( src_path, PATH_MAX, "
// Create destination path from target directory + directory name
snprintf( dst_path, PATH_MAX, "
// Get length of: "user." + acct name + '.'
len = strlen( g_account ) + 6;
// Open soruce file
src_file = fopen( src_path, "r" );
if ( src_file == NULL ) {
fprintf( stdout, "*** Warning: Could not open subscribe file: return( -1 );
}
dst_file = fopen( dst_path, "w" );
if ( dst_file == NULL ) {
fprintf( stdout, "*** Warning: Could not open subscribe file: fclose( src_file );
return( -1 );
}
while ( (line_buf = read_line( src_file )) != NULL ) {
if ( strlen( line_buf ) > 0 ) {
tab_char = strrchr( &line_buf[len], '\t' );
if ( tab_char )
*tab_char = '\0';
}
// Write just the mailbox name
fprintf( dst_file, "
free( line_buf );
line_buf = NULL;
}
fclose( dst_file );
fclose( src_file );
return( 0 );
} // set_subscribe
// ------------------------------------------------------------------
//
// create_maildir_dirs ()
int create_maildir_dirs ( char *root, char *dir, int is_root )
{
char mb_root[ PATH_MAX + 1 ];
char mb_cur[ PATH_MAX + 1 ];
char mb_new[ PATH_MAX + 1 ];
char mb_tmp[ PATH_MAX + 1 ];
char mb_mdir[ PATH_MAX + 1 ];
snprintf( mb_root, PATH_MAX, " snprintf( mb_cur, PATH_MAX, " snprintf( mb_new, PATH_MAX, " snprintf( mb_tmp, PATH_MAX, " snprintf( mb_mdir, PATH_MAX, "
mkdir( mb_root, S_IRWXU );
mkdir( mb_cur, S_IRWXU );
mkdir( mb_new, S_IRWXU );
mkdir( mb_tmp, S_IRWXU );
if ( !is_root ) {
FILE *a_file = NULL;
a_file = fopen( mb_mdir, "w");
if ( a_file != NULL )
fclose( a_file );
}
return( 0 );
} // create_maildir_dirs
// ------------------------------------------------------------------
//
// migrate_message ()
int migrate_message ( char *in_src, unsigned long in_date, char *in_dst, char *in_flags, unsigned long *out_size )
{
NSError *nsError = nil;
NSString *nsStr_dst = nil;
struct stat file_stat;
struct timeval time_val[ 2 ];
NSFileManager *nsFileMgr = [NSFileManager defaultManager];
stat( in_src, &file_stat );
*out_size = file_stat.st_size;
// Destination path name
nsStr_dst = [NSString stringWithFormat: @" if ( c_flag == 1 || e_flag == 1 ) {
if ( g_debug )
fprintf( stdout, "copying:
if ( [nsFileMgr copyItemAtPath: [NSString stringWithUTF8String: in_src] toPath: nsStr_dst error: &nsError ] == NO )
fprintf( stdout, "Error: Failed to move:
if ( e_flag == 1 ) {
if ( [nsFileMgr removeItemAtPath: [NSString stringWithUTF8String: in_src] error: &nsError ] == NO )
fprintf( stdout, "Error: Failed to delete: }
} else {
if ( g_debug )
fprintf( stdout, "moving:
if ( [nsFileMgr moveItemAtPath: [NSString stringWithUTF8String: in_src] toPath: nsStr_dst error: &nsError ] == NO )
fprintf( stdout, "Error: Failed to move: }
// fix file mod times, this is the imap "received time"
time_val[ 0 ].tv_sec = file_stat.st_atime;
time_val[ 1 ].tv_sec = in_date;
utimes( [nsStr_dst UTF8String], time_val );
return( 0 );
} // migrate_message
// ------------------------------------------------------------------
//
// migrate_mail ()
int migrate_mail ( char *in_src_path, char *in_dest_path, int is_root )
{
unsigned int i = 0;
unsigned long ver = 0;
unsigned int len = 0;
unsigned long msg_size = 0;
long long total_bytes = 0LL;
long total_msgs = 0;
char *mb_path = NULL;
FILE *hdr_file = NULL;
FILE *uid_file = NULL;
DIR *p_dir = NULL;
struct dirent *dir_entry;
char keyword [ 2 ];
char dst_file_path [ PATH_MAX + 1 ];
char src_mb_path [ PATH_MAX + 1 ];
char cy_index_path [ PATH_MAX + 1 ];
char cy_header_path[ PATH_MAX + 1 ];
char src_msg_path [ PATH_MAX + 1 ];
char msg_flags_str [ PATH_MAX + 1 ];
struct index_header cy_header;
struct index_entry cy_index_entry;
if ( g_debug ) {
fprintf( stdout, "Migrating mailbox:
// Get cyrus mailbox index file path
snprintf( cy_index_path, PATH_MAX, "
// Get cyrus mailbox header file path
snprintf( cy_header_path, PATH_MAX, "
// Get the cyrus index version
if ( hdr_file = fopen( cy_index_path, "r") ) {
unsigned long uidvalidity = 0;
// get the version number for the cyrus minor_version in header
memset( &cy_header, 0, sizeof( cy_header ) );
fread( &cy_header, 3*4, 1, hdr_file );
ver = ntohl( cy_header.minor_version );
fseek( hdr_file, 0, SEEK_SET );
if ( g_debug ) {
fprintf( stdout, "cyrus index version:
// check for the only two we care about
switch ( ver ) {
case 2:
fread( &cy_header, 44, 1, hdr_file );
uidvalidity = (unsigned long)ntohl(cy_header.pop3_last_login);
break;
case 3:
fread( &cy_header, 56, 1, hdr_file );
uidvalidity = (unsigned long)ntohl(cy_header.pop3_last_login);
break;
case 4:
fread( &cy_header, 76, 1, hdr_file );
uidvalidity = (unsigned long)ntohl(cy_header.pop3_last_login);
break;
case 6:
fread( &cy_header, 80, 1, hdr_file );
uidvalidity = (unsigned long)ntohl(cy_header.uidvalidity);
break;
case 9:
fread( &cy_header, 96, 1, hdr_file );
uidvalidity = (unsigned long)ntohl(cy_header.uidvalidity);
break;
default:
fprintf( stdout, "Unsupported cyrus mailbox header version: [gPool release];
exit(1);
}
sprintf( dst_file_path, " uid_file = fopen( dst_file_path, "w" );
if ( uid_file == NULL ) {
fprintf( stdout, "*** Error: could not open: [gPool release];
exit(1);
}
// Copy over uid data from cyrus to maildir mailbox
fprintf( uid_file, "1
// Parse cyrus seen db file
strlcpy( src_mb_path, in_src_path, PATH_MAX );
len = strlen( g_cyrus_spool_dir );
// Just get the relative mailbox path
mb_path = src_mb_path + len;
// Convert any /'s to .'s in the mailbox path name
for ( len = 0; len < strlen( mb_path ); len++ ) {
if ( mb_path[ len ]=='/' )
mb_path[ len ]='.';
}
mb_path++;
// Do the seen file parsing
parse_seen_file( mb_path, uidvalidity );
memset( &cy_index_entry, 0, sizeof( cy_index_entry ) );
fseek( hdr_file, htonl(cy_header.start_offset), SEEK_SET );
// Scan through the cyrus header file and migrate a mail file for each entry
while( fread( &cy_index_entry, ntohl( cy_header.record_size ), 1, hdr_file) ) {
// Make target file path
sprintf( dst_file_path, " in_dest_path,
(unsigned long)ntohl( cy_index_entry.internaldate ),
(unsigned long)ntohl( cy_index_entry.uid ) );
// Set maildir flags
strlcpy( msg_flags_str, ":2,", PATH_MAX );
// Draft flag
if ( (ntohl(cy_index_entry.system_flags) & FLAG_DRAFT) )
strcat( msg_flags_str, "D" );
// Flagged flag
if ( (ntohl(cy_index_entry.system_flags) & FLAG_FLAGGED) )
strcat( msg_flags_str, "F" );
// Draft flag
if ( (ntohl(cy_index_entry.system_flags) & FLAG_ANSWERED) )
strcat( msg_flags_str, "R" );
// Seen flag
if ( is_seen( (unsigned long)(ntohl(cy_index_entry.uid))) )
strcat( msg_flags_str, "S" );
// Draft flag
if ( (ntohl(cy_index_entry.system_flags) & FLAG_DELETED) )
strcat( msg_flags_str, "T" );
// Set dovecot keywords
keyword[ 1 ] = '\0';
for ( i = 0; i < MAX_USER_FLAGS && i <= 'z'-'a'; i++) {
if ( (htonl( cy_index_entry.user_flags[i / 8 ]) & (1 << (i keyword[ 0 ] = 'a' + i;
strcat( msg_flags_str, keyword );
}
}
// Full source path
sprintf( src_msg_path, "
// Now do the actual copy/move of the cyrus mail file
migrate_message( src_msg_path, (unsigned long)ntohl(cy_index_entry.internaldate), dst_file_path, msg_flags_str, &msg_size );
// Set uid in dovecot dovecot-uidlist
fprintf( uid_file, "
total_bytes += msg_size;
total_msgs++;
}
if ( total_bytes != 0 ) {
if ( g_maildirsize != NULL )
fprintf( g_maildirsize, " }
fprintf( stdout, "- mailbox: fprintf( stdout, " messages:
g_count += total_msgs;
g_size += total_bytes;
fclose( hdr_file );
fclose( uid_file );
} else {
// Hmmmm, no cyrus.index file here
// Do we even want to mess with this mailbox or just log it and move on....
// For now, check for mail and log that there may be un-migrated messages
// Checking if there may be mail here
if ( p_dir = opendir(in_src_path) ) {
while ( (dir_entry = readdir(p_dir)) ) {
if ( (dir_entry->d_name[strlen( dir_entry->d_name ) - 1] == '.') && (dir_entry->d_name[0] != '.') ) {
// There may be messages here
fprintf( stdout, "*** Warning: Missing cyrus index file and mailbox may cntain messages in: break;
}
}
closedir( p_dir );
}
}
// Now let's write us some dovecot keywords
return( set_dovecot_keywords( cy_header_path, in_dest_path ) );
} // migrate_mail
// ------------------------------------------------------------------
//
// set_dovecot_keywords ()
int set_dovecot_keywords ( const char *in_cy_header_path, char *in_dest_path )
{
unsigned int i = 0;
char *keyword = NULL;
FILE *keyword_file = NULL;
FILE *hdr_file = NULL;
char dst_file_path [ PATH_MAX + 1 ];
char line_buf[ BUF_SIZE ];
// Now let's write us some dovecot keywords
hdr_file = fopen( in_cy_header_path, "r" );
if ( hdr_file != NULL ) {
sprintf( dst_file_path, "
// Skip past header tag
fseek( hdr_file, sizeof( MAILBOX_HEADER_MAGIC ) - 1, SEEK_SET );
// Get second line past header tag
if ( (fgets( line_buf, sizeof( line_buf ), hdr_file ) != NULL) &&
(fgets( line_buf, sizeof( line_buf ), hdr_file ) != NULL) &&
(*line_buf != '\0') )
{
if ( line_buf[ strlen( line_buf ) - 1 ] == '\n' )
line_buf[ strlen( line_buf ) -1 ] = '\0';
// Tokenize this line to get keywords
keyword = strtok( line_buf, " ");
if ( keyword != NULL ) {
keyword_file = fopen( dst_file_path, "w" );
if ( keyword_file == NULL ) {
fprintf( stdout, "*** Error: could not open: return( 1 );
}
// Write the keywords to the dovecot file
do {
if ( *keyword != '\0' )
fprintf( keyword_file, "
i++;
} while ( (keyword = strtok( NULL, " " ) ) != NULL );
fclose( keyword_file );
}
}
fclose( hdr_file );
}
return( 0 );
} // set_dovecot_keywords
// ------------------------------------------------------------------
//
// read_seen_file ()
int read_seen_file ( const char *in_seen_file )
{
int i =- 1;
char *token = NULL;
FILE *fp_seen = NULL;
char *line_buf = NULL;
struct s_seen_data *out_seen_file;
// Open the seen file
if ( in_seen_file ) {
fp_seen = fopen( in_seen_file, "r" );
if ( fp_seen == NULL )
return( 1 );
}
out_seen_file = malloc( sizeof(struct s_seen_data) );
if ( out_seen_file == NULL ) {
if ( g_debug != 0 )
fprintf( stdout, "
fclose( fp_seen );
return( 1 );
}
out_seen_file->seen_count = -1;
out_seen_file->uid_flag = -1;
while ( (line_buf = read_line( fp_seen )) != NULL ) {
if ( strlen( line_buf ) )
i++;
else
continue;
// parse the cyrus seen file
token = strtok( line_buf, " \t" );
out_seen_file->seen_array[ i ].mbox_uid = cpy_str( token );
token = strtok( NULL, " \t" );
token = strtok( NULL, " \t" );
token = strtok( NULL, " \t" );
token = strtok( NULL, " \t" );
token = strtok( NULL, " \t" );
if ( token )
out_seen_file->seen_array[i].seen_uids = cpy_str( token );
else
out_seen_file->seen_array[i].seen_uids = cpy_str( "" );
free( line_buf );
line_buf = NULL;
}
if ( i >= 0 )
out_seen_file->seen_count = i + 1;
fclose( fp_seen );
g_seen_file = out_seen_file;
return( 0 );
} // read_seen_file
// ------------------------------------------------------------------
//
// parse_seen_file ()
int parse_seen_file ( const char *in_mailbox, const unsigned long in_uidvalidity )
{
int i = 0;
int index = 0;
char *p = NULL;
size_t str_len = 0;
char *str = NULL;
char *left_str = NULL;
char *right_str = NULL;
char *tuple_str = NULL;
char *token_str = NULL;
char mbox [ PATH_MAX + 1 ];
char uniqueid [ 17 ];
if ( g_seen_file == NULL ) {
if ( g_debug != 0 )
fprintf( stdout, "No seen file found for
return( 1 );
}
snprintf( mbox, PATH_MAX, "user.
// Make mailbox uid
mailbox_make_uniqueid( mbox, in_uidvalidity, uniqueid );
// Find the seen line for this mailbox
g_seen_file->uid_flag = -1;
for ( i = 0; i < g_seen_file->seen_count; i++ ) {
if ( strcmp( uniqueid, g_seen_file->seen_array[ i ].mbox_uid ) == 0 )
g_seen_file->uid_flag = i;
}
if ( (g_seen_file->uid_flag) >= 0 && (g_seen_file->seen_count != -1) ) {
if ( tuple_str = cpy_str( g_seen_file->seen_array[ g_seen_file->uid_flag ].seen_uids ) ) {
// Scan across seen string parsing out seen uid's
// ie. 3:4,8:9,11:12,15:16,20 or 1,3,5,7,9,11
// Comma separated token string
token_str = strtok( tuple_str, "," );
i = 0;
while ( token_str ) {
// Bail if we have exceeded max range
assert( i < CYRUS_SEENMAX );
str = cpy_str( token_str );
// Look for a ':' separator
p = strchr( str, ':' );
index = -1;
if ( p != NULL )
index = p - str;
// If ':' was not found
if ( index == -1 ) {
left_str = cpy_str( str );
right_str = cpy_str( str );
} else {
// We found the ':', get uid min/max values
if ( index >= strlen( str ) )
left_str = cpy_str( str );
else if ( index <= 0 )
left_str = NULL;
else {
left_str = malloc( index + 1 );
if ( left_str != NULL ) {
memset( left_str, 0, (size_t)index + 1 );
memcpy( left_str, str, (size_t)index );
}
}
if ( index >= strlen( str ) )
right_str = NULL;
else if ( index < 0 )
right_str = cpy_str( str );
else {
str_len = ( strlen( str ) - index - 1 );
right_str = malloc( str_len + 1 );
if ( right_str != NULL )
{
memset( right_str, 0, str_len + 1 );
memcpy( right_str, str +(index + 1), str_len );
}
}
}
g_seen_file->uid_array[ i ].uid_min = atol( left_str );
g_seen_file->uid_array[ i ].uid_max = atol( right_str );
i++;
// Free the strings
if ( str ) {
free( str );
str = NULL;
}
if ( left_str ) {
free( left_str );
left_str = NULL;
}
if ( right_str ) {
free( right_str );
right_str = NULL;
}
// next token
token_str = strtok( NULL, "," );
}
free( tuple_str );
g_seen_file->uid_count = i;
}
else
g_seen_file->uid_count = -1;
}
return( 0 );
} // parse_seen_file
// ------------------------------------------------------------------
//
// is_seen ()
int is_seen ( unsigned long in_uid )
{
int i;
if ( g_seen_file == NULL )
return( 0 );
if ( g_seen_file->uid_flag == -1 )
return( 0 );
for ( i = 0; i < g_seen_file->uid_count; i++ ) {
if ( (in_uid >= g_seen_file->uid_array[ i ].uid_min) && (in_uid <= g_seen_file->uid_array[ i ].uid_max) )
return( 1 );
}
return( 0 );
} // is_seen
// ------------------------------------------------------------------
//
// free_seen_file ()
void free_seen_file ( void )
{
int i;
if ( g_seen_file == NULL )
return;
for ( i = 0; i < g_seen_file->seen_count; i++ ) {
free( g_seen_file->seen_array[ i ].mbox_uid );
free( g_seen_file->seen_array[ i ].seen_uids );
}
free( g_seen_file );
} // free_seen_file
// ------------------------------------------------------------------
//
// read_line ()
char *read_line ( FILE *in_file )
{
int c = 0;
int line_len = -1;
int buf_size = 1024;
char *out_buf = NULL;
if ( in_file && !feof( in_file ) ) {
out_buf = (char *)malloc( buf_size );
for( ;; ) {
c = fgetc( in_file );
line_len++;
if ( line_len >= buf_size ) {
buf_size += 4096;
if ( g_debug != 0 )
fprintf( stdout, "
out_buf = realloc( out_buf, buf_size );
if ( out_buf == NULL )
return( NULL );
}
out_buf[ line_len ] = (char)c;
// Check for line termination or EOF
if ( (c == '\n') || (c == '\r') || (c == '\0') || (c == EOF) ) {
if( c == '\r' ) {
c = fgetc( in_file );
if ( c !='\n' )
ungetc(c, in_file);
}
// Terminate the string
out_buf[ line_len ] = '\0';
return( out_buf );
}
}
}
return( NULL );
} // read_line
// ------------------------------------------------------------------
//
// cpy_str ()
char * cpy_str ( const char *in_str )
{
int len = 0;
char *out_str = NULL;
if ( in_str != NULL ) {
len = strlen( in_str ) + 1;
out_str = malloc( len );
if ( out_str == NULL ) {
if ( g_debug != 0 )
fprintf( stdout, " } else
strlcpy( out_str, in_str, len );
}
return( out_str );
} // cpy_str
// ------------------------------------------------------------------
//
// mailbox_make_uniqueid ()
//
// Function from cyrus mailbox.c
//
void mailbox_make_uniqueid ( char *name, unsigned long in_uidvalidity, char *uniqueid )
{
u_int32_t hash = 0;
while (*name) {
hash *= 251;
hash += *name++;
hash }
sprintf( uniqueid, "} // mailbox_make_uniqueid
// ------------------------------------------------------------------
//
void rename_mailboxes ( const char *g_dest_dir )
{
DIR *p_dir = NULL;
struct dirent *dir_entry;
if ( p_dir = opendir( g_dest_dir ) ) {
// Recursively scan across all mailboxes
while( (dir_entry = readdir(p_dir)) ) {
// Skip the '.' & '..' directories
if ( strcmp( dir_entry->d_name, "." ) && strcmp( dir_entry->d_name, "..") )
map_mailbox_to_guid( g_dest_dir, dir_entry->d_name );
}
closedir( p_dir );
}
} // rename_mailboxes
// ------------------------------------------------------------------
//
void set_attribute ( NSString *in_path )
{
BOOL is_dir = NO;
NSError *nsError = nil;
NSArray *nsArry = nil;
NSString *nsStr_name = nil;
NSString *nsStr_path = nil;
NSEnumerator *nsEnum = nil;
NSFileManager *nsFileMgr = [NSFileManager defaultManager];
NSDictionary *nsDict_dir = [NSDictionary dictionaryWithObjectsAndKeys:
@"_dovecot", NSFileOwnerAccountName,
@"mail", NSFileGroupOwnerAccountName,
[NSNumber numberWithUnsignedLong:0700], NSFilePosixPermissions, nil];
NSDictionary *nsDict_file= [NSDictionary dictionaryWithObjectsAndKeys :
@"_dovecot", NSFileOwnerAccountName,
@"mail", NSFileGroupOwnerAccountName,
[NSNumber numberWithUnsignedLong:0600], NSFilePosixPermissions, nil];
if ( [nsFileMgr fileExistsAtPath: in_path isDirectory : &is_dir ] && is_dir ) {
[nsFileMgr setAttributes: nsDict_dir ofItemAtPath: in_path error: &nsError];
nsArry = [nsFileMgr contentsOfDirectoryAtPath: in_path error: &nsError];
nsEnum = [nsArry objectEnumerator];
while ( (nsStr_name = [nsEnum nextObject]) ) {
nsStr_path = [in_path stringByAppendingPathComponent: nsStr_name];
if ( [nsFileMgr fileExistsAtPath: nsStr_path isDirectory : &is_dir ] && is_dir )
set_attribute( nsStr_path );
else
[nsFileMgr setAttributes: nsDict_file ofItemAtPath: nsStr_path error: &nsError];
}
}
else
[nsFileMgr setAttributes: nsDict_file ofItemAtPath: nsStr_path error: &nsError];
} // set_attribute
// ------------------------------------------------------------------
void fix_mailboxes ( const char *g_dest_dir )
{
DIR *p_dir = NULL;
NSString *nsStr_path = nil;
struct dirent *dir_entry;
if ( p_dir = opendir( g_dest_dir ) ) {
// Recursively scan across all mailboxes
while ( (dir_entry = readdir(p_dir)) ) {
// Skip the '.' & '..' directories
if ( strcmp( dir_entry->d_name, "." ) && strcmp( dir_entry->d_name, "..") ) {
nsStr_path = [NSString stringWithFormat: @" [NSString stringWithUTF8String: dir_entry->d_name]];
set_attribute ( nsStr_path );
}
}
closedir( p_dir );
}
} // fix_mailboxes
// -----------------------------------------------------------------
void write_settings ( NSDictionary *in_dict )
{
[in_dict writeToFile: MAIL_USER_SETTINGS_PLIST atomically: YES];
NSDictionary *nsDict_attrs = [NSDictionary dictionaryWithObjectsAndKeys :
@"_postfix", NSFileOwnerAccountName,
@"mail", NSFileGroupOwnerAccountName,
[NSNumber numberWithUnsignedLong: 0660], NSFilePosixPermissions,
nil];
[[NSFileManager defaultManager] setAttributes: nsDict_attrs ofItemAtPath: MAIL_USER_SETTINGS_PLIST error: nil];
} // set_attributes
// -----------------------------------------------------------------
void set_attributes ( NSString *in_path, NSString *in_owner, NSString *in_group, int in_perms )
{
NSDictionary *nsDict_attrs = [NSDictionary dictionaryWithObjectsAndKeys :
in_owner, NSFileOwnerAccountName,
in_group, NSFileGroupOwnerAccountName,
[NSNumber numberWithUnsignedLong: in_perms], NSFilePosixPermissions,
nil];
[[NSFileManager defaultManager] setAttributes: nsDict_attrs ofItemAtPath: in_path error: nil];
} // set_attributes
// ------------------------------------------------------------------
void set_migration_flag ( const char *in_guid )
{
NSFileManager *nsFileMgr = [NSFileManager defaultManager];
if ( ![nsFileMgr fileExistsAtPath: MAIL_USER_SETTINGS_PLIST] ) {
log_message( LOG_ERR, [NSString stringWithFormat: @"missing migration file: return;
}
// get the guid user list from mailusersettings.plist
NSMutableDictionary *users = [NSMutableDictionary dictionaryWithContentsOfFile: MAIL_USER_SETTINGS_PLIST];
if ( !users )
users = [[[NSMutableDictionary alloc] init]autorelease];
// look for the individual GUID and set migration flag and mail attribute
NSMutableDictionary *user = [users objectForKey: [NSString stringWithUTF8String: in_guid]];
if (user)
[user setObject: kXMLValueAcctMigrated forKey: kXMLKeyMigrationFlag];
else
[users setObject: [NSMutableDictionary dictionaryWithObjectsAndKeys:
kXMLValueAcctMigrated, kXMLKeyMigrationFlag, nil]
forKey: [NSString stringWithUTF8String: in_guid]];
// get mail attribute from od
NSDictionary *mail_attribute = get_mail_attribute(in_guid);
if (mail_attribute) {
user = [users objectForKey: [NSString stringWithUTF8String: in_guid]];
if (user)
[user addEntriesFromDictionary: mail_attribute];
}
write_settings( users );
} // set_migration_flag
// ------------------------------------------------------------------
//
void set_migration_flags ( BOOL in_set )
{
NSFileManager *nsFileMgr = [NSFileManager defaultManager];
if ( ![nsFileMgr fileExistsAtPath: MAIL_USER_SETTINGS_PLIST] ) {
log_message( LOG_ERR, [NSString stringWithFormat: @"missing migration file: return;
}
NSMutableDictionary *users = [NSMutableDictionary dictionaryWithContentsOfFile: MAIL_USER_SETTINGS_PLIST];
if ( !users )
return;
NSMutableDictionary *user = nil;
NSEnumerator *enumerator = [users objectEnumerator];
while ( (user = [enumerator nextObject]) ) {
if ( in_set )
[user setObject: kXMLValueAcctMigrated forKey: kXMLKeyMigrationFlag];
else
[user setObject: kXMLValueAcctNotMigrated forKey: kXMLKeyMigrationFlag];
}
write_settings( users );
} // set_migration_flags
// ------------------------------------------------------------------
//
void fix_message_dates ( NSString *nsStr_in_path )
{
BOOL is_dir = NO;
NSError *nsErr = nil;
NSArray *nsArry = nil;
NSString *nsStr_path = nil;
NSString *nsStr_name = nil;
NSEnumerator *nsEnum = nil;
NSFileManager *nsFileMgr = [NSFileManager defaultManager];
if ( [nsFileMgr fileExistsAtPath: nsStr_in_path isDirectory: &is_dir] ) {
if ( is_dir == YES ) {
nsArry = [nsFileMgr contentsOfDirectoryAtPath: nsStr_in_path error: &nsErr];
nsEnum = [nsArry objectEnumerator];
while ( (nsStr_name = [nsEnum nextObject]) ) {
nsStr_path = [nsStr_in_path stringByAppendingPathComponent: nsStr_name ];
fix_message_dates( nsStr_path );
}
} else {
NSMutableDictionary *nsMutDict = [NSMutableDictionary dictionaryWithDictionary:
[nsFileMgr attributesOfItemAtPath: nsStr_in_path error: &nsErr]];
if ( nsMutDict != nil ) {
NSDate *nsDate = [nsMutDict objectForKey: @"NSFileModificationDate" ];
if ( nsDate != nil ) {
NSDate *nsDate_new = get_message_date( nsStr_in_path );
if ( nsDate_new != nil ) {
[nsMutDict setObject: nsDate_new forKey: @"NSFileModificationDate"];
[nsFileMgr setAttributes: nsMutDict ofItemAtPath: nsStr_in_path error: &nsErr];
}
}
}
}
}
nsArry = [nsFileMgr contentsOfDirectoryAtPath: nsStr_in_path error: &nsErr];
} // fix_message_dates
// ------------------------------------------------------------------
//
void map_mailbox_to_guid ( const char *in_path, const char *in_user )
{
BOOL is_dir = NO;
const char *guid_map_str = NULL;
NSError *nsError = nil;
NSString *nsStr_user = [NSString stringWithUTF8String: in_user];
NSString *nsStr_path = [NSString stringWithUTF8String: in_path];
NSString *nsStr_guid = nil;
NSString *nsStr_new_path = nil;
NSString *nsStr_cur_path = nil;
NSFileManager *nsFileMgr = [NSFileManager defaultManager];
nsStr_cur_path = [nsStr_path stringByAppendingPathComponent: nsStr_user];
if ( [nsFileMgr fileExistsAtPath: nsStr_cur_path isDirectory : &is_dir ] && is_dir ) {
if ( (guid_map_str = map_guid( in_user )) != NULL )
nsStr_guid = [NSString stringWithUTF8String: guid_map_str];
if ( nsStr_guid != nil ) {
nsStr_new_path = [nsStr_path stringByAppendingPathComponent: nsStr_guid];
if ( [ nsFileMgr moveItemAtPath: nsStr_cur_path toPath: nsStr_new_path error: &nsError ] == NO )
fprintf( stdout, "Unable to rename path: else
fprintf( stdout, "Mapping mailbox: } else
fprintf( stdout, "No GUID found for: } else
fprintf( stdout, "Error: } // map_mailbox_to_guid
// ------------------------------------------------------------------
//
const char *map_guid ( const char *in_user )
{
ODQuery *od_query = nil;
ODNode *ds_search_node = nil;
ODRecord *od_record = nil;
NSArray *nsArray_values = nil;
NSArray *nsArray_records = nil;
NSString *nsStr_name = nil;
ds_search_node = [ODNode nodeWithSession: [ODSession defaultSession] type: kODNodeTypeAuthentication error: nil];
if ( ds_search_node != nil ) {
od_query = [ODQuery queryWithNode: ds_search_node
forRecordTypes: [NSArray arrayWithObject: @kDSStdRecordTypeUsers]
attribute: @kDSNAttrRecordName
matchType: kODMatchEqualTo
queryValues: [NSString stringWithUTF8String: in_user]
returnAttributes: [NSArray arrayWithObject: @kDS1AttrGeneratedUID]
maximumResults: 1
error: nil];
if ( od_query != nil ) {
nsArray_records = [od_query resultsAllowingPartial: NO error: nil];
if ( (nsArray_records != nil) && [nsArray_records count] ) {
od_record = [nsArray_records objectAtIndex: 0];
if ( od_record != nil ) {
// get the real name
nsArray_values = [od_record valuesForAttribute: @kDS1AttrGeneratedUID error: nil];
if ( nsArray_values != nil )
nsStr_name = [nsArray_values objectAtIndex: 0];
}
}
}
}
if ( (nsStr_name != nil) && [nsStr_name length] )
return( [nsStr_name UTF8String] );
return( NULL );
} // map_guid
// ------------------------------------------------------------------
//
const char *map_userid ( const char *in_guid )
{
ODQuery *od_query = nil;
ODNode *ds_search_node = nil;
ODRecord *od_record = nil;
NSArray *nsArray_values = nil;
NSArray *nsArray_records = nil;
NSString *nsStr_name = nil;
ds_search_node = [ODNode nodeWithSession: [ODSession defaultSession] type: kODNodeTypeAuthentication error: nil];
if ( ds_search_node != nil ) {
od_query = [ODQuery queryWithNode: ds_search_node
forRecordTypes: [NSArray arrayWithObject: @kDSStdRecordTypeUsers]
attribute: @kDS1AttrGeneratedUID
matchType: kODMatchEqualTo
queryValues: [NSString stringWithUTF8String: in_guid]
returnAttributes: [NSArray arrayWithObject: @kDSNAttrRecordName]
maximumResults: 1
error: nil];
if ( od_query != nil ) {
nsArray_records = [od_query resultsAllowingPartial: NO error: nil];
if ( (nsArray_records != nil) && [nsArray_records count] ) {
od_record = [nsArray_records objectAtIndex: 0];
if ( od_record != nil ) {
// get the real name
nsArray_values = [od_record valuesForAttribute: @kDSNAttrRecordName error: nil];
if ( nsArray_values != nil )
nsStr_name = [nsArray_values objectAtIndex: 0];
}
}
}
}
if ( (nsStr_name != nil) && [nsStr_name length] )
return( [nsStr_name UTF8String] );
return( NULL );
} // map_userid
// ------------------------------------------------------------------
//
NSDictionary *get_mail_attribute ( const char *in_guid )
{
NSDictionary *out_dict = nil;
ODNode *ds_search_node = [ODNode nodeWithSession: [ODSession defaultSession] type: kODNodeTypeAuthentication error: nil];
if ( ds_search_node == nil )
return( NULL );
ODQuery *od_query = [ODQuery queryWithNode: ds_search_node
forRecordTypes: [NSArray arrayWithObject: @kDSStdRecordTypeUsers]
attribute: @kDS1AttrGeneratedUID
matchType: kODMatchEqualTo
queryValues: [NSString stringWithUTF8String: in_guid]
returnAttributes: [NSArray arrayWithObject: @kDS1AttrMailAttribute]
maximumResults: 1
error: nil];
if ( od_query != nil ) {
NSArray *records = [od_query resultsAllowingPartial: NO error: nil];
if ( (records != nil) && [records count] ) {
ODRecord *od_record = [records objectAtIndex: 0];
if ( od_record != nil ) {
// get the real name
NSArray *values = [od_record valuesForAttribute: @kDS1AttrMailAttribute error: nil];
if ( values && [values count] ) {
NSData *data = [[values objectAtIndex: 0] dataUsingEncoding:NSUTF8StringEncoding];
NSPropertyListFormat format;
out_dict = [NSPropertyListSerialization propertyListFromData: data
mutabilityOption: NSPropertyListImmutable format: &format errorDescription: nil];
}
}
}
}
return( out_dict );
} // get_mail_attribute
// ------------------------------------------------------------------
//
void log_message ( int in_log_lvl, NSString *in_msg, NSError *in_ns_err )
{
const char *cc_tag = "Info:";
if ( in_log_lvl == LOG_ERR )
cc_tag = "Error:";
else if ( in_log_lvl == LOG_WARNING )
cc_tag = "Warning:";
if ( (in_ns_err != nil) && ([[in_ns_err localizedFailureReason] length]) )
syslog( in_log_lvl, " else
syslog( in_log_lvl, "} // log_message
// ------------------------------------------------------------------
//
NSString *get_date_str ( NSString *nsStr_in_date )
{
NSString *nsStr = nil;
NSString *nsStr_out = nil;
NSScanner *nsScann_line = nil;
NSCharacterSet *ws_charSet = [NSCharacterSet whitespaceCharacterSet];
nsStr = [NSString stringWithString: nsStr_in_date];
while ( [nsStr rangeOfString: @";" options: NSLiteralSearch].location != NSNotFound ) {
nsScann_line = [NSScanner scannerWithString: nsStr];
[nsScann_line setCaseSensitive: YES];
[nsScann_line setCharactersToBeSkipped: nil];
if ( [nsScann_line scanUpToString: @";" intoString: nil ] )
{
[nsScann_line scanString: @";" intoString: nil ];
[nsScann_line scanCharactersFromSet: ws_charSet intoString: nil];
[nsScann_line scanUpToString: @"\r\n" intoString: &nsStr_out];
nsStr = [NSString stringWithString: nsStr_out];
}
}
return( nsStr_out );
} //
// ------------------------------------------------------------------
//
NSDate *get_message_date ( NSString *nsStr_in_path )
{
BOOL hit = NO;
BOOL done = NO;
NSDate *nsDate_out = nil;
NSError *nsErr = nil;
NSScanner *nsScann_data = nil;
NSScanner *nsScann_line = nil;
NSString *nsStr_tmp = nil;
NSString *nsStr_line = nil;
NSString *nsStr_msg_data = nil;
NSString *nsStr_date = nil;
NSDateFormatter *date_fmt = [[NSDateFormatter alloc] init];
NSMutableString *sMutStr_header = [NSMutableString stringWithString: @""];
NSCharacterSet *eol_charSet = [NSCharacterSet characterSetWithCharactersInString: @"\r\n"];
NSCharacterSet *ws_charSet = [NSCharacterSet whitespaceCharacterSet];
nsStr_msg_data = [NSString stringWithContentsOfFile: nsStr_in_path encoding: NSUTF8StringEncoding error: &nsErr];
if ( nsStr_msg_data == nil )
return( nil );
nsScann_data = [NSScanner scannerWithString: nsStr_msg_data];
[nsScann_data setCaseSensitive: YES];
[nsScann_data setCharactersToBeSkipped: nil];
// Get first Received: header
while ( ![nsScann_data isAtEnd] & !done )
{
[nsScann_data scanUpToCharactersFromSet: eol_charSet intoString: &nsStr_line];
[nsScann_data scanCharactersFromSet: eol_charSet intoString: nil];
if ( [nsStr_line hasPrefix: @"Received:"] && (hit == NO) ) {
hit = YES;
[sMutStr_header appendString: nsStr_line];
} else if ( hit == YES ) {
if ( [nsStr_line hasPrefix: @" "] || [nsStr_line hasPrefix: @"\t"] )
{
nsScann_line = [NSScanner scannerWithString: nsStr_line];
[nsScann_line setCaseSensitive: YES];
[nsScann_line setCharactersToBeSkipped: nil];
[nsScann_line scanCharactersFromSet: ws_charSet intoString: nil];
[nsScann_line scanUpToString: @"EOL" intoString: &nsStr_tmp];
[sMutStr_header appendString: @" "];
[sMutStr_header appendString: nsStr_tmp];
} else {
[sMutStr_header appendString: @"\r\n"];
break;
}
}
}
if ( [sMutStr_header length] == 0 )
return( nil );
// parse Received: header and extract date
nsStr_date = get_date_str( sMutStr_header );
if ( nsStr_date == nil )
return( nil );
[date_fmt setFormatterBehavior:NSDateFormatterBehavior10_4];
if ( [nsStr_date rangeOfString: @"(" options: NSLiteralSearch].location == NSNotFound )
[date_fmt setDateFormat: @"EEE, dd MMM yyyy HH:m:ss vvvv"];
else if ( isdigit([nsStr_date characterAtIndex: 1]) )
[date_fmt setDateFormat: @"dd MMM yyyy HH:m:ss vvvv (zzz)"];
else
[date_fmt setDateFormat: @"EEE, dd MMM yyyy HH:m:ss vvvv (zzz)"];
nsDate_out = [date_fmt dateFromString: nsStr_date];
return( nsDate_out );
} // get_message_date
// ------------------------------------------------------------------
NSDictionary *get_alt_data_stores ( int in_print )
{
NSError *ns_err = nil;
NSMutableDictionary *out_dict = [[[NSMutableDictionary alloc] init] autorelease];
if ( in_print ) {
printf("\n" );
printf("alternate data store locations and tags\n" );
printf("---------------------------------------\n" );
}
NSString *file_data = [NSMutableString stringWithContentsOfFile: @"/etc/dovecot/partition_map.conf" encoding: NSUTF8StringEncoding error: &ns_err];
if ( file_data && [file_data length] ) {
NSString *map_str = nil;
file_data = [file_data stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSArray *alt_stores = [file_data componentsSeparatedByCharactersInSet: [NSCharacterSet newlineCharacterSet]];
NSEnumerator *ns_enum = [alt_stores objectEnumerator];
while ( (map_str = [ns_enum nextObject]) ) {
NSArray *mapping = [map_str componentsSeparatedByCharactersInSet: [NSCharacterSet characterSetWithCharactersInString: @":"]];
[out_dict setObject: [mapping objectAtIndex: 1] forKey: [mapping objectAtIndex: 0]];
if ( in_print )
printf("tag: }
}
return( out_dict );
} // get_alt_data_stores
// ------------------------------------------------------------------
void set_alt_data_store_tag ( const char *in_tag, const char *in_path )
{
BOOL is_set = NO;
BOOL is_dir = NO;
NSDictionary *alt_stores = get_alt_data_stores(0);
NSMutableDictionary *maps = [[alt_stores mutableCopy] autorelease];
NSMutableString *new_map = [[[NSMutableString alloc]init ]autorelease];
NSString *path_str = [NSString stringWithCString: in_path encoding: NSUTF8StringEncoding];
if ( [[NSFileManager defaultManager] fileExistsAtPath: path_str isDirectory: &is_dir ] && is_dir ) {
NSString *tag_str = [NSString stringWithCString: in_tag encoding: NSUTF8StringEncoding];
// change existing tag path value
if ( [maps objectForKey: tag_str] ) {
[maps setObject: path_str forKey: tag_str];
is_set = YES;
}
// sanity check, make sure "default" exists, otherwise create it
if ( ![maps objectForKey: @"default"] )
[maps setObject: DEFAULT_DATA_LOCATION forKey: @"default"];
[new_map appendString: [NSString stringWithFormat: @"default: id key = 0;
NSEnumerator *ns_enum = [[maps allKeys] objectEnumerator];
while ( (key = [ns_enum nextObject]) ) {
if ( ![key isEqualToString: @"default"] )
[new_map appendString: [NSString stringWithFormat: @" }
if ( !is_set )
[new_map appendString: [NSString stringWithFormat: @" set_attributes ( path_str, @"_dovecot", @"mail", 0775 );
[new_map writeToFile: DOVECOT_PARTITION_MAPS atomically: YES encoding: NSUTF8StringEncoding error: nil];
set_attributes ( DOVECOT_PARTITION_MAPS, @"root", @"wheel", 0644 );
get_alt_data_stores(1);
} else
printf("Error: path: } // set_alt_data_store_tag
// ------------------------------------------------------------------
void reset_alt_data_store_tag ( const char *in_tag )
{
NSString *tag_str = [NSString stringWithCString: in_tag encoding: NSUTF8StringEncoding];
if ( [tag_str isEqualToString: @"default"] ) {
printf("Cannot reset default alternate store tag\n");
return;
}
NSDictionary *maps = get_alt_data_stores(0);
NSMutableString *new_map = [[[NSMutableString alloc]init ]autorelease];
if ( [maps objectForKey: tag_str] ) {
id key = 0;
NSEnumerator *ns_enum = [[maps allKeys] objectEnumerator];
while ( (key = [ns_enum nextObject]) ) {
if ( ![key isEqualToString: tag_str] )
[new_map appendString: [NSString stringWithFormat: @" }
[new_map writeToFile: DOVECOT_PARTITION_MAPS atomically: YES encoding: NSUTF8StringEncoding error: nil];
set_attributes ( DOVECOT_PARTITION_MAPS, @"root", @"wheel", 0644 );
} else
printf("Error: tag does not exist:
get_alt_data_stores(1);
} // reset_alt_data_store_tag
// ------------------------------------------------------------------
void list_alt_data_stores( const char *in_guid )
{
id key = 0;
printf("\n" );
printf("local user alternate data store location settings\n" );
printf("-------------------------------------------------\n" );
NSDictionary *users_dict = [NSDictionary dictionaryWithContentsOfFile: MAIL_USER_SETTINGS_PLIST];
if ( users_dict ) {
NSDictionary *maps = get_alt_data_stores(0);
if ( in_guid ) {
NSString *guid = [NSString stringWithCString: in_guid encoding: NSUTF8StringEncoding];
NSDictionary *user_dict = [users_dict objectForKey: guid];
if ( !user_dict )
printf("user: default: else {
NSString *alt_tag = [user_dict objectForKey: @"kAltMailStoreLoc"];
if ( [maps objectForKey: alt_tag] )
printf("user: else
printf("user: }
} else {
NSEnumerator *enumer = [[users_dict allKeys] objectEnumerator];
while ( (key = [enumer nextObject]) ) {
NSDictionary *user_dict = [users_dict objectForKey: key];
NSString *alt_tag = [user_dict objectForKey: @"kAltMailStoreLoc"];
if ( [maps objectForKey: alt_tag] )
printf("user: else
printf("user: }
}
}
printf("\n" );
} // list_alt_data_stores
// ------------------------------------------------------------------
void set_alt_data_store ( const char *in_guid, const char *in_store_tag )
{
NSMutableDictionary *users_dict = [NSMutableDictionary dictionaryWithContentsOfFile: MAIL_USER_SETTINGS_PLIST];
if ( !users_dict )
users_dict = [[[NSMutableDictionary alloc] init] autorelease];
NSString *guid = [NSString stringWithCString: in_guid encoding: NSUTF8StringEncoding];
NSString *tag = [NSString stringWithCString: in_store_tag encoding: NSUTF8StringEncoding];
NSDictionary *maps = get_alt_data_stores(0);
NSMutableDictionary *user_dict = [users_dict objectForKey: guid];
if ( user_dict ) {
if ( [maps objectForKey: tag] )
[user_dict setObject: tag forKey: @"kAltMailStoreLoc"];
else
[user_dict setObject: @"default" forKey: @"kAltMailStoreLoc"];
} else {
if ( [maps objectForKey: tag] )
[users_dict setObject: [NSDictionary dictionaryWithObjectsAndKeys:
tag, @"kAltMailStoreLoc", nil] forKey: guid];
else
[users_dict setObject: [NSDictionary dictionaryWithObjectsAndKeys:
@"default", @"kAltMailStoreLoc", nil] forKey: guid];
}
write_settings( users_dict );
list_alt_data_stores( in_guid );
} // set_alt_data_store
// ------------------------------------------------------------------
void reset_alt_data_stores ( const char *in_guid )
{
NSMutableDictionary *users_dict = [NSMutableDictionary dictionaryWithContentsOfFile: MAIL_USER_SETTINGS_PLIST];
if ( !users_dict )
users_dict = [[[NSMutableDictionary alloc] init] autorelease];
NSString *guid = [NSString stringWithCString: in_guid encoding: NSUTF8StringEncoding];
NSMutableDictionary *user_dict = [users_dict objectForKey: guid];
if ( user_dict ) {
[user_dict setObject: @"default" forKey: @"kAltMailStoreLoc"];
} else {
[users_dict setObject: [NSDictionary dictionaryWithObjectsAndKeys:
@"default", @"kAltMailStoreLoc", nil] forKey: guid];
}
write_settings( users_dict );
list_alt_data_stores( in_guid );
} // reset_alt_data_stores
// ------------------------------------------------------------------
void set_auto_forward( const char *in_guid, const char *in_fwd_addr )
{
NSMutableDictionary *users_dict = [NSMutableDictionary dictionaryWithContentsOfFile: MAIL_USER_SETTINGS_PLIST];
if ( !users_dict )
users_dict = [[[NSMutableDictionary alloc] init] autorelease];
NSString *guid = [NSString stringWithCString: in_guid encoding: NSUTF8StringEncoding];
NSString *fwd_addr = [NSString stringWithCString: in_fwd_addr encoding: NSUTF8StringEncoding];
NSMutableDictionary *user_dict = [users_dict objectForKey: guid];
if ( user_dict ) {
[user_dict setObject: @"Forward" forKey: @"kMailAccountState"];
[user_dict setObject: fwd_addr forKey: @"kAutoForwardValue"];
} else {
[users_dict setObject: [NSDictionary dictionaryWithObjectsAndKeys:
@"Forward", @"kMailAccountState",
fwd_addr, @"kAutoForwardValue", nil] forKey: guid];
}
write_settings( users_dict );
list_auto_forwards( in_guid );
} // set_auto_forward
// ------------------------------------------------------------------
void reset_auto_forward( const char *in_guid )
{
NSMutableDictionary *users_dict = [NSMutableDictionary dictionaryWithContentsOfFile: MAIL_USER_SETTINGS_PLIST];
if ( !users_dict )
return;
NSString *guid = [NSString stringWithCString: in_guid encoding: NSUTF8StringEncoding];
NSMutableDictionary *user_dict = [users_dict objectForKey: guid];
if ( user_dict ) {
[user_dict setObject: @"" forKey: @"kMailAccountState"];
[user_dict setObject: @"" forKey: @"kAutoForwardValue"];
}
write_settings( users_dict );
list_auto_forwards( in_guid );
} // reset_auto_forward
// ------------------------------------------------------------------
void list_auto_forwards ( const char *in_guid )
{
id key = 0;
printf("\n" );
printf("local user auto-forward settings\n" );
printf("--------------------------------\n" );
NSDictionary *users_dict = [NSDictionary dictionaryWithContentsOfFile: MAIL_USER_SETTINGS_PLIST];
if ( users_dict ) {
if ( in_guid ) {
NSString *guid = [NSString stringWithCString: in_guid encoding: NSUTF8StringEncoding];
NSDictionary *user_dict = [users_dict objectForKey: guid];
if ( [[user_dict objectForKey: @"kMailAccountState"] isEqualToString: @"Forward"] )
printf("user: } else {
NSEnumerator *enumer = [[users_dict allKeys] objectEnumerator];
while ( (key = [enumer nextObject]) ) {
NSDictionary *user_dict = [users_dict objectForKey: key];
if ( [[user_dict objectForKey: @"kMailAccountState"] isEqualToString: @"Forward"] )
printf("user: }
}
}
printf("\n" );
} // list_auto_forwards