def apply(self, ui): # look if this makes sense: do we have any accounts set up? my_accounts = settings.get_accounts() if not my_accounts: ui.notify('no accounts set', priority='error') return # get message to forward if not given in constructor if not self.message: self.message = ui.current_buffer.get_selected_message() mail = self.message.get_email() envelope = Envelope() if self.inline: # inline mode # set body text name, address = self.message.get_author() timestamp = self.message.get_date() qf = settings.get_hook('forward_prefix') if qf: quote = qf(name, address, timestamp, ui=ui, dbm=ui.dbman) else: quote = 'Forwarded message from %s (%s):\n' % (name or address, timestamp) mailcontent = quote quotehook = settings.get_hook('text_quote') if quotehook: mailcontent += quotehook(self.message.accumulate_body()) else: quote_prefix = settings.get('quote_prefix') for line in self.message.accumulate_body().splitlines(): mailcontent += quote_prefix + line + '\n' envelope.body = mailcontent else: # attach original mode # attach original msg mail.set_type('message/rfc822') mail['Content-Disposition'] = 'attachment' envelope.attach(Attachment(mail)) # copy subject subject = decode_header(mail.get('Subject', '')) subject = 'Fwd: ' + subject forward_subject_hook = settings.get_hook('forward_subject') if forward_subject_hook: subject = forward_subject_hook(subject) else: fsp = settings.get('forward_subject_prefix') if not subject.startswith(('Fwd:', fsp)): subject = fsp + subject envelope.add('Subject', subject) # set From realname, address = recipient_to_from(mail, my_accounts) envelope.add('From', '%s <%s>' % (realname, address)) # continue to compose ui.apply_command( ComposeCommand(envelope=envelope, spawn=self.force_spawn))
def apply(self, ui): # look if this makes sense: do we have any accounts set up? my_accounts = settings.get_accounts() if not my_accounts: ui.notify('no accounts set', priority='error') return # get message to forward if not given in constructor if not self.message: self.message = ui.current_buffer.get_selected_message() mail = self.message.get_email() envelope = Envelope() if self.inline: # inline mode # set body text name, address = self.message.get_author() timestamp = self.message.get_date() qf = settings.get_hook('forward_prefix') if qf: quote = qf(name, address, timestamp, ui=ui, dbm=ui.dbman) else: quote = 'Forwarded message from %s (%s):\n' % ( name or address, timestamp) mailcontent = quote quotehook = settings.get_hook('text_quote') if quotehook: mailcontent += quotehook(self.message.accumulate_body()) else: quote_prefix = settings.get('quote_prefix') for line in self.message.accumulate_body().splitlines(): mailcontent += quote_prefix + line + '\n' envelope.body = mailcontent else: # attach original mode # attach original msg mail.set_type('message/rfc822') mail['Content-Disposition'] = 'attachment' envelope.attach(Attachment(mail)) # copy subject subject = decode_header(mail.get('Subject', '')) subject = 'Fwd: ' + subject forward_subject_hook = settings.get_hook('forward_subject') if forward_subject_hook: subject = forward_subject_hook(subject) else: fsp = settings.get('forward_subject_prefix') if not subject.startswith(('Fwd:', fsp)): subject = fsp + subject envelope.add('Subject', subject) # set From realname, address = recipient_to_from(mail, my_accounts) envelope.add('From', '%s <%s>' % (realname, address)) # continue to compose ui.apply_command(ComposeCommand(envelope=envelope, spawn=self.force_spawn))
def main(): """The main entry point to alot. It parses the command line and prepares for the user interface main loop to run.""" options, command = parser() # logging root_logger = logging.getLogger() for log_handler in root_logger.handlers: root_logger.removeHandler(log_handler) root_logger = None numeric_loglevel = getattr(logging, options.debug_level.upper(), None) logformat = '%(levelname)s:%(module)s:%(message)s' logging.basicConfig(level=numeric_loglevel, filename=options.logfile, filemode='w', format=logformat) # locate alot config files if options.config is None: alotconfig = os.path.join( os.environ.get('XDG_CONFIG_HOME', os.path.expanduser('~/.config')), 'alot', 'config') if os.path.exists(alotconfig): settings.alot_rc_path = alotconfig else: settings.alot_rc_path = options.config settings.notmuch_rc_path = options.notmuch_config try: settings.read_config() settings.read_notmuch_config() except (ConfigError, OSError, IOError) as e: sys.exit(e) # store options given by config swiches to the settingsManager: if options.colour_mode: settings.set('colourmode', options.colour_mode) # get ourselves a database manager indexpath = settings.get_notmuch_setting('database', 'path') indexpath = options.mailindex_path or indexpath dbman = DBManager(path=indexpath, ro=options.read_only) # determine what to do if command is None: try: cmdstring = settings.get('initial_command') except CommandParseError as err: sys.exit(err) elif command.subcommand in _SUBCOMMANDS: cmdstring = ' '.join(options.command) # set up and start interface UI(dbman, cmdstring) # run the exit hook exit_hook = settings.get_hook('exit') if exit_hook is not None: exit_hook()
def main(): """The main entry point to alot. It parses the command line and prepares for the user interface main loop to run.""" options, command = parser() # logging root_logger = logging.getLogger() for log_handler in root_logger.handlers: root_logger.removeHandler(log_handler) root_logger = None numeric_loglevel = getattr(logging, options.debug_level.upper(), None) logformat = '%(levelname)s:%(module)s:%(message)s' logging.basicConfig(level=numeric_loglevel, filename=options.logfile, filemode='w', format=logformat) # locate alot config files if options.config is None: alotconfig = os.path.join( os.environ.get('XDG_CONFIG_HOME', os.path.expanduser('~/.config')), 'alot', 'config') if not os.path.exists(alotconfig): alotconfig = None else: alotconfig = options.config try: settings.read_config(alotconfig) settings.read_notmuch_config(options.notmuch_config) except (ConfigError, OSError, IOError) as e: sys.exit(e) # store options given by config swiches to the settingsManager: if options.colour_mode: settings.set('colourmode', options.colour_mode) # get ourselves a database manager indexpath = settings.get_notmuch_setting('database', 'path') indexpath = options.mailindex_path or indexpath dbman = DBManager(path=indexpath, ro=options.read_only) # determine what to do if command is None: try: cmdstring = settings.get('initial_command') except CommandParseError as err: sys.exit(err) elif command.subcommand in _SUBCOMMANDS: cmdstring = ' '.join(options.command) # set up and start interface UI(dbman, cmdstring) # run the exit hook exit_hook = settings.get_hook('exit') if exit_hook is not None: exit_hook()
def apply(self, ui): logging.info('open attachment') mimetype = self.attachment.get_content_type() # returns pair of preliminary command string and entry dict containing # more info. We only use the dict and construct the command ourselves _, entry = settings.mailcap_find_match(mimetype) if entry: afterwards = None # callback, will rm tempfile if used handler_stdin = None tempfile_name = None handler_raw_commandstring = entry['view'] # read parameter part = self.attachment.get_mime_representation() parms = tuple(map('='.join, part.get_params())) # in case the mailcap defined command contains no '%s', # we pipe the files content to the handling command via stdin if '%s' in handler_raw_commandstring: nametemplate = entry.get('nametemplate', '%s') prefix, suffix = parse_mailcap_nametemplate(nametemplate) fn_hook = settings.get_hook('sanitize_attachment_filename') if fn_hook: # get filename filename = self.attachment.get_filename() prefix, suffix = fn_hook(filename, prefix, suffix) tmpfile = tempfile.NamedTemporaryFile(delete=False, prefix=prefix, suffix=suffix) tempfile_name = tmpfile.name self.attachment.write(tmpfile) tmpfile.close() def afterwards(): os.unlink(tempfile_name) else: handler_stdin = StringIO() self.attachment.write(handler_stdin) # create handler command list handler_cmd = mailcap.subst(handler_raw_commandstring, mimetype, filename=tempfile_name, plist=parms) handler_cmdlist = split_commandstring(handler_cmd) # 'needsterminal' makes handler overtake the terminal nt = entry.get('needsterminal', None) overtakes = (nt is None) ui.apply_command(ExternalCommand(handler_cmdlist, stdin=handler_stdin, on_success=afterwards, thread=overtakes)) else: ui.notify('unknown mime type')
def __init__(self, cmd, stdin=None, shell=False, spawn=False, refocus=True, thread=False, on_success=None, **kwargs): """ :param cmd: the command to call :type cmd: list or str :param stdin: input to pipe to the process :type stdin: file or str :param spawn: run command in a new terminal :type spawn: bool :param shell: let shell interpret command string :type shell: bool :param thread: run asynchronously, don't block alot :type thread: bool :param refocus: refocus calling buffer after cmd termination :type refocus: bool :param on_success: code to execute after command successfully exited :type on_success: callable """ logging.debug({'spawn': spawn}) # make sure cmd is a list of str if isinstance(cmd, unicode): # convert cmdstring to list: in case shell==True, # Popen passes only the first item in the list to $SHELL cmd = [cmd] if shell else split_commandstring(cmd) # determine complete command list to pass touchhook = settings.get_hook('touch_external_cmdlist') # filter cmd, shell and thread through hook if defined if touchhook is not None: logging.debug('calling hook: touch_external_cmdlist') res = touchhook(cmd, shell=shell, spawn=spawn, thread=thread) logging.debug('got: %s' % res) cmd, shell, self.in_thread = res # otherwise if spawn requested and X11 is running elif spawn: if 'DISPLAY' in os.environ: term_cmd = settings.get('terminal_cmd', '') logging.info('spawn in terminal: %s' % term_cmd) termcmdlist = split_commandstring(term_cmd) cmd = termcmdlist + cmd else: thread = False self.cmdlist = cmd self.stdin = stdin self.shell = shell self.refocus = refocus self.in_thread = thread self.on_success = on_success Command.__init__(self, **kwargs)
def __init__( self, cmd, stdin=None, shell=False, spawn=False, refocus=True, thread=False, on_success=None, **kwargs ): """ :param cmd: the command to call :type cmd: list or str :param stdin: input to pipe to the process :type stdin: file or str :param spawn: run command in a new terminal :type spawn: bool :param shell: let shell interpret command string :type shell: bool :param thread: run asynchronously, don't block alot :type thread: bool :param refocus: refocus calling buffer after cmd termination :type refocus: bool :param on_success: code to execute after command successfully exited :type on_success: callable """ logging.debug({"spawn": spawn}) # make sure cmd is a list of str if isinstance(cmd, unicode): # convert cmdstring to list: in case shell==True, # Popen passes only the first item in the list to $SHELL cmd = [cmd] if shell else split_commandstring(cmd) # determine complete command list to pass touchhook = settings.get_hook("touch_external_cmdlist") # filter cmd, shell and thread through hook if defined if touchhook is not None: logging.debug("calling hook: touch_external_cmdlist") res = touchhook(cmd, shell=shell, spawn=spawn, thread=thread) logging.debug("got: %s" % res) cmd, shell, self.in_thread = res # otherwise if spawn requested and X11 is running elif spawn: if "DISPLAY" in os.environ: term_cmd = settings.get("terminal_cmd", "") logging.info("spawn in terminal: %s" % term_cmd) termcmdlist = split_commandstring(term_cmd) cmd = termcmdlist + cmd else: thread = False self.cmdlist = cmd self.stdin = stdin self.shell = shell self.refocus = refocus self.in_thread = thread self.on_success = on_success Command.__init__(self, **kwargs)
def openEnvelopeFromTmpfile(): # This parses the input from the tempfile. # we do this ourselves here because we want to be able to # just type utf-8 encoded stuff into the tempfile and let alot # worry about encodings. # get input # tempfile will be removed on buffer cleanup f = open(self.envelope.tmpfile.name) enc = settings.get('editor_writes_encoding') template = string_decode(f.read(), enc) f.close() # call post-edit translate hook translate = settings.get_hook('post_edit_translate') if translate: template = translate(template, ui=ui, dbm=ui.dbman) self.envelope.parse_template(template, only_body=self.edit_only_body) if self.openNew: ui.buffer_open(buffers.EnvelopeBuffer(ui, self.envelope)) else: ebuffer.envelope = self.envelope ebuffer.rebuild()
def apply(self, ui): # look if this makes sense: do we have any accounts set up? my_accounts = settings.get_accounts() if not my_accounts: ui.notify('no accounts set', priority='error') return # get message to forward if not given in constructor if not self.message: self.message = ui.current_buffer.get_selected_message() mail = self.message.get_email() # set body text name, address = self.message.get_author() timestamp = self.message.get_date() qf = settings.get_hook('reply_prefix') if qf: quotestring = qf(name, address, timestamp, ui=ui, dbm=ui.dbman) else: quotestring = 'Quoting %s (%s)\n' % (name or address, timestamp) mailcontent = quotestring quotehook = settings.get_hook('text_quote') if quotehook: mailcontent += quotehook(self.message.accumulate_body()) else: quote_prefix = settings.get('quote_prefix') for line in self.message.accumulate_body().splitlines(): mailcontent += quote_prefix + line + '\n' envelope = Envelope(bodytext=mailcontent) # copy subject subject = decode_header(mail.get('Subject', '')) reply_subject_hook = settings.get_hook('reply_subject') if reply_subject_hook: subject = reply_subject_hook(subject) else: rsp = settings.get('reply_subject_prefix') if not subject.startswith(('Re:', rsp)): subject = rsp + subject envelope.add('Subject', subject) # set From realname, address = recipient_to_from(mail, my_accounts) envelope.add('From', '%s <%s>' % (realname, address)) # set To sender = mail['Reply-To'] or mail['From'] recipients = [sender] my_addresses = settings.get_addresses() if self.groupreply: if sender != mail['From']: recipients.append(mail['From']) cleared = self.clear_my_address(my_addresses, mail.get('To', '')) recipients.append(cleared) # copy cc for group-replies if 'Cc' in mail: cc = self.clear_my_address(my_addresses, mail['Cc']) envelope.add('Cc', decode_header(cc)) to = ', '.join(recipients) logging.debug('reply to: %s' % to) envelope.add('To', decode_header(to)) # set In-Reply-To header envelope.add('In-Reply-To', '<%s>' % self.message.get_message_id()) # set References header old_references = mail.get('References', '') if old_references: old_references = old_references.split() references = old_references[-8:] if len(old_references) > 8: references = old_references[:1] + references references.append('<%s>' % self.message.get_message_id()) envelope.add('References', ' '.join(references)) else: envelope.add('References', '<%s>' % self.message.get_message_id()) # continue to compose ui.apply_command( ComposeCommand(envelope=envelope, spawn=self.force_spawn))
def apply(self, ui): ebuffer = ui.current_buffer if not self.envelope: self.envelope = ui.current_buffer.envelope #determine editable headers edit_headers = set(settings.get('edit_headers_whitelist')) if '*' in edit_headers: edit_headers = set(self.envelope.headers.keys()) blacklist = set(settings.get('edit_headers_blacklist')) if '*' in blacklist: blacklist = set(self.envelope.headers.keys()) edit_headers = edit_headers - blacklist logging.info('editable headers: %s' % edit_headers) def openEnvelopeFromTmpfile(): # This parses the input from the tempfile. # we do this ourselves here because we want to be able to # just type utf-8 encoded stuff into the tempfile and let alot # worry about encodings. # get input # tempfile will be removed on buffer cleanup f = open(self.envelope.tmpfile.name) enc = settings.get('editor_writes_encoding') template = string_decode(f.read(), enc) f.close() # call post-edit translate hook translate = settings.get_hook('post_edit_translate') if translate: template = translate(template, ui=ui, dbm=ui.dbman) self.envelope.parse_template(template, only_body=self.edit_only_body) if self.openNew: ui.buffer_open(buffers.EnvelopeBuffer(ui, self.envelope)) else: ebuffer.envelope = self.envelope ebuffer.rebuild() # decode header headertext = u'' for key in edit_headers: vlist = self.envelope.get_all(key) if not vlist: # ensure editable headers are present in template vlist = [''] else: # remove to be edited lines from envelope del self.envelope[key] for value in vlist: # newlines (with surrounding spaces) by spaces in values value = value.strip() value = re.sub('[ \t\r\f\v]*\n[ \t\r\f\v]*', ' ', value) headertext += '%s: %s\n' % (key, value) # determine editable content bodytext = self.envelope.body if headertext: content = '%s\n%s' % (headertext, bodytext) self.edit_only_body = False else: content = bodytext self.edit_only_body = True # call pre-edit translate hook translate = settings.get_hook('pre_edit_translate') if translate: content = translate(content, ui=ui, dbm=ui.dbman) #write stuff to tempfile old_tmpfile = None if self.envelope.tmpfile: old_tmpfile = self.envelope.tmpfile self.envelope.tmpfile = tempfile.NamedTemporaryFile(delete=False, prefix='alot.') self.envelope.tmpfile.write(content.encode('utf-8')) self.envelope.tmpfile.flush() self.envelope.tmpfile.close() if old_tmpfile: os.unlink(old_tmpfile.name) cmd = globals.EditCommand(self.envelope.tmpfile.name, on_success=openEnvelopeFromTmpfile, spawn=self.force_spawn, thread=self.force_spawn, refocus=self.refocus) ui.apply_command(cmd)
indexpath = settings.get_notmuch_setting('database', 'path') indexpath = args['mailindex-path'] or indexpath dbman = DBManager(path=indexpath, ro=args['read-only']) # determine what to do try: if args.subCommand == 'search': query = ' '.join(args.subOptions.args) cmdstring = 'search %s %s' % (args.subOptions.as_argparse_opts(), query) elif args.subCommand == 'compose': cmdstring = 'compose %s' % args.subOptions.as_argparse_opts() if args.subOptions.rest is not None: cmdstring += ' ' + args.subOptions.rest else: cmdstring = settings.get('initial_command') except CommandParseError, e: sys.exit(e) # set up and start interface UI(dbman, cmdstring) # run the exit hook exit_hook = settings.get_hook('exit') if exit_hook is not None: exit_hook() if __name__ == "__main__": main()
def apply(self, ui): # get message to forward if not given in constructor if not self.message: self.message = ui.current_buffer.get_selected_message() mail = self.message.get_email() # set body text name, address = self.message.get_author() timestamp = self.message.get_date() qf = settings.get_hook('reply_prefix') if qf: quotestring = qf(name, address, timestamp, ui=ui, dbm=ui.dbman) else: quotestring = 'Quoting %s (%s)\n' % (name or address, timestamp) mailcontent = quotestring quotehook = settings.get_hook('text_quote') if quotehook: mailcontent += quotehook(self.message.accumulate_body()) else: quote_prefix = settings.get('quote_prefix') for line in self.message.accumulate_body().splitlines(): mailcontent += quote_prefix + line + '\n' envelope = Envelope(bodytext=mailcontent) # copy subject subject = decode_header(mail.get('Subject', '')) reply_subject_hook = settings.get_hook('reply_subject') if reply_subject_hook: subject = reply_subject_hook(subject) else: rsp = settings.get('reply_subject_prefix') if not subject.lower().startswith(('re:', rsp.lower())): subject = rsp + subject envelope.add('Subject', subject) # Auto-detect ML auto_replyto_mailinglist = settings.get('auto_replyto_mailinglist') if mail['List-Id'] and self.listreply is None: # mail['List-Id'] is need to enable reply-to-list self.listreply = auto_replyto_mailinglist elif mail['List-Id'] and self.listreply is True: self.listreply = True elif self.listreply is False: # In this case we only need the sender self.listreply = False # set From-header and sending account try: from_header, account = determine_sender(mail, 'reply') except AssertionError as e: ui.notify(e.message, priority='error') return envelope.add('From', from_header) # set To sender = mail['Reply-To'] or mail['From'] my_addresses = settings.get_addresses() sender_address = parseaddr(sender)[1] cc = '' # check if reply is to self sent message if sender_address in my_addresses: recipients = [mail['To']] emsg = 'Replying to own message, set recipients to: %s' \ % recipients logging.debug(emsg) else: recipients = [sender] if self.groupreply: # make sure that our own address is not included # if the message was self-sent, then our address is not included MFT = mail.get_all('Mail-Followup-To', []) followupto = self.clear_my_address(my_addresses, MFT) if followupto and settings.get('honor_followup_to'): logging.debug('honor followup to: %s', followupto) recipients = [followupto] # since Mail-Followup-To was set, ignore the Cc header else: if sender != mail['From']: recipients.append(mail['From']) # append To addresses if not replying to self sent message if sender_address not in my_addresses: cleared = self.clear_my_address( my_addresses, mail.get_all('To', [])) recipients.append(cleared) # copy cc for group-replies if 'Cc' in mail: cc = self.clear_my_address( my_addresses, mail.get_all('Cc', [])) envelope.add('Cc', decode_header(cc)) to = ', '.join(recipients) logging.debug('reply to: %s' % to) if self.listreply: # To choose the target of the reply --list # Reply-To is standart reply target RFC 2822:, RFC 1036: 2.2.1 # X-BeenThere is needed by sourceforge ML also winehq # X-Mailing-List is also standart and is used by git-send-mail to = mail['Reply-To'] or mail['X-BeenThere'] or mail['X-Mailing-List'] # Some mail server (gmail) will not resend you own mail, so you have # to deal with the one in sent if to is None: to = mail['To'] logging.debug('mail list reply to: %s' % to) # Cleaning the 'To' in this case if envelope.get('To') is not None: envelope.__delitem__('To') # Finally setup the 'To' header envelope.add('To', decode_header(to)) # if any of the recipients is a mailinglist that we are subscribed to, # set Mail-Followup-To header so that duplicates are avoided if settings.get('followup_to'): # to and cc are already cleared of our own address allrecipients = [to] + [cc] lists = settings.get('mailinglists') # check if any recipient address matches a known mailing list if any([addr in lists for n, addr in getaddresses(allrecipients)]): followupto = ', '.join(allrecipients) logging.debug('mail followup to: %s' % followupto) envelope.add('Mail-Followup-To', decode_header(followupto)) # set In-Reply-To header envelope.add('In-Reply-To', '<%s>' % self.message.get_message_id()) # set References header old_references = mail.get('References', '') if old_references: old_references = old_references.split() references = old_references[-8:] if len(old_references) > 8: references = old_references[:1] + references references.append('<%s>' % self.message.get_message_id()) envelope.add('References', ' '.join(references)) else: envelope.add('References', '<%s>' % self.message.get_message_id()) # continue to compose encrypt = mail.get_content_subtype() == 'encrypted' ui.apply_command(ComposeCommand(envelope=envelope, spawn=self.force_spawn, encrypt=encrypt))
def apply(self, ui): # look if this makes sense: do we have any accounts set up? my_accounts = settings.get_accounts() if not my_accounts: ui.notify('no accounts set', priority='error') return # get message to forward if not given in constructor if not self.message: self.message = ui.current_buffer.get_selected_message() mail = self.message.get_email() # set body text name, address = self.message.get_author() timestamp = self.message.get_date() qf = settings.get_hook('reply_prefix') if qf: quotestring = qf(name, address, timestamp, ui=ui, dbm=ui.dbman) else: quotestring = 'Quoting %s (%s)\n' % (name, timestamp) mailcontent = quotestring for line in self.message.accumulate_body().splitlines(): mailcontent += '>' + line + '\n' envelope = Envelope(bodytext=mailcontent) # copy subject subject = decode_header(mail.get('Subject', '')) if not subject.startswith('Re:'): subject = 'Re: ' + subject envelope.add('Subject', subject) # set From realname, address = recipient_to_from(mail, my_accounts) envelope.add('From', '%s <%s>' % (realname, address)) # set To sender = mail['Reply-To'] or mail['From'] recipients = [sender] my_addresses = settings.get_addresses() if self.groupreply: if sender != mail['From']: recipients.append(mail['From']) cleared = self.clear_my_address(my_addresses, mail.get('To', '')) recipients.append(cleared) # copy cc for group-replies if 'Cc' in mail: cc = self.clear_my_address(my_addresses, mail['Cc']) envelope.add('Cc', decode_header(cc)) to = ', '.join(recipients) logging.debug('reply to: %s' % to) envelope.add('To', decode_header(to)) # set In-Reply-To header envelope.add('In-Reply-To', '<%s>' % self.message.get_message_id()) # set References header old_references = mail.get('References', '') if old_references: old_references = old_references.split() references = old_references[-8:] if len(old_references) > 8: references = old_references[:1] + references references.append('<%s>' % self.message.get_message_id()) envelope.add('References', ' '.join(references)) else: envelope.add('References', '<%s>' % self.message.get_message_id()) # continue to compose ui.apply_command(ComposeCommand(envelope=envelope, spawn=self.force_spawn))
def apply(self, ui): # get message to forward if not given in constructor if not self.message: self.message = ui.current_buffer.get_selected_message() mail = self.message.get_email() # set body text name, address = self.message.get_author() timestamp = self.message.get_date() qf = settings.get_hook('reply_prefix') if qf: quotestring = qf(name, address, timestamp, ui=ui, dbm=ui.dbman) else: quotestring = 'Quoting %s (%s)\n' % (name or address, timestamp) mailcontent = quotestring quotehook = settings.get_hook('text_quote') if quotehook: mailcontent += quotehook(self.message.accumulate_body()) else: quote_prefix = settings.get('quote_prefix') for line in self.message.accumulate_body().splitlines(): mailcontent += quote_prefix + line + '\n' envelope = Envelope(bodytext=mailcontent) # copy subject subject = decode_header(mail.get('Subject', '')) reply_subject_hook = settings.get_hook('reply_subject') if reply_subject_hook: subject = reply_subject_hook(subject) else: rsp = settings.get('reply_subject_prefix') if not subject.startswith(('Re:', rsp)): subject = rsp + subject envelope.add('Subject', subject) # set From-header and sending account try: from_header, account = determine_sender(mail, 'reply') except AssertionError as e: ui.notify(e.message, priority='error') return envelope.add('From', from_header) # set To sender = mail['Reply-To'] or mail['From'] recipients = [sender] my_addresses = settings.get_addresses() if self.groupreply: if sender != mail['From']: recipients.append(mail['From']) cleared = self.clear_my_address(my_addresses, mail.get('To', '')) recipients.append(cleared) # copy cc for group-replies if 'Cc' in mail: cc = self.clear_my_address(my_addresses, mail['Cc']) envelope.add('Cc', decode_header(cc)) to = ', '.join(recipients) logging.debug('reply to: %s' % to) envelope.add('To', decode_header(to)) # set In-Reply-To header envelope.add('In-Reply-To', '<%s>' % self.message.get_message_id()) # set References header old_references = mail.get('References', '') if old_references: old_references = old_references.split() references = old_references[-8:] if len(old_references) > 8: references = old_references[:1] + references references.append('<%s>' % self.message.get_message_id()) envelope.add('References', ' '.join(references)) else: envelope.add('References', '<%s>' % self.message.get_message_id()) # continue to compose ui.apply_command(ComposeCommand(envelope=envelope, spawn=self.force_spawn))
def main(): # interpret cml arguments args = Options() try: args.parseOptions() # When given no argument, parses sys.argv[1:] except usage.UsageError as errortext: print('%s' % errortext) print('Try --help for usage details.') sys.exit(1) # logging root_logger = logging.getLogger() for log_handler in root_logger.handlers: root_logger.removeHandler(log_handler) root_logger = None numeric_loglevel = getattr(logging, args['debug-level'].upper(), None) logfilename = os.path.expanduser(args['logfile']) logformat = '%(levelname)s:%(module)s:%(message)s' logging.basicConfig(level=numeric_loglevel, filename=logfilename, filemode='w', format=logformat) # locate alot config files configfiles = [ os.path.join( os.environ.get('XDG_CONFIG_HOME', os.path.expanduser('~/.config')), 'alot', 'config'), ] if args['config']: expanded_path = os.path.expanduser(args['config']) if not os.path.exists(expanded_path): msg = 'Config file "%s" does not exist. Goodbye for now.' sys.exit(msg % expanded_path) configfiles.insert(0, expanded_path) # locate notmuch config notmuchpath = os.environ.get('NOTMUCH_CONFIG', '~/.notmuch-config') if args['notmuch-config']: notmuchpath = args['notmuch-config'] notmuchconfig = os.path.expanduser(notmuchpath) alotconfig = None # read the first alot config file we find for configfilename in configfiles: if os.path.exists(configfilename): alotconfig = configfilename break # use only the first try: settings.read_config(alotconfig) settings.read_notmuch_config(notmuchconfig) except (ConfigError, OSError, IOError) as e: sys.exit(e) # store options given by config swiches to the settingsManager: if args['colour-mode']: settings.set('colourmode', args['colour-mode']) # get ourselves a database manager indexpath = settings.get_notmuch_setting('database', 'path') indexpath = args['mailindex-path'] or indexpath dbman = DBManager(path=indexpath, ro=args['read-only']) # determine what to do try: if args.subCommand == 'search': query = ' '.join(args.subOptions.args) cmdstring = 'search %s %s' % (args.subOptions.as_argparse_opts(), query) elif args.subCommand == 'compose': cmdstring = 'compose %s' % args.subOptions.as_argparse_opts() if args.subOptions.rest is not None: cmdstring += ' ' + args.subOptions.rest else: cmdstring = settings.get('initial_command') except CommandParseError as e: sys.exit(e) # set up and start interface UI(dbman, cmdstring) # run the exit hook exit_hook = settings.get_hook('exit') if exit_hook is not None: exit_hook()
def apply(self, ui): # get message to forward if not given in constructor if not self.message: self.message = ui.current_buffer.get_selected_message() mail = self.message.get_email() envelope = Envelope() if self.inline: # inline mode # set body text name, address = self.message.get_author() timestamp = self.message.get_date() qf = settings.get_hook('forward_prefix') if qf: quote = qf(name, address, timestamp, ui=ui, dbm=ui.dbman) else: quote = 'Forwarded message from %s (%s):\n' % ( name or address, timestamp) mailcontent = quote quotehook = settings.get_hook('text_quote') if quotehook: mailcontent += quotehook(self.message.accumulate_body()) else: quote_prefix = settings.get('quote_prefix') for line in self.message.accumulate_body().splitlines(): mailcontent += quote_prefix + line + '\n' envelope.body = mailcontent for a in self.message.get_attachments(): envelope.attach(a) else: # attach original mode # attach original msg original_mail = Message() original_mail.set_type('message/rfc822') original_mail['Content-Disposition'] = 'attachment' original_mail.set_payload(email_as_string(mail)) envelope.attach(Attachment(original_mail)) # copy subject subject = decode_header(mail.get('Subject', '')) subject = 'Fwd: ' + subject forward_subject_hook = settings.get_hook('forward_subject') if forward_subject_hook: subject = forward_subject_hook(subject) else: fsp = settings.get('forward_subject_prefix') if not subject.startswith(('Fwd:', fsp)): subject = fsp + subject envelope.add('Subject', subject) # set From-header and sending account try: from_header, account = determine_sender(mail, 'reply') except AssertionError as e: ui.notify(e.message, priority='error') return envelope.add('From', from_header) # continue to compose ui.apply_command(ComposeCommand(envelope=envelope, spawn=self.force_spawn))
def apply(self, ui): # get message to forward if not given in constructor if not self.message: self.message = ui.current_buffer.get_selected_message() mail = self.message.get_email() # set body text name, address = self.message.get_author() timestamp = self.message.get_date() qf = settings.get_hook('reply_prefix') if qf: quotestring = qf(name, address, timestamp, ui=ui, dbm=ui.dbman) else: quotestring = 'Quoting %s (%s)\n' % (name or address, timestamp) mailcontent = quotestring quotehook = settings.get_hook('text_quote') if quotehook: mailcontent += quotehook(self.message.accumulate_body()) else: quote_prefix = settings.get('quote_prefix') for line in self.message.accumulate_body().splitlines(): mailcontent += quote_prefix + line + '\n' envelope = Envelope(bodytext=mailcontent) # copy subject subject = decode_header(mail.get('Subject', '')) reply_subject_hook = settings.get_hook('reply_subject') if reply_subject_hook: subject = reply_subject_hook(subject) else: rsp = settings.get('reply_subject_prefix') if not subject.lower().startswith(('re:', rsp.lower())): subject = rsp + subject envelope.add('Subject', subject) # set From-header and sending account try: from_header, account = determine_sender(mail, 'reply') except AssertionError as e: ui.notify(e.message, priority='error') return envelope.add('From', from_header) # set To sender = mail['Reply-To'] or mail['From'] my_addresses = settings.get_addresses() sender_address = parseaddr(sender)[1] cc = '' # check if reply is to self sent message if sender_address in my_addresses: recipients = [mail['To']] emsg = 'Replying to own message, set recipients to: %s' \ % recipients logging.debug(emsg) else: recipients = [sender] if self.groupreply: # make sure that our own address is not included # if the message was self-sent, then our address is not included MFT = mail.get_all('Mail-Followup-To', []) followupto = self.clear_my_address(my_addresses, MFT) if followupto and settings.get('honor_followup_to'): logging.debug('honor followup to: %s', followupto) recipients = [followupto] # since Mail-Followup-To was set, ignore the Cc header else: if sender != mail['From']: recipients.append(mail['From']) # append To addresses if not replying to self sent message if sender_address not in my_addresses: cleared = self.clear_my_address(my_addresses, mail.get_all('To', [])) recipients.append(cleared) # copy cc for group-replies if 'Cc' in mail: cc = self.clear_my_address(my_addresses, mail.get_all('Cc', [])) envelope.add('Cc', decode_header(cc)) to = ', '.join(recipients) logging.debug('reply to: %s' % to) envelope.add('To', decode_header(to)) # if any of the recipients is a mailinglist that we are subscribed to, # set Mail-Followup-To header so that duplicates are avoided if settings.get('followup_to'): # to and cc are already cleared of our own address allrecipients = [to] + [cc] lists = settings.get('mailinglists') # check if any recipient address matches a known mailing list if any([addr in lists for n, addr in getaddresses(allrecipients)]): followupto = ', '.join(allrecipients) logging.debug('mail followup to: %s' % followupto) envelope.add('Mail-Followup-To', decode_header(followupto)) # set In-Reply-To header envelope.add('In-Reply-To', '<%s>' % self.message.get_message_id()) # set References header old_references = mail.get('References', '') if old_references: old_references = old_references.split() references = old_references[-8:] if len(old_references) > 8: references = old_references[:1] + references references.append('<%s>' % self.message.get_message_id()) envelope.add('References', ' '.join(references)) else: envelope.add('References', '<%s>' % self.message.get_message_id()) # continue to compose ui.apply_command( ComposeCommand(envelope=envelope, spawn=self.force_spawn))
def main(): # interpret cml arguments args = Options() try: args.parseOptions() # When given no argument, parses sys.argv[1:] except usage.UsageError as errortext: print('%s' % errortext) print('Try --help for usage details.') sys.exit(1) # logging root_logger = logging.getLogger() for log_handler in root_logger.handlers: root_logger.removeHandler(log_handler) root_logger = None numeric_loglevel = getattr(logging, args['debug-level'].upper(), None) logfilename = os.path.expanduser(args['logfile']) logformat = '%(levelname)s:%(module)s:%(message)s' logging.basicConfig(level=numeric_loglevel, filename=logfilename, filemode='w', format=logformat) # locate alot config files configfiles = [ os.path.join(os.environ.get('XDG_CONFIG_HOME', os.path.expanduser('~/.config')), 'alot', 'config'), ] if args['config']: expanded_path = os.path.expanduser(args['config']) if not os.path.exists(expanded_path): msg = 'Config file "%s" does not exist. Goodbye for now.' sys.exit(msg % expanded_path) configfiles.insert(0, expanded_path) # locate notmuch config notmuchpath = os.environ.get('NOTMUCH_CONFIG', '~/.notmuch-config') if args['notmuch-config']: notmuchpath = args['notmuch-config'] notmuchconfig = os.path.expanduser(notmuchpath) alotconfig = None # read the first alot config file we find for configfilename in configfiles: if os.path.exists(configfilename): alotconfig = configfilename break # use only the first try: settings.read_config(alotconfig) settings.read_notmuch_config(notmuchconfig) except (ConfigError, OSError, IOError) as e: sys.exit(e) # store options given by config swiches to the settingsManager: if args['colour-mode']: settings.set('colourmode', args['colour-mode']) # get ourselves a database manager indexpath = settings.get_notmuch_setting('database', 'path') indexpath = args['mailindex-path'] or indexpath dbman = DBManager(path=indexpath, ro=args['read-only']) # determine what to do try: if args.subCommand == 'search': query = ' '.join(args.subOptions.args) cmdstring = 'search %s %s' % (args.subOptions.as_argparse_opts(), query) elif args.subCommand == 'compose': cmdstring = 'compose %s' % args.subOptions.as_argparse_opts() if args.subOptions.rest is not None: cmdstring += ' ' + args.subOptions.rest else: cmdstring = settings.get('initial_command') except CommandParseError as e: sys.exit(e) # set up and start interface UI(dbman, cmdstring) # run the exit hook exit_hook = settings.get_hook('exit') if exit_hook is not None: exit_hook()
def apply(self, ui): # get message to forward if not given in constructor if not self.message: self.message = ui.current_buffer.get_selected_message() mail = self.message.get_email() # set body text name, address = self.message.get_author() timestamp = self.message.get_date() qf = settings.get_hook('reply_prefix') if qf: quotestring = qf(name, address, timestamp, ui=ui, dbm=ui.dbman) else: quotestring = 'Quoting %s (%s)\n' % (name or address, timestamp) mailcontent = quotestring quotehook = settings.get_hook('text_quote') if quotehook: mailcontent += quotehook(self.message.accumulate_body()) else: quote_prefix = settings.get('quote_prefix') for line in self.message.accumulate_body().splitlines(): mailcontent += quote_prefix + line + '\n' envelope = Envelope(bodytext=mailcontent) # copy subject subject = decode_header(mail.get('Subject', '')) reply_subject_hook = settings.get_hook('reply_subject') if reply_subject_hook: subject = reply_subject_hook(subject) else: rsp = settings.get('reply_subject_prefix') if not subject.lower().startswith(('re:', rsp.lower())): subject = rsp + subject envelope.add('Subject', subject) # set From-header and sending account try: from_header, account = determine_sender(mail, 'reply') except AssertionError as e: ui.notify(e.message, priority='error') return envelope.add('From', from_header) # set To sender = mail['Reply-To'] or mail['From'] my_addresses = settings.get_addresses() sender_address = parseaddr(sender)[1] cc = '' # check if reply is to self sent message if sender_address in my_addresses: recipients = [mail['To']] emsg = 'Replying to own message, set recipients to: %s' \ % recipients logging.debug(emsg) else: recipients = [sender] if self.groupreply: # make sure that our own address is not included # if the message was self-sent, then our address is not included MFT = mail.get_all('Mail-Followup-To', []) followupto = self.clear_my_address(my_addresses, MFT) if followupto and settings.get('honor_followup_to'): logging.debug('honor followup to: %s', followupto) recipients = [followupto] # since Mail-Followup-To was set, ignore the Cc header else: if sender != mail['From']: recipients.append(mail['From']) # append To addresses if not replying to self sent message if sender_address not in my_addresses: cleared = self.clear_my_address( my_addresses, mail.get_all('To', [])) recipients.append(cleared) # copy cc for group-replies if 'Cc' in mail: cc = self.clear_my_address( my_addresses, mail.get_all('Cc', [])) envelope.add('Cc', decode_header(cc)) to = ', '.join(recipients) logging.debug('reply to: %s' % to) envelope.add('To', decode_header(to)) # if any of the recipients is a mailinglist that we are subscribed to, # set Mail-Followup-To header so that duplicates are avoided if settings.get('followup_to'): # to and cc are already cleared of our own address allrecipients = [to] + [cc] lists = settings.get('mailinglists') # check if any recipient address matches a known mailing list if any([addr in lists for n, addr in getaddresses(allrecipients)]): followupto = ', '.join(allrecipients) logging.debug('mail followup to: %s' % followupto) envelope.add('Mail-Followup-To', decode_header(followupto)) # set In-Reply-To header envelope.add('In-Reply-To', '<%s>' % self.message.get_message_id()) # set References header old_references = mail.get('References', '') if old_references: old_references = old_references.split() references = old_references[-8:] if len(old_references) > 8: references = old_references[:1] + references references.append('<%s>' % self.message.get_message_id()) envelope.add('References', ' '.join(references)) else: envelope.add('References', '<%s>' % self.message.get_message_id()) # continue to compose ui.apply_command(ComposeCommand(envelope=envelope, spawn=self.force_spawn))
def main(): # set up the parser to parse the command line options. parser = argparse.ArgumentParser() parser.add_argument('-v', '--version', action='version', version=alot.__version__) parser.add_argument('-r', '--read-only', action='store_true', help='open db in read only mode') parser.add_argument('-c', '--config', help='config file', type=lambda x: argparse.FileType('r')(x).name) parser.add_argument('-n', '--notmuch-config', default=os.environ.get( 'NOTMUCH_CONFIG', os.path.expanduser('~/.notmuch-config')), type=lambda x: argparse.FileType('r')(x).name, help='notmuch config') parser.add_argument('-C', '--colour-mode', choices=(1, 16, 256), type=int, default=256, help='terminal colour mode [default: %(default)s].') parser.add_argument('-p', '--mailindex-path', #type=directory, help='path to notmuch index') parser.add_argument('-d', '--debug-level', default='info', choices=('debug', 'info', 'warning', 'error'), help='debug log [default: %(default)s]') parser.add_argument('-l', '--logfile', default='/dev/null', type=lambda x: argparse.FileType('w')(x).name, help='logfile [default: %(default)s]') # We will handle the subcommands in a seperate run of argparse as argparse # does not support optional subcommands until now. subcommands = ('search', 'compose', 'bufferlist', 'taglist', 'pyshell') parser.add_argument('command', nargs=argparse.REMAINDER, help='possible subcommands are {}'.format( ', '.join(subcommands))) options = parser.parse_args() if options.command: # We have a command after the initial options so we also parse that. # But we just use the parser that is already defined for the internal # command that will back this subcommand. parser = argparse.ArgumentParser() subparsers = parser.add_subparsers(dest='subcommand') for subcommand in subcommands: subparsers.add_parser(subcommand, parents=[COMMANDS['global'][subcommand][1]]) command = parser.parse_args(options.command) else: command = None # logging root_logger = logging.getLogger() for log_handler in root_logger.handlers: root_logger.removeHandler(log_handler) root_logger = None numeric_loglevel = getattr(logging, options.debug_level.upper(), None) logformat = '%(levelname)s:%(module)s:%(message)s' logging.basicConfig(level=numeric_loglevel, filename=options.logfile, filemode='w', format=logformat) # locate alot config files if options.config is None: alotconfig = os.path.join( os.environ.get('XDG_CONFIG_HOME', os.path.expanduser('~/.config')), 'alot', 'config') if not os.path.exists(alotconfig): alotconfig = None else: alotconfig = options.config try: settings.read_config(alotconfig) settings.read_notmuch_config(options.notmuch_config) except (ConfigError, OSError, IOError) as e: sys.exit(e) # store options given by config swiches to the settingsManager: if options.colour_mode: settings.set('colourmode', options.colour_mode) # get ourselves a database manager indexpath = settings.get_notmuch_setting('database', 'path') indexpath = options.mailindex_path or indexpath dbman = DBManager(path=indexpath, ro=options.read_only) # determine what to do if command is None: try: cmdstring = settings.get('initial_command') except CommandParseError as err: sys.exit(err) elif command.subcommand in subcommands: cmdstring = ' '.join(options.command) # set up and start interface UI(dbman, cmdstring) # run the exit hook exit_hook = settings.get_hook('exit') if exit_hook is not None: exit_hook()
def main(): # interpret cml arguments args = Options() try: args.parseOptions() # When given no argument, parses sys.argv[1:] except usage.UsageError as errortext: print "%s" % errortext print "Try --help for usage details." sys.exit(1) # logging root_logger = logging.getLogger() for log_handler in root_logger.handlers: root_logger.removeHandler(log_handler) root_logger = None numeric_loglevel = getattr(logging, args["debug-level"].upper(), None) logfilename = os.path.expanduser(args["logfile"]) logformat = "%(levelname)s:%(module)s:%(message)s" logging.basicConfig(level=numeric_loglevel, filename=logfilename, filemode="w", format=logformat) # locate alot config files configfiles = [os.path.join(os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~/.config")), "alot", "config")] if args["config"]: expanded_path = os.path.expanduser(args["config"]) if not os.path.exists(expanded_path): msg = 'Config file "%s" does not exist. Goodbye for now.' sys.exit(msg % expanded_path) configfiles.insert(0, expanded_path) # locate notmuch config notmuchpath = os.environ.get("NOTMUCH_CONFIG", "~/.notmuch-config") if args["notmuch-config"]: notmuchpath = args["notmuch-config"] notmuchconfig = os.path.expanduser(notmuchpath) alotconfig = None # read the first alot config file we find for configfilename in configfiles: if os.path.exists(configfilename): alotconfig = configfilename break # use only the first try: settings.read_config(alotconfig) settings.read_notmuch_config(notmuchconfig) except (ConfigError, OSError, IOError) as e: sys.exit(e) # store options given by config swiches to the settingsManager: if args["colour-mode"]: settings.set("colourmode", args["colour-mode"]) # get ourselves a database manager indexpath = settings.get_notmuch_setting("database", "path") indexpath = args["mailindex-path"] or indexpath dbman = DBManager(path=indexpath, ro=args["read-only"]) # determine what to do try: if args.subCommand == "search": query = " ".join(args.subOptions.args) cmdstring = "search %s %s" % (args.subOptions.as_argparse_opts(), query) elif args.subCommand == "compose": cmdstring = "compose %s" % args.subOptions.as_argparse_opts() if args.subOptions.rest is not None: cmdstring += " " + args.subOptions.rest else: cmdstring = settings.get("initial_command") except CommandParseError as e: sys.exit(e) # set up and start interface UI(dbman, cmdstring) # run the exit hook exit_hook = settings.get_hook("exit") if exit_hook is not None: exit_hook()
# get ourselves a database manager indexpath = settings.get_notmuch_setting('database', 'path') indexpath = args['mailindex-path'] or indexpath dbman = DBManager(path=indexpath, ro=args['read-only']) # determine what to do try: if args.subCommand == 'search': query = ' '.join(args.subOptions.args) cmdstring = 'search %s %s' % (args.subOptions.as_argparse_opts(), query) elif args.subCommand == 'compose': cmdstring = 'compose %s' % args.subOptions.as_argparse_opts() if args.subOptions.rest is not None: cmdstring += ' ' + args.subOptions.rest else: cmdstring = settings.get('initial_command') except CommandParseError, e: sys.exit(e) # set up and start interface UI(dbman, cmdstring) # run the exit hook exit_hook = settings.get_hook('exit') if exit_hook is not None: exit_hook() if __name__ == "__main__": main()