CVSROOT collect_diffs.rb,NONE,1.1 cvsspam.rb,NONE,1.1 record_lastdir.rb,NONE,1.1 checkoutlist,1.4,1.5

Sebastian Bergmann <[email protected]>
Newsgroups gmane.comp.web.phpopentracker.cvs
Message-ID <[email protected]>
Update of /cvsroot/phpopencounter/CVSROOT
In directory sc8-pr-cvs1:/tmp/cvs-serv10621

Modified Files:
	checkoutlist 
Added Files:
	collect_diffs.rb cvsspam.rb record_lastdir.rb 
Log Message:
Import CVSspam.

--- NEW FILE: record_lastdir.rb ---
#!/usr/bin/ruby -w

# Part of CVSspam
#   http://www.badgers-in-foil.co.uk/projects/cvsspam/
# Copyright (c) David Holroyd

$repositorydir = ARGV.shift

$tmpdir = ENV["TMPDIR"] || "/tmp"

# try to pick a name to avoid collisions with other people's commits
$dirtemplate = "#cvsspam.#{Process.getpgrp}.#{Process.uid}"

def find_data_dir
  Dir["#{$tmpdir}/#{$dirtemplate}-*"].each do |dir|
    stat = File.stat(dir)
    return dir if stat.owned?
  end
  nil
end

$datadir = find_data_dir()

if $datadir==nil
  $datadir = "#{$tmpdir}/#{$dirtemplate}-#{rand(99999999)}"
  Dir.mkdir($datadir, 0700)
end

$tags = nil

# Record any tag name found in 'Entries' for files being commited.  This is
# required at pre-commit-time as we have no other way of dertermining what
# branch a file was on if it's been removed.
#
# If the commitinfo-tags file exists from a previous, unsuccessful commit,
# then it's possible for it to contain multiple entries for a particular file.
# The consumer of commitinfo-tags must take care to only use the last entry
# for a given filename.

def write_tag(name, file)
  if $tags.nil?
    $tags = File.new("#{$datadir}/commitinfo-tags", File::WRONLY|File::CREAT|File::APPEND);
    return if $tags.nil?
  end
  $tags.puts("#{name}\t#{file}")
end

