def __send_message(msg_type, tmpl, options, *args): """Message sending dispatcher. """ domain = options.domain or config.get('stgit.domain') if domain: if sys.version_info < (3, 2): raise CmdException("Setting domain requires Python version 3.2+") msg_id = email.utils.make_msgid('stgit', domain=domain) else: msg_id = email.utils.make_msgid('stgit') if msg_type == 'cover': assert len(args) == 1, 'too many args for msg_type == "cover"' patches = args[0] msg = __build_cover(tmpl, msg_id, options, patches) outstr = 'the cover message' elif msg_type == 'patch': patch, patch_nr, total_nr, ref_id = args msg = __build_message(tmpl, msg_id, options, patch, patch_nr, total_nr, ref_id) outstr = 'patch "%s"' % patch else: raise AssertionError('invalid msg_type: %s' % msg_type) # pragma: no cover if hasattr(msg, 'as_bytes'): msg_bytes = msg.as_bytes(options.mbox) else: msg_bytes = msg.as_string(options.mbox) # Python 3.3 only has Message.as_string(). We encode it back to bytes # and hope for the best. if isinstance(msg_bytes, text): msg_bytes = msg_bytes.encode('utf-8') if options.mbox: out.stdout_bytes(msg_bytes + b'\n') return msg_id if not options.git: from_addr, to_addrs = __parse_addresses(msg) out.start('Sending ' + outstr) smtpserver = options.smtp_server or config.get('stgit.smtpserver') if options.git: __send_message_git(msg_bytes, msg['From'], options) elif smtpserver.startswith('/'): # Use the sendmail tool __send_message_sendmail(smtpserver, msg_bytes) else: # Use the SMTP server (we have host and port information) __send_message_smtp(smtpserver, from_addr, to_addrs, msg_bytes, options) # give recipients a chance of receiving related patches in correct order if msg_type == 'cover' or (msg_type == 'patch' and patch_nr < total_nr): sleep = options.sleep or config.getint('stgit.smtpdelay') time.sleep(sleep) if not options.git: out.done() return msg_id
def func(parser, options, args): """Create a new patch.""" stack = directory.repository.current_stack if stack.repository.default_index.conflicts(): raise CmdException( 'Cannot create a new patch -- resolve conflicts first') # Choose a name for the new patch -- or None, which means make one # up later when we've gotten hold of the commit message. if len(args) == 0: name = None elif len(args) == 1: name = args[0] if not stack.patches.is_name_valid(name): raise CmdException('Invalid patch name: "%s"' % name) elif name in stack.patches: raise CmdException('%s: patch already exists' % name) else: parser.error('incorrect number of arguments') if options.verbose: verbose = options.verbose else: verbose_int = config.getint('commit.verbose') verbose = verbose_int > 0 if verbose_int else False cd = CommitData( tree=stack.head.data.tree, parents=[stack.head], message='', author=Person.author(), committer=Person.committer(), ) cd = update_commit_data( cd, message=options.message, author=options.author(cd.author), sign_str=options.sign_str, edit=(not options.save_template and options.message is None), verbose=verbose, ) if options.save_template: options.save_template(cd.message) return utils.STGIT_SUCCESS if not options.no_verify: cd = run_commit_msg_hook(stack.repository, cd) if name is None: name = stack.patches.make_name(cd.message_str) # Write the new patch. stack.repository.default_iw trans = StackTransaction(stack, 'new: %s' % name) trans.patches[name] = stack.repository.commit(cd) trans.applied.append(name) return trans.run()
def patch_name_from_msg(msg): """Return a string to be used as a patch name. This is generated from the top line of the string passed as argument.""" if not msg: return None name_len = config.getint("stgit.namelength") if not name_len: name_len = 30 subject_line = msg.split("\n", 1)[0].lstrip().lower() return re.sub("[\W]+", "-", subject_line).strip("-")[:name_len]
def __send_message(msg_type, tmpl, options, *args): """Message sending dispatcher. """ msg_id = email.utils.make_msgid('stgit', domain=options.domain or config.get('stgit.domain')) if msg_type == 'cover': assert len(args) == 1, 'too many args for msg_type == "cover"' patches = args[0] msg = __build_cover(tmpl, msg_id, options, patches) outstr = 'the cover message' elif msg_type == 'patch': patch, patch_nr, total_nr, ref_id = args msg = __build_message(tmpl, msg_id, options, patch, patch_nr, total_nr, ref_id) outstr = 'patch "%s"' % patch else: raise AssertionError('invalid msg_type: %s' % msg_type) # pragma: no cover msg_bytes = msg.as_bytes(options.mbox) if options.mbox: out.stdout_bytes(msg_bytes + b'\n') return msg_id if not options.git: from_addr, to_addrs = __parse_addresses(msg) out.start('Sending ' + outstr) smtpserver = options.smtp_server or config.get('stgit.smtpserver') if options.git: __send_message_git(msg_bytes, msg['From'], options) elif smtpserver.startswith('/'): # Use the sendmail tool __send_message_sendmail(smtpserver, msg_bytes) else: # Use the SMTP server (we have host and port information) __send_message_smtp(smtpserver, from_addr, to_addrs, msg_bytes, options) # give recipients a chance of receiving related patches in correct order if msg_type == 'cover' or (msg_type == 'patch' and patch_nr < total_nr): sleep = options.sleep or config.getint('stgit.smtpdelay') time.sleep(sleep) if not options.git: out.done() return msg_id
def __send_message(type, tmpl, options, *args): """Message sending dispatcher. """ (build, outstr) = { 'cover': (__build_cover, 'the cover message'), 'patch': (__build_message, 'patch "%s"' % args[0]), }[type] if type == 'patch': (patch_nr, total_nr) = (args[1], args[2]) msg_id = email.utils.make_msgid('stgit') msg = build(tmpl, msg_id, options, *args) if hasattr(msg, 'as_bytes'): msg_bytes = msg.as_bytes(options.mbox) else: msg_bytes = msg.as_string(options.mbox) # Python 3.3 only has Message.as_string(). We encode it back to bytes # and hope for the best. if isinstance(msg_bytes, text): msg_bytes = msg_bytes.encode('utf-8') if options.mbox: out.stdout_bytes(msg_bytes + b'\n') return msg_id if not options.git: from_addr, to_addrs = __parse_addresses(msg) out.start('Sending ' + outstr) smtpserver = options.smtp_server or config.get('stgit.smtpserver') if options.git: __send_message_git(msg_bytes, msg['From'], options) elif smtpserver.startswith('/'): # Use the sendmail tool __send_message_sendmail(smtpserver, msg_bytes) else: # Use the SMTP server (we have host and port information) __send_message_smtp(smtpserver, from_addr, to_addrs, msg_bytes, options) # give recipients a chance of receiving related patches in correct order if type == 'cover' or (type == 'patch' and patch_nr < total_nr): sleep = options.sleep or config.getint('stgit.smtpdelay') time.sleep(sleep) if not options.git: out.done() return msg_id
def patch_name_from_msg(msg): """Return a string to be used as a patch name. This is generated from the top line of the string passed as argument.""" if not msg: return None name_len = config.getint('stgit.namelength') if not name_len: name_len = 30 subject_line = msg.split('\n', 1)[0].lstrip().lower() words = re.sub(r'[\W]+', ' ', subject_line).split() # use loop to avoid truncating the last name name = words and words[0] or 'unknown' for word in words[1:]: new = name + '-' + word if len(new) > name_len: break name = new return name
def patch_name_from_msg(msg): """Return a string to be used as a patch name. This is generated from the top line of the string passed as argument.""" if not msg: return None name_len = config.getint('stgit.namelength') if not name_len: name_len = 30 subject_line = msg.split('\n', 1)[0].lstrip().lower() words = re.sub(r'(?u)[\W]+', ' ', subject_line).split() # use loop to avoid truncating the last name name = words and words[0] or 'unknown' for word in words[1:]: new = name + '-' + word if len(new) > name_len: break name = new return name
def __send_message(type, tmpl, options, *args): """Message sending dispatcher. """ (build, outstr) = { 'cover': (__build_cover, 'the cover message'), 'patch': (__build_message, 'patch "%s"' % args[0]) }[type] if type == 'patch': (patch_nr, total_nr) = (args[1], args[2]) msg_id = email.utils.make_msgid('stgit') msg = build(tmpl, msg_id, options, *args) msg_str = msg.as_string(options.mbox) if options.mbox: out.stdout_raw(msg_str + '\n') return msg_id if not options.git: from_addr, to_addrs = __parse_addresses(msg) out.start('Sending ' + outstr) smtpserver = options.smtp_server or config.get('stgit.smtpserver') if options.git: __send_message_git(msg, options) elif smtpserver.startswith('/'): # Use the sendmail tool __send_message_sendmail(smtpserver, msg_str) else: # Use the SMTP server (we have host and port information) __send_message_smtp(smtpserver, from_addr, to_addrs, msg_str, options) # give recipients a chance of receiving related patches in correct order if type == 'cover' or (type == 'patch' and patch_nr < total_nr): sleep = options.sleep or config.getint('stgit.smtpdelay') time.sleep(sleep) if not options.git: out.done() return msg_id
def __send_message(type, tmpl, options, *args): """Message sending dispatcher. """ (build, outstr) = {'cover': (__build_cover, 'the cover message'), 'patch': (__build_message, 'patch "%s"' % args[0])}[type] if type == 'patch': (patch_nr, total_nr) = (args[1], args[2]) msg_id = email.Utils.make_msgid('stgit') msg = build(tmpl, msg_id, options, *args) msg_str = msg.as_string(options.mbox) if options.mbox: out.stdout_raw(msg_str + '\n') return msg_id if not options.git: from_addr, to_addrs = __parse_addresses(msg) out.start('Sending ' + outstr) smtpserver = options.smtp_server or config.get('stgit.smtpserver') if options.git: __send_message_git(msg, options) elif smtpserver.startswith('/'): # Use the sendmail tool __send_message_sendmail(smtpserver, msg_str) else: # Use the SMTP server (we have host and port information) __send_message_smtp(smtpserver, from_addr, to_addrs, msg_str, options) # give recipients a chance of receiving related patches in correct order if type == 'cover' or (type == 'patch' and patch_nr < total_nr): sleep = options.sleep or config.getint('stgit.smtpdelay') time.sleep(sleep) if not options.git: out.done() return msg_id
def make_name(self, raw, unique=True, lower=True, allow=(), disallow=()): """Make a unique and valid patch name from provided raw name. The raw name may come from a filename, commit message, or email subject line. The generated patch name will meet the rules of `git check-ref-format` along with some additional StGit patch name rules. """ default_name = 'patch' for line in raw.splitlines(): trimmed = line.strip() if trimmed: candidate = trimmed break else: candidate = default_name if lower: candidate = candidate.lower() name = "" prev = '' # Git has a bunch of rules about which ascii symbols are valid # in ref names (see git-check-ref-format(1)), but to keep # generated patch names clean, most ascii symbols are mapped to # '-' in the generated patch name. # # Characters that are valid and _could_ be allowed in patchnames: # ()<>!#$%'"`|;,]}+= # # Characters that are never valid: # ~:^&*[ (along with control characters) # # Characters that are valid in some contexts: # @{/. for c in candidate: if c.isspace() or not c.isprintable(): # Replace all ascii and unicode whitespace and non-printables if prev != '-': name += '-' prev = '-' elif c.isalnum() or c == '_' or not ord(c) < 127: # Accept all ascii alphanumerics and '_' along with # remaining printable, non-whitespace unicode characters. name += c prev = c elif c in '-.': # '.' and '-' are okay, but not consecutively if prev not in '-.': name += c prev = c elif prev not in '-.': # Replace non-alphanumeric ascii chars with '-' name += '-' prev = '-' while True: prev_len = len(name) while name.endswith(".lock"): name = name.rsplit(".lock", 1)[0] name = name.strip('-.') if len(name) == prev_len: break # Might not be anything left after the above stripping if not name: name = default_name len_limit = config.getint('stgit.namelength') if len_limit is None or len_limit <= 0 or len(name) <= len_limit: short_name = name else: words = [w.strip('.') for w in name.split('-')] if words: short_name = words.pop(0) else: short_name = default_name for word in words: if len(short_name) + 1 + len(word) <= len_limit: short_name += '-' + word else: break assert self.is_name_valid(short_name), (candidate, short_name) if not unique: return short_name unique_name = short_name while unique_name not in allow and (unique_name in self or unique_name in disallow): m = re.match(r'(.*?)(-)?(\d+)$', unique_name) if m: base, sep, n_str = m.groups() n = int(n_str) + 1 if sep: unique_name = '%s%s%d' % (base, sep, n) else: unique_name = '%s%d' % (base, n) else: unique_name = '%s-1' % unique_name assert self.is_name_valid(unique_name), (candidate, unique_name) return unique_name
def make_name(self, raw, unique=True, lower=True, allow=(), disallow=()): """Make a unique and valid patch name from provided raw name. The raw name may come from a filename, commit message, or email subject line. The generated patch name will meet the rules of `git check-ref-format` along with some additional StGit patch name rules. """ default_name = 'patch' for line in raw.split('\n'): if line: break if not line: line = default_name if lower: line = line.lower() parts = [] for part in line.split('/'): # fmt: off part = re.sub(r'\.lock$', '', part) # Disallowed in Git refs part = re.sub(r'^\.+|\.+$', '', part) # Cannot start or end with '.' part = re.sub(r'\.+', '.', part) # No consecutive '.' part = re.sub(r'[^\w.]+', '-', part) # Non-word and whitespace to dashes part = re.sub(r'-+', '-', part) # Squash consecutive dashes part = re.sub(r'^-+|-+$', '', part) # Remove leading and trailing dashes # fmt: on if part: parts.append(part) long_name = '/'.join(parts) # TODO: slashes could be allowed in the future. long_name = long_name.replace('/', '-') if not long_name: long_name = default_name assert self.is_name_valid(long_name) name_len = config.getint('stgit.namelength') words = long_name.split('-') short_name = words[0] for word in words[1:]: new_name = '%s-%s' % (short_name, word) if name_len <= 0 or len(new_name) <= name_len: short_name = new_name else: break assert self.is_name_valid(short_name) if not unique: return short_name unique_name = short_name while unique_name not in allow and (self.exists(unique_name) or unique_name in disallow): m = re.match(r'(.*?)(-)?(\d+)$', unique_name) if m: base, sep, n_str = m.groups() n = int(n_str) + 1 if sep: unique_name = '%s%s%d' % (base, sep, n) else: unique_name = '%s%d' % (base, n) else: unique_name = '%s-1' % unique_name assert self.is_name_valid(unique_name) return unique_name
def func(parser, options, args): """Send the patches by e-mail using the patchmail.tmpl file as a template """ smtpserver = config.get('stgit.smtpserver') applied = crt_series.get_applied() if options.all: patches = applied elif len(args) >= 1: unapplied = crt_series.get_unapplied() patches = parse_patches(args, applied + unapplied, len(applied)) else: raise CmdException, 'Incorrect options. Unknown patches to send' smtppassword = options.smtp_password or config.get('stgit.smtppassword') smtpuser = options.smtp_user or config.get('stgit.smtpuser') if (smtppassword and not smtpuser): raise CmdException, 'SMTP password supplied, username needed' if (smtpuser and not smtppassword): raise CmdException, 'SMTP username supplied, password needed' total_nr = len(patches) if total_nr == 0: raise CmdException, 'No patches to send' if options.refid: if options.noreply or options.unrelated: raise CmdException, \ '--refid option not allowed with --noreply or --unrelated' ref_id = options.refid else: ref_id = None sleep = options.sleep or config.getint('stgit.smtpdelay') # send the cover message (if any) if options.cover or options.edit_cover: if options.unrelated: raise CmdException, 'cover sending not allowed with --unrelated' # find the template file if options.cover: tmpl = file(options.cover).read() else: tmpl = templates.get_template('covermail.tmpl') if not tmpl: raise CmdException, 'No cover message template file found' msg_id = email.Utils.make_msgid('stgit') msg = __build_cover(tmpl, total_nr, msg_id, options) from_addr, to_addr_list = __parse_addresses(msg) msg_string = msg.as_string(options.mbox) # subsequent e-mails are seen as replies to the first one if not options.noreply: ref_id = msg_id if options.mbox: out.stdout_raw(msg_string + '\n') else: out.start('Sending the cover message') __send_message(smtpserver, from_addr, to_addr_list, msg_string, sleep, smtpuser, smtppassword) out.done() # send the patches if options.template: tmpl = file(options.template).read() else: tmpl = templates.get_template('patchmail.tmpl') if not tmpl: raise CmdException, 'No e-mail template file found' for (p, patch_nr) in zip(patches, range(1, len(patches) + 1)): msg_id = email.Utils.make_msgid('stgit') msg = __build_message(tmpl, p, patch_nr, total_nr, msg_id, ref_id, options) from_addr, to_addr_list = __parse_addresses(msg) msg_string = msg.as_string(options.mbox) # subsequent e-mails are seen as replies to the first one if not options.noreply and not options.unrelated and not ref_id: ref_id = msg_id if options.mbox: out.stdout_raw(msg_string + '\n') else: out.start('Sending patch "%s"' % p) __send_message(smtpserver, from_addr, to_addr_list, msg_string, sleep, smtpuser, smtppassword) out.done()
def func(parser, options, args): """Send the patches by e-mail using the patchmail.tmpl file as a template """ smtpserver = options.smtp_server or config.get('stgit.smtpserver') applied = crt_series.get_applied() if options.all: patches = applied elif len(args) >= 1: unapplied = crt_series.get_unapplied() patches = parse_patches(args, applied + unapplied, len(applied)) else: raise CmdException, 'Incorrect options. Unknown patches to send' # early test for sender identity __get_sender() out.start('Checking the validity of the patches') for p in patches: if crt_series.empty_patch(p): raise CmdException, 'Cannot send empty patch "%s"' % p out.done() smtppassword = options.smtp_password or config.get('stgit.smtppassword') smtpuser = options.smtp_user or config.get('stgit.smtpuser') smtpusetls = options.smtp_tls or config.get('stgit.smtptls') == 'yes' if (smtppassword and not smtpuser): raise CmdException, 'SMTP password supplied, username needed' if (smtpusetls and not smtpuser): raise CmdException, 'SMTP over TLS requested, username needed' if (smtpuser and not smtppassword): smtppassword = getpass.getpass("Please enter SMTP password: "******"%s"' % p) __send_message(smtpserver, from_addr, to_addr_list, msg_string, smtpuser, smtppassword, smtpusetls) # give recipients a chance of receiving related patches in the # correct order. if patch_nr < total_nr: time.sleep(sleep) out.done()