# HG changeset patch # User HIROSE Yuuji # Date 1339341280 -32400 # Node ID 1b16890d61d68d9099e6061b2388b00538847c00 # Parent 1c0505ce5d6a250fb0c104a8a9c0d170c254e949 ML: digited subject, ML-specific email addresses, spooling diff -r 1c0505ce5d6a -r 1b16890d61d6 after5.rb --- a/after5.rb Wed May 30 11:49:20 2012 +0900 +++ b/after5.rb Mon Jun 11 00:14:40 2012 +0900 @@ -4,7 +4,7 @@ # Associative Scheduling Table - after5 # (C)2003, 2004, 2006, 2008, 2012 by HIROSE Yuuji [yuujigentei.org] # $Id: after5.rb,v 1.19 2012/04/01 11:52:25 yuuji Exp yuuji $ -# Last modified Wed May 30 11:47:39 2012 on firestorm +# Last modified Mon Jun 11 00:12:27 2012 on firestorm # See http://www.gentei.org/~yuuji/software/after5/ # このスクリプトはEUCで保存してください。 $hgid = <<_HGID_.split[1..-2].join(" ") @@ -340,6 +340,10 @@ return user.sub(/@.*/, '') end end + def mailaddress(user, grp = nil) + grp ? mail4grp(user, grp) : \ + (getuserattr(user, 'email') || user) + end def setnickname(user, nickname) putuserattr(user, 'name', nickname) end @@ -407,7 +411,7 @@ if test(?d, memd) Dir.foreach(memd){|a| next if /^\./ =~ a - map[g]['members'] << a + map[g]['members'] << a.untaint } end } @@ -423,6 +427,9 @@ grp = groups.grep(group)[0] # group may be tainted, using kept name return nil unless grp for u in users + m = nil + u, m = u if u.is_a?(Array) # ["user", "mailto"] + m = nil if mailaddress(u)==m || /@/ !~ m next unless account_exists(u) mdir = File.join(@groupmapdir, grp, role).untaint file = File.join(mdir, u).untaint @@ -433,7 +440,7 @@ @groupmap[grp][role] << u @groupmap[grp][role].uniq Dir.mkdir(file) unless test(?d, mdir) - open(file, "w"){|x|} # Touch it + open(file, "w"){|x|x.puts m if m} end end grp @@ -479,7 +486,8 @@ return nil if %r@[\/()\;|,$\%^!\#&\'\"]@ =~ user email = email || user @usermap[user] = {} - Dir.mkdir(File.join(@usermapdir, user)) + dir = File.join(@usermapdir, user).untaint + test(?d, dir) || Dir.mkdir(dir) putuserattr(user, 'email', email) end def deleteuser(user) @@ -523,10 +531,19 @@ ! @groupmap.select{|k, v| k==instance}.empty? end end + def mail4grp(usr, group) + file = File.expand_path((group+"/members/"+usr).untaint, @groupmapdir) + if test(?s, file.untaint) + open(file, "r"){|f|f.gets.chomp}.untaint + else + mailaddress(usr) + end + end def ismember(user, grouporuser) - return true if user==grouporuser + return user if user==grouporuser if @groupmap[grouporuser] - @groupmap[grouporuser]['members'].grep(user)[0] + @groupmap[grouporuser]['members'].grep(user)[0] && + mail4grp(user, grouporuser) end end def isuser(user) @@ -1055,7 +1072,7 @@ 'pswddb' => 's/a5pswd', 'lang' => 'j', 'notifymail' => true, - 'mailbracket' => '[%s-ML]', + 'mailbracket' => '[%n-ML]', } @ntlist = [ ['nt10m', "10"+msg('minutes', 'before')], @@ -1403,9 +1420,8 @@ def outputError(*msg) @O.print @H.p(msg('error')+sprintf(*msg)) end - def mailaddress(user) - email = @sc.getuserattr(user, "email") - email || user + def mailaddress(user, grp=nil) + @sc.mailaddress(user, grp) end def webpage(user) @sc.getuserattr(user, "webpage") @@ -1450,6 +1466,7 @@ throw :auth, nil end elsif passwd == '' + # Create new user from Web-UI newp = pm.setnewpasswd(user, @opt['pswdlen']) @sc.createuser(user, user) sendMail(email, "#{@mybase} new account", @@ -1532,7 +1549,33 @@ end end - def sendMail(to, subject, body, from=nil, rcptto=nil, header={}, thru=nil) + def dospool(dir, outhandle) + seq=1 + seqfile=File.expand_path("seq", dir).untaint + spooldir=File.expand_path("spool", dir).untaint + test(?d, spooldir) or Dir.mkdir(spooldir) + if test(?s, seqfile) + seq=open(seqfile, "r"){|s|s.gets.to_i} + end + seq+=1 while test(?s, (newfile=sprintf("%s/%d", spooldir, seq))) + open(newfile, "w") do |spoolfile| + countdone = nil + while line=STDIN.gets + if !countdone && /^X-ML-Name: / =~ line + line += sprintf("X-Mail-Count: %d\n", seq) + coutndone=true + end + spoolfile.print line + outhandle.print line + end + end + open(seqfile, "w"){|s| s.puts seq.to_s} # update `seq' file + end + def mlseq(dir) + open(dir+"/seq", "r"){|s|s.gets.to_i+1} + end + def sendMail(to, subject, body, from=nil, rcptto=nil, header={}, + thru=nil, spoolto=false) body = NKF.nkf("-j", body) unless thru subject = NKF.nkf("-jM", subject.strip) to = safecopy(to) # cleanup tainted address @@ -1557,16 +1600,29 @@ # exec(@attr['mail'], "-s", subject, to) recipient = rcptto || [to] #p recipient - if ENV['MAILCMD'] - #exec("qmail-inject", "yuuji@gentei.org", "yuuji@koeki-u.ac.jp") - open("/tmp/body", "w") {|w| w.print STDIN.readlines.join - w.puts "---" - w.puts recipient.join(",\n") - } - exit 0 - else - #recipient.unshift "-f"+header['return-path'] if header['return-path'] - exec(ENV['MAILCMD'] || @opt['sendmail'], *recipient) + if spoolto && spoolto.is_a?(String) && + proc { + require 'fileutils' + begin + test(?d, spoolto) or FileUtils.mkdir_p(spoolto) + test(?w, spoolto) + rescue + nil + end}.call && + (tee=open("|-", "w")) # popen should be done in if-condition + dospool(spoolto, tee) + else + if ENV['MAILCMD'] + #exec("qmail-inject", "yuuji@gentei.org", "yuuji@koeki-u.ac.jp") + open("/tmp/body", "w") {|w| w.print STDIN.readlines.join + w.puts "---" + w.puts recipient.join(",\n") + } + exit 0 + else + #recipient.unshift "-f"+header['return-path'] if header['return-path'] + exec(ENV['MAILCMD'] || @opt['sendmail'], *recipient) + end end exit 0; end @@ -2529,7 +2585,7 @@ exit 0 end # ML functions - def tagify_subj(body, tag) + def tagify_subj(body, tag, removeregexp) # This method should be generic for other headers than `Subject'? hold = [] ret = [] @@ -2549,6 +2605,7 @@ else if skip then sj = hold.join.toeuc.sub("Subject: ", "").gsub(tag, "").strip + sj.gsub!(removeregexp, "") sj = sj.sub(/^(re: *)+/i, "Re: ").gsub("\n", "") ret << "Subject: "+NKF.nkf('-jM', tag+" "+sj).strip+"\n" else @@ -2580,22 +2637,30 @@ name = ENV['DEFAULT'] else # via http return nil unless checkauth - name = @params['name'] + name = @params['name'].untaint nick = @sc.nickname(@params['user']) from = sprintf("%s <%s>", nick, @params['user']) body = @params['body'].gsub("\r", "").untaint end - to = sprintf("%s-%s@%s", @opt['mailprefix'], name, @opt['maildomain']) + mldir = "ml/"+name + prefix = (@opt['mailprefix'] || "") + dash = prefix > '' ? "-" : "" + to = sprintf("%s%s%s@%s", prefix, dash, name, @opt['maildomain']) subj = @params['subject'] || "Message from "+@myname - sjtag = sprintf(@opt['mailbracket'], nickname(name)) - subj = sjtag.strip+" "+subj.gsub(sjtag, "") + sjtag = @opt['mailbracket'].gsub("%n", nickname(name)). + gsub("%i", name). + gsub(/%(\d*)c/){("%0"+$1+"d") % [mlseq(mldir)]} + tagpt = Regexp.quote(@opt['mailbracket']). # compute bracket pattern + gsub("%n", Regexp.quote(nickname(name))). + gsub("%i", Regexp.quote(name)). + gsub(/%(\d*)c/, '\d+') + subj = sjtag.strip+" "+subj.gsub(Regexp.new(tagpt), "") if viamail then - body = tagify_subj(STDIN.readlines, sjtag).join - # body = STDIN.readlines.join + body = tagify_subj(STDIN.readlines, sjtag, Regexp.new(tagpt)).join end header = { "Reply-to" => to, - "X-ML-Driver" => $hgid, + "X-ML-Driver" => ($hgid || @myname), "X-ML-Driver-URI" => $myurl, "X-ML-Name" => name, "Errors-to" => @opt['maintainer'], @@ -2605,12 +2670,13 @@ @sc.members(name) else [name] - end.collect {|u| mailaddress(u)} + end.collect {|u| mailaddress(u, name)} # p rcpts # p to sendMail(to, subj, body, from, rcpts, header, - ENV['SENDER']) + ENV['SENDER'], + @opt['mlspooling'] ? mldir : nil) if !viamail then @O.print @H.elementln("h1"){msg('sendall_done')} @O.print @H.p(sprintf(msg('sendall_head'), @@ -2833,6 +2899,7 @@ @H.elementln("form", {'action'=>@myname+"?-groupmod", 'method'=>'POST'}){ @H.elementln("table", {'border'=>'1', 'vertical-align'=>'top'}){ grmap.collect{|g, ghash| + memberp = @sc.ismember(user, g) @H.elementln("tr"){ @H.element("td", @sc.isadmin(user, g) ? admclass : nil){ g @@ -2847,7 +2914,6 @@ } } + \ @H.element("td"){ - memberp = @sc.ismember(user, g) if ghash['admin'].grep(user)[0] @H.text("groupname-#{g}", ghash['name'], nil, 20) else @@ -2857,8 +2923,14 @@ @H.radio("groupadd-#{g}", "no", "OUT", !memberp) } + \ @H.element("td"){ - ghash['members'].collect{|u| - @sc.nickname(u) + memlist = ghash['members'] + if memberp # move this user to the beginning of list + memlist.delete(user) + memlist.unshift(user) + end + memlist.collect{|u| + @sc.nickname(u) + \ + ((u == user) ? ("("+@H.text("mail4-#{g}", memberp, 30, 80)+")") : "") }.join(", ") } + \ @H.element("td"){ @@ -2903,6 +2975,18 @@ end end # + # as a member, change group-specific mailto address. + key = "mail4-#{grp}" + if memberp && @params[key] && memberp != @params[key] + @sc.addgroup(grp, [[user, @params[key]]]) + newmemp = @sc.ismember(user, grp) + @O.print @H.elementln("p") { + sprintf("%s `%s' %s => %s%s", + msg('group'), grp, msg('mailaddress'), @params[key], + @params[key]==mailaddress(user) ? "(same)" : "") + } + end + # # as a owner, change the name of group if @sc.isadmin(user, grp) && (newname = @params["groupname-#{grp}"]) && @@ -3462,7 +3546,12 @@ prohibitviahttp() pm = open_pm() exit 1 unless pm + email = nil + if /(.*@.*)=(.*@.*)/ =~ user + user, email = $1, $2 + end newpwd = pm.setnewpasswd(user, 4) + @sc.createuser(user, email) print "#{user}'s password is #{newpwd}\n" pm.close() exit 0 @@ -3472,6 +3561,7 @@ pm = open_pm() exit 1 unless pm pm.delete(user) + @sc.deleteuser(user) pm.close() exit 0 end @@ -3497,7 +3587,7 @@ when /^(no|none|null|nil|false|0|off)$/io @opt[key] = nil else - @opt[key] = value + @opt[key] = value.untaint end print "#{key} set to #{value}\n" if $DEBUG end