File.open("CVS/Entries") do |file|
  file.each_line do |line|
    next if line =~ /^D/
    info = line.split(/\//)
    # skip entries not commited this invocation,
    if ARGV.delete(info[1]).nil?
      next
    end
    if info[5] =~ /^T(.+)/
      write_tag($1, "#{$repositorydir}/#{info[1]}")
    end
  end
end

$tags.close unless $tags.nil?

unless ARGV.empty?
  $stderr.puts "Nothing in CVS/Entries for "+ARGV.join(", ")
end


# Record the directory currently being commited to.
# 
# This script (and collect_diffs.rb) will be run just for the files in a
# single directory.
# 
# A commit to files in multiple directories will therefore produce multiple
# invocations of these scripts.  To send the email only when the whole commit
# is done, each run overwrites the 'lastdir' file; collect_diffs.rb will
# later inspect the value it contains to work out if it needs to generate the
# email yet.

File.open("#{$datadir}/lastdir", "w") { |file|
	file.write $repositorydir
}

--- NEW FILE: cvsspam.rb ---
#!/usr/bin/ruby -w

# Part of CVSspam
#   http://www.badgers-in-foil.co.uk/projects/cvsspam/
# Copyright (c) David Holroyd

# collect_diffs.rb expects to find this script in the same directory as it
#

# TODO: exemplify syntax for 'cvs admin -m' when log message is missing
# TODO: make max-line limit on diff output configurable
# TODO: put max size limit on whole email
# TODO: support non-html mail too (text/plain, multipart/alternative)

# If you want another 'todo keyword' (TODO & FIXME are highlighted by default)
# you could add
#   $task_keywords << "KEYWORD" << "MAYBEANOTHERWORD"
# to your cvssppam.conf

[...1323 lines suppressed...]
mailer.send($from_address, $recipients) do |mail|
  mail.header("Subject", mailSubject)
  mail.header("MIME-Version", "1.0")
  mail.header("Content-Type", "text/html" + ($charset.nil? ? "" : "; charset=\"#{$charset}\""))
  if ENV['REMOTE_HOST']
    # TODO: I think this will always be an IP address.  If a hostname is
    # possible, it may need encoding of some kind,
    mail.header("X-Originating-IP", "[#{ENV['REMOTE_HOST']}]")
  end
  unless ($additionalHeaders.empty?)
    $additionalHeaders.each do |header|
      mail.header(header[0], header[1])
    end
  end
  mail.header("X-Mailer", "CVSspam #{$version} <http://www.badgers-in-foil.co.uk/projects/cvsspam/>")

  mail.body do |body|
    make_html_email(body)
  end
end

--- NEW FILE: collect_diffs.rb ---
#!/usr/bin/ruby -w

# Part of CVSspam
#   http://www.badgers-in-foil.co.uk/projects/cvsspam/
# Copyright (c) David Holroyd

#  CVSROOT is /var/lib/cvs
#  ARGV is 'CVSROOT/foo/space dir space file,NONE,1.1 some,file,1.4,1.5'
#  ----
#  Update of /var/lib/cvs/CVSROOT/foo/space dir
#  In directory foil:/tmp/cvs-serv13059
#  
#  Modified Files:
#  	some,file 
#  Added Files:
#  	space file 
#  Log Message:
#  msg
#  


# Assumptions
# - file names do not contain newlines or single quotes


$tmpdir = ENV["TMPDIR"] || "/tmp"
$dirtemplate = "#cvsspam.#{Process.getpgrp}.#{Process.uid}"

def find_data_dir
  Dir["#{$tmpdir}/#{$dirtemplate}-*"].each do |dir|
    stat = File.stat(dir)
    return dir if stat.owned?
  end
  nil
end


def blah(msg)
  if $debug
    $stderr.puts "collect_diffs.rb: #{msg}"
  end
end


# Like IO.popen, but accepts multiple arguments like Kernel.exec
# (So no need to escape shell metacharacters)
def safer_popen(*args)
  IO.popen("-") do |pipe|
    if pipe==nil
      exec(*args)
    else
      yield pipe
    end
  end
end

class ChangeInfo
  def initialize(file, fromVer, toVer)
    @file, @fromVer, @toVer = file, fromVer, toVer
    if fromVer == toVer
      fail "'from' and 'to' versions should be different ('#{fromVer}')"
    end
  end
  attr_reader :file, :fromVer, :toVer
  def to_s
    "<ChangeInfo \"#{@file}\" #{@toVer}<--#{@fromVer}>"
  end

  def isAddition ; fromVer == 'NONE' end

  def isRemoval ; toVer == 'NONE' end

  #def isModification ; !(isAddition || isRemoval) end
end

$commitinfo_tags = nil

def get_commitinfo_tag(filename)
  if $commitinfo_tags.nil?
    return nil unless FileTest.exists?("#{$datadir}/commitinfo-tags")
    File.open("#{$datadir}/commitinfo-tags") do |file|
      $commitinfo_tags = Hash.new
      file.each_line do |line|
	line =~ /([^\t]+)\t(.+)/
	key = $2
	val = $1
	key.sub!(/^#{ENV['CVSROOT']}\//, '')
	$commitinfo_tags[key] = val
      end
    end
  end
  return $commitinfo_tags[filename]
end

def process_log(cvs_info)
  cvsroot = ENV['CVSROOT']

  $datadir = find_data_dir()

  raise "missing data dir (#{$tmpdir}/#{$dirtemplate}-XXXXXX)" if $datadir==nil

  line = $stdin.gets
  unless line =~ /^Update of (.+)/
    fail "Log preamble looks suspect (doesn't start 'Update of ...')"
  end

  # cvs_info comes from the command line, ultimately as the expansion of the
  # %{sVv} in $CVSROOT/loginfo.  It isn't possible to parse this value
  # unambiguously, but we make an effort to get it right in as many cases as
  # possible.

  $path = $1
  unless $path.slice(0,cvsroot.length) == cvsroot
    fail "CVSROOT ('#{cvsroot}') doesn't match log preamble ('#{$path}')"
  end

  $repository_path = $path.slice(cvsroot.length+1, $path.length-cvsroot.length-1)
  unless cvs_info.slice(0, $repository_path.length+1) == "#{$repository_path} "
    fail "calculated repository path ('#{$repository_path}') doesn't match start of command line arg ('#{cvs_info}')"
  end

  version_info = cvs_info.slice($repository_path.length,
                                cvs_info.length-$repository_path.length)

  changes = Array.new
  # make a list of changed files given on the command line
  while version_info.length>0
    if version_info.sub!(/^ (.+?),(NONE|[.0-9]+),(NONE|[.0-9]+)/, '') == nil
      fail "'#{version_info}' doesn't match ' <name>,<ver>,<ver> ...'"
    end
    changes << ChangeInfo.new($1, $2, $3)
  end

  # look for the start of the user's comment
  $stdin.each do |line|
    break if line =~ /^Log Message/
  end

  unless line =~ /^Log Message/
    fail "Input did not contain a 'Log Message:' entry"
  end

  File.open("#{$datadir}/logfile", File::WRONLY|File::CREAT|File::APPEND) do |file|
    $stdin.each do |line|
      file.puts "#> #{line}"
    end

    changes.each do |change|

      # record version information
      file.puts "#V #{change.fromVer},#{change.toVer}"

      # note if the file is on a branch
      tag = nil
      if change.isRemoval
	tag = get_commitinfo_tag("#{$repository_path}/#{change.file}")
      else
        status = nil
        safer_popen($cvs_prog, "-nq", "status", change.file) do |io|
          status = io.read
        end
        fail "couldn't get cvs status: #{$!}" unless ($?>>8)==0

	if status =~ /^\s*Sticky Tag:\s*(.+) \(branch: +/m
	  tag = $1
	end
      end
      file.puts "#T #{tag}" unless tag.nil?

      diff_cmd = Array.new << $cvs_prog << "-nq" << "diff" << "-Nu"
      diff_cmd << "-kk" if $diff_ignore_keywords

      if change.isAddition
        file.write "#A "
        # cruft up a date in the distant past, when the file would not have
        # existed, so that the diff will show all lines as added
        diff_cmd << "-D1/26/1977" << "-r#{change.toVer}"
      elsif change.isRemoval
        file.write "#R "
        # just specifying one version, cvs will diff between that version and
        # the current version (will show all lines removed)
        diff_cmd << "-r#{change.fromVer}"
      else
        file.write "#M "
        diff_cmd << "-r#{change.fromVer}" << "-r#{change.toVer}"
      end
      file.puts "#{$repository_path}/#{change.file}"
      diff_cmd << change.file
      # do a cvs diff and place the output into our temp file
      blah("about to run #{diff_cmd.join(' ')}")
      safer_popen(*diff_cmd) do |pipe|
        # skip over cvs-diff's preamble
        pipe.each do |line|
          break if line =~ /^diff /
        end
        file.puts "#U #{line}"
        pipe.each do |line|
          file.puts "#U #{line}"
        end
      end
      # TODO: don't how to do this reliably on different systems...
      #fail "cvsdiff did not give exit status 1 for invocation: #{diff_cmd.join(' ')}" unless ($?>>8)==1
    end
  end

end


def choose_operation(op)
  if op =~ / - New directory$/
    blah("No action taken on directory creation")
  elsif op =~ / - Imported sources$/
    blah("Imported not handled")
  else
    process_log(op)
    mailtest
  end
end

def mailtest
  lastdir = nil
  File.open("#{$datadir}/lastdir") do |file|
    lastdir = file.gets
  end
  if $path == lastdir
    blah("sending spam.  (I am #{$0})")
    # REVISIT: $0 will not contain the path to this script on all systems
    cmd = File.dirname($0) + "/cvsspam.rb"
    unless system(cmd, "#{$datadir}/logfile", *$passthoughArgs)
      fail "problem running '#{cmd}'"
    end
    if $debug
      blah("leaving file #{$datadir}/logfile")
    else 
      File.unlink("#{$datadir}/logfile")
    end
    File.unlink("#{$datadir}/lastdir")
    File.unlink("#{$datadir}/commitinfo-tags") if FileTest.exists?("#{$datadir}/commitinfo-tags")
    Dir.rmdir($datadir) unless $debug
  else
    blah("not spam time yet, #{$path}!=#{lastdir}")
  end
end

$config = nil
$cvs_prog = "cvs"
$debug = false
$diff_ignore_keywords = false
$task_keywords = []

unless ENV.has_key?('CVSROOT')
  fail "$CVSROOT not defined.  It should be when I am invoked from CVSROOT/loginfo"
end


require 'getoptlong'

opts = GetoptLong.new(
  [ "--to",     "-t", GetoptLong::REQUIRED_ARGUMENT ],
  [ "--config", "-c", GetoptLong::REQUIRED_ARGUMENT ],
  [ "--debug",  "-d", GetoptLong::NO_ARGUMENT ],
  [ "--from",   "-u", GetoptLong::REQUIRED_ARGUMENT ]
)

# arguments to pass though to 'cvsspam.rb'
$passthoughArgs = Array.new
opts.each do |opt, arg|
  if ["--to", "--config", "--from"].include?(opt)
    $passthoughArgs << opt << arg
  end
  if ["--debug"].include?(opt)
    $passthoughArgs << opt
  end
  $config = arg if opt=="--config"
  $debug = true if opt == "--debug"
end

blah("CVSROOT is #{ENV['CVSROOT']}")
blah("ARGV is '#{ARGV.join(', ')}'")

if $config == nil
  if FileTest.exists?("#{ENV['CVSROOT']}/CVSROOT/cvsspam.conf")
    $config = "#{ENV['CVSROOT']}/CVSROOT/cvsspam.conf"
  elsif FileTest.exists?("/etc/cvsspam/cvsspam.conf")
    $config = "/etc/cvsspam/cvsspam.conf"
  end

  if $config != nil
    $passthoughArgs << "--config" << $config
  end
end

if $config != nil
  if FileTest.exists?($config)
    def addHeader(name,val)
    end
    def addRecipient(who)
    end
    class GUESS
    end
    load $config
  else
    blah("Config file '#{$config}' not found, ignoring")
  end
end

if ARGV.length != 1
  $stderr.puts "Expected arguments missing"
  $stderr.puts "* You shouldn't run collect_diffs by hand, but from a CVSROOT/loginfo entry *"
  $stderr.puts "Usage: collect_diffs.rb [ --to <email> ] [ --config <file> ] %{sVv}"
  $stderr.puts "       (the sequence '%{sVv}' is expanded by CVS, when found in CVSROOT/loginfo)"
  exit
end
choose_operation(ARGV[0])

Index: checkoutlist
===================================================================
RCS file: /cvsroot/phpopencounter/CVSROOT/checkoutlist,v
retrieving revision 1.4
retrieving revision 1.5
diff -C2 -d -r1.4 -r1.5
*** checkoutlist	20 Sep 2003 16:00:42 -0000	1.4
--- checkoutlist	11 Dec 2003 19:18:27 -0000	1.5
***************
*** 14,15 ****
--- 14,19 ----
  cia-bot.pl
  syncmail
+ record_lastdir.rb
+ collect_diffs.rb
+ cvsspam.rb
+ 




-------------------------------------------------------
This SF.net email is sponsored by: SF.net Giveback Program.
Does SourceForge.net help you be more productive?  Does it
help you create better code?  SHARE THE LOVE, and help us help
YOU!  Click Here: http://sourceforge.net/donate/
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.