def syncitall(accounts, config, siglisteners): currentThread().setExitMessage('SYNC_WITH_TIMER_TERMINATE') threads = threadlist() mbnames.init(config, accounts) for accountname in accounts: syncaccount(threads, config, accountname, siglisteners) # Wait for the threads to finish. threads.reset()
def syncitall(accounts, config): currentThread().setExitMessage('SYNC_WITH_TIMER_TERMINATE') ui = UIBase.getglobalui() threads = threadutil.threadlist() mbnames.init(config, accounts) for accountname in accounts: syncaccount(threads, config, accountname) # Wait for the threads to finish. threads.reset()
def syncitall(accounts, config, siglisteners): currentThread().setExitMessage('SYNC_WITH_TIMER_TERMINATE') ui = UIBase.getglobalui() threads = threadutil.threadlist() mbnames.init(config, accounts) for accountname in accounts: syncaccount(threads, config, accountname, siglisteners) # Wait for the threads to finish. threads.reset()
def run(self): """Parse the commandline and invoke everything""" # next line also sets self.config and self.ui options, args = self.__parse_cmd_options() if options.diagnostics: self.__serverdiagnostics(options) elif options.migrate_fmd5: self.__migratefmd5(options) elif options.mbnames_prune: mbnames.init(self.config, self.ui, options.dryrun) mbnames.prune(self.config.get("general", "accounts")) mbnames.write() elif options.deletefolder: return self.__deletefolder(options) else: return self.__sync(options)
def __sync(self, options): """Invoke the correct single/multithread syncing self.config is supposed to have been correctly initialized already.""" def sig_handler(sig, frame): if sig == signal.SIGUSR1: # tell each account to stop sleeping accounts.Account.set_abort_event(self.config, 1) elif sig == signal.SIGUSR2: # tell each account to stop looping getglobalui().warn("Terminating after this sync...") accounts.Account.set_abort_event(self.config, 2) elif sig in (signal.SIGTERM, signal.SIGINT, signal.SIGHUP): # tell each account to ABORT ASAP (ctrl-c) getglobalui().warn("Terminating NOW (this may "\ "take a few seconds)...") accounts.Account.set_abort_event(self.config, 3) if 'thread' in self.ui.debuglist: self.__dumpstacks(5) # Abort after three Ctrl-C keystrokes self.num_sigterm += 1 if self.num_sigterm >= 3: getglobalui().warn("Signaled thrice. Aborting!") sys.exit(1) elif sig == signal.SIGQUIT: stacktrace.dump(sys.stderr) os.abort() try: self.num_sigterm = 0 signal.signal(signal.SIGHUP, sig_handler) signal.signal(signal.SIGUSR1, sig_handler) signal.signal(signal.SIGUSR2, sig_handler) signal.signal(signal.SIGTERM, sig_handler) signal.signal(signal.SIGINT, sig_handler) signal.signal(signal.SIGQUIT, sig_handler) # Various initializations that need to be performed: activeaccounts = self._get_activeaccounts(options) mbnames.init(self.config, self.ui, options.dryrun) if options.singlethreading: # Singlethreaded. self.__sync_singlethreaded(activeaccounts) else: # Multithreaded. t = threadutil.ExitNotifyThread( target=syncitall, name='Sync Runner', args=(activeaccounts, self.config,) ) # Special exit message for the monitor to stop looping. t.exit_message = threadutil.STOP_MONITOR t.start() threadutil.monitor() # All sync are done. mbnames.write() self.ui.terminate() return 0 except (SystemExit): raise except Exception as e: self.ui.error(e) self.ui.terminate() return 1
def __sync(self, options): """Invoke the correct single/multithread syncing self.config is supposed to have been correctly initialized already.""" def sig_handler(sig, frame): if sig == signal.SIGUSR1: # tell each account to stop sleeping accounts.Account.set_abort_event(self.config, 1) elif sig == signal.SIGUSR2: # tell each account to stop looping getglobalui().warn("Terminating after this sync...") accounts.Account.set_abort_event(self.config, 2) elif sig in (signal.SIGTERM, signal.SIGINT, signal.SIGHUP): # tell each account to ABORT ASAP (ctrl-c) getglobalui().warn("Terminating NOW (this may "\ "take a few seconds)...") accounts.Account.set_abort_event(self.config, 3) if 'thread' in self.ui.debuglist: self.__dumpstacks(5) # Abort after three Ctrl-C keystrokes self.num_sigterm += 1 if self.num_sigterm >= 3: getglobalui().warn("Signaled thrice. Aborting!") sys.exit(1) elif sig == signal.SIGQUIT: stacktrace.dump(sys.stderr) os.abort() try: self.num_sigterm = 0 signal.signal(signal.SIGHUP, sig_handler) signal.signal(signal.SIGUSR1, sig_handler) signal.signal(signal.SIGUSR2, sig_handler) signal.signal(signal.SIGTERM, sig_handler) signal.signal(signal.SIGINT, sig_handler) signal.signal(signal.SIGQUIT, sig_handler) # Various initializations that need to be performed: activeaccounts = self._get_activeaccounts(options) mbnames.init(self.config, self.ui, options.dryrun) if options.singlethreading: # Singlethreaded. self.__sync_singlethreaded(activeaccounts) else: # Multithreaded. t = threadutil.ExitNotifyThread(target=syncitall, name='Sync Runner', args=( activeaccounts, self.config, )) # Special exit message for the monitor to stop looping. t.exit_message = threadutil.STOP_MONITOR t.start() threadutil.monitor() # All sync are done. mbnames.write() self.ui.terminate() return 0 except (SystemExit): raise except Exception as e: self.ui.error(e) self.ui.terminate() return 1
def __parse_cmd_options(self): parser = OptionParser( version=offlineimap.__version__, description="%s.\n\n%s"% (offlineimap.__copyright__, offlineimap.__license__) ) parser.add_option("--dry-run", action="store_true", dest="dryrun", default=False, help="dry run mode") parser.add_option("--info", action="store_true", dest="diagnostics", default=False, help="output information on the configured email repositories") parser.add_option("-1", action="store_true", dest="singlethreading", default=False, help="(the number one) disable all multithreading operations") parser.add_option("-P", dest="profiledir", metavar="DIR", help="sets OfflineIMAP into profile mode.") parser.add_option("-a", dest="accounts", metavar="account1[,account2[,...]]", help="list of accounts to sync") parser.add_option("-c", dest="configfile", metavar="FILE", default=None, help="specifies a configuration file to use") parser.add_option("-d", dest="debugtype", metavar="type1[,type2[,...]]", help="enables debugging for OfflineIMAP " " (types: imap, maildir, thread)") parser.add_option("-l", dest="logfile", metavar="FILE", help="log to FILE") parser.add_option("-s", action="store_true", dest="syslog", default=False, help="log to syslog") parser.add_option("-f", dest="folders", metavar="folder1[,folder2[,...]]", help="only sync the specified folders") parser.add_option("-k", dest="configoverride", action="append", metavar="[section:]option=value", help="override configuration file option") parser.add_option("-o", action="store_true", dest="runonce", default=False, help="run only once (ignore autorefresh)") parser.add_option("-q", action="store_true", dest="quick", default=False, help="run only quick synchronizations (don't update flags)") parser.add_option("-u", dest="interface", help="specifies an alternative user interface" " (quiet, basic, syslog, ttyui, blinkenlights, machineui)") parser.add_option("--migrate-fmd5-using-nametrans", action="store_true", dest="migrate_fmd5", default=False, help="migrate FMD5 hashes from versions prior to 6.3.5") parser.add_option("--mbnames-prune", action="store_true", dest="mbnames_prune", default=False, help="remove mbnames entries for accounts not in accounts") parser.add_option("-V", action="store_true", dest="version", default=False, help="show full version infos") (options, args) = parser.parse_args() globals.set_options (options) if options.version: print("offlineimap v%s, imaplib2 v%s (%s)"% ( offlineimap.__version__, imaplib.__version__, imaplib.DESC)) sys.exit(0) # Read in configuration file. if not options.configfile: # Try XDG location, then fall back to ~/.offlineimaprc xdg_var = 'XDG_CONFIG_HOME' if not xdg_var in os.environ or not os.environ[xdg_var]: xdg_home = os.path.expanduser('~/.config') else: xdg_home = os.environ[xdg_var] options.configfile = os.path.join(xdg_home, "offlineimap", "config") if not os.path.exists(options.configfile): options.configfile = os.path.expanduser('~/.offlineimaprc') configfilename = options.configfile else: configfilename = os.path.expanduser(options.configfile) config = CustomConfigParser() if not os.path.exists(configfilename): # TODO, initialize and make use of chosen ui for logging logging.error(" *** Config file '%s' does not exist; aborting!"% configfilename) sys.exit(1) config.read(configfilename) # Profile mode chosen? if options.profiledir: if not options.singlethreading: # TODO, make use of chosen ui for logging logging.warn("Profile mode: Forcing to singlethreaded.") options.singlethreading = True if os.path.exists(options.profiledir): # TODO, make use of chosen ui for logging logging.warn("Profile mode: Directory '%s' already exists!"% options.profiledir) else: os.mkdir(options.profiledir) threadutil.ExitNotifyThread.set_profiledir(options.profiledir) # TODO, make use of chosen ui for logging logging.warn("Profile mode: Potentially large data will be " "created in '%s'"% options.profiledir) # Override a config value. if options.configoverride: for option in options.configoverride: (key, value) = option.split('=', 1) if ':' in key: (secname, key) = key.split(':', 1) section = secname.replace("_", " ") else: section = "general" config.set(section, key, value) # Which ui to use? CLI option overrides config file. ui_type = config.getdefault('general', 'ui', 'ttyui') if options.interface != None: ui_type = options.interface if '.' in ui_type: # Transform Curses.Blinkenlights -> Blinkenlights. ui_type = ui_type.split('.')[-1] # TODO, make use of chosen ui for logging logging.warning('Using old interface name, consider using one ' 'of %s'% ', '.join(UI_LIST.keys())) if options.diagnostics: ui_type = 'basic' # enforce basic UI for --info # dry-run? Set [general]dry-run=True. if options.dryrun: dryrun = config.set('general', 'dry-run', 'True') config.set_if_not_exists('general', 'dry-run', 'False') try: # Create the ui class. self.ui = UI_LIST[ui_type.lower()](config) except KeyError: logging.error("UI '%s' does not exist, choose one of: %s"% \ (ui_type, ', '.join(UI_LIST.keys()))) sys.exit(1) setglobalui(self.ui) # Set up additional log files. if options.logfile: self.ui.setlogfile(options.logfile) # Set up syslog. if options.syslog: self.ui.setup_sysloghandler() # Welcome blurb. self.ui.init_banner() if options.debugtype: self.ui.logger.setLevel(logging.DEBUG) if options.debugtype.lower() == 'all': options.debugtype = 'imap,maildir,thread' # Force single threading? if not ('thread' in options.debugtype.split(',') \ and not options.singlethreading): self.ui._msg("Debug mode: Forcing to singlethreaded.") options.singlethreading = True debugtypes = options.debugtype.split(',') + [''] for dtype in debugtypes: dtype = dtype.strip() self.ui.add_debug(dtype) if dtype.lower() == u'imap': imaplib.Debug = 5 if options.mbnames_prune: mbnames.init(config, self.ui, options.dryrun) mbnames.prune(config.get("general", "accounts")) mbnames.write() sys.exit(0) if options.runonce: # Must kill the possible default option. if config.has_option('DEFAULT', 'autorefresh'): config.remove_option('DEFAULT', 'autorefresh') # FIXME: spaghetti code alert! for section in accounts.getaccountlist(config): config.remove_option('Account ' + section, "autorefresh") if options.quick: for section in accounts.getaccountlist(config): config.set('Account ' + section, "quick", '-1') # Custom folder list specified? if options.folders: foldernames = options.folders.split(",") folderfilter = "lambda f: f in %s"% foldernames folderincludes = "[]" for accountname in accounts.getaccountlist(config): account_section = 'Account ' + accountname remote_repo_section = 'Repository ' + \ config.get(account_section, 'remoterepository') config.set(remote_repo_section, "folderfilter", folderfilter) config.set(remote_repo_section, "folderincludes", folderincludes) if options.logfile: sys.stderr = self.ui.logfile socktimeout = config.getdefaultint("general", "socktimeout", 0) if socktimeout > 0: socket.setdefaulttimeout(socktimeout) threadutil.initInstanceLimit( ACCOUNT_LIMITED_THREAD_NAME, config.getdefaultint('general', 'maxsyncaccounts', 1) ) for reposname in config.getsectionlist('Repository'): # XXX: We are likely lying around. If we must use at most n # connections for a remote IMAP server, why do we allow twice this # number? The max connections number is used by both the FOLDER_ and # the MSGCOPY_ prefixes! for namespace in [accounts.FOLDER_NAMESPACE + reposname, MSGCOPY_NAMESPACE + reposname]: if options.singlethreading: threadutil.initInstanceLimit(namespace, 1) else: threadutil.initInstanceLimit( namespace, config.getdefaultint( 'Repository ' + reposname, 'maxconnections', 2) ) self.config = config return (options, args)
def __sync(self, options): """Invoke the correct single/multithread syncing self.config is supposed to have been correctly initialized already.""" try: # Honor CLI --account option, only. # Accounts to sync are put into syncaccounts variable. activeaccounts = self.config.get("general", "accounts") if options.accounts: activeaccounts = options.accounts activeaccounts = activeaccounts.replace(" ", "") activeaccounts = activeaccounts.split(",") allaccounts = accounts.AccountHashGenerator(self.config) syncaccounts = [] for account in activeaccounts: if account not in allaccounts: if len(allaccounts) == 0: errormsg = "The account '%s' does not exist because no" \ " accounts are defined!"% account else: errormsg = "The account '%s' does not exist. Valid ac" \ "counts are: %s"% \ (account, ", ".join(allaccounts.keys())) self.ui.terminate(1, errormsg=errormsg) if account not in syncaccounts: syncaccounts.append(account) def sig_handler(sig, frame): if sig == signal.SIGUSR1: # tell each account to stop sleeping accounts.Account.set_abort_event(self.config, 1) elif sig == signal.SIGUSR2: # tell each account to stop looping getglobalui().warn("Terminating after this sync...") accounts.Account.set_abort_event(self.config, 2) elif sig in (signal.SIGTERM, signal.SIGINT, signal.SIGHUP): # tell each account to ABORT ASAP (ctrl-c) getglobalui().warn("Terminating NOW (this may "\ "take a few seconds)...") accounts.Account.set_abort_event(self.config, 3) if 'thread' in self.ui.debuglist: self.__dumpstacks(5) # Abort after three Ctrl-C keystrokes self.num_sigterm += 1 if self.num_sigterm >= 3: getglobalui().warn("Signaled thrice. Aborting!") sys.exit(1) elif sig == signal.SIGQUIT: stacktrace.dump(sys.stderr) os.abort() self.num_sigterm = 0 signal.signal(signal.SIGHUP, sig_handler) signal.signal(signal.SIGUSR1, sig_handler) signal.signal(signal.SIGUSR2, sig_handler) signal.signal(signal.SIGTERM, sig_handler) signal.signal(signal.SIGINT, sig_handler) signal.signal(signal.SIGQUIT, sig_handler) # Various initializations that need to be performed: mbnames.init(self.config, self.ui, options.dryrun) if options.singlethreading: # Singlethreaded. self.__sync_singlethreaded(syncaccounts) else: # Multithreaded. t = threadutil.ExitNotifyThread( target=syncitall, name='Sync Runner', args=(syncaccounts, self.config,) ) # Special exit message for the monitor to stop looping. t.exit_message = threadutil.STOP_MONITOR t.start() threadutil.monitor() # All sync are done. mbnames.write() self.ui.terminate() return 0 except (SystemExit): raise except Exception as e: self.ui.error(e) self.ui.terminate() return 1