changeset 85:e2b6a2e8b5c7 draft

Enable inter-member mail Support attachment files
author HIROSE Yuuji <yuuji@gentei.org>
date Mon, 16 Dec 2013 10:41:21 +0900
parents f67f5304baac
children 26c81703a80c
files after5.rb
diffstat 1 files changed, 194 insertions(+), 47 deletions(-) [+]
line wrap: on
line diff
--- a/after5.rb	Thu Dec 12 19:57:20 2013 +0900
+++ b/after5.rb	Mon Dec 16 10:41:21 2013 +0900
@@ -4,7 +4,7 @@
 # Associative Scheduling Table - after5
 # (C)2003, 2004, 2006, 2008, 2012, 2013 by HIROSE Yuuji [yuuji<at>gentei.org]
 # $Id: after5.rb,v 1.20 2012/12/03 15:54:20 yuuji Exp $
-# Last modified Thu Dec 12 18:09:01 2013 on firestorm
+# Last modified Mon Dec 16 10:38:55 2013 on firestorm
 # See http://www.gentei.org/~yuuji/software/after5/
 # このスクリプトはEUCで保存してください。
 $hgid = <<_HGID_.split[1..-2].join(" ")
@@ -62,7 +62,7 @@
     lf = nl ? "\n" : ""
     if attrs.is_a?(Hash)
       for k in attrs.keys
-	attr += " %s=\"%s\"" % [k, attrs[k]]
+        attr += " %s=\"%s\"" % [k, attrs[k]]
       end
     end
     body = yield
@@ -1099,6 +1099,9 @@
     @schedulearea = {'rows'=>'4', 'cols'=>'60', 'name'=>'schedule'}
     @oldagent = (%r,Mozilla/4, =~ ENV['HTTP_USER_AGENT'])
     @lang = 0
+    @mlbasedir = "ml"
+    @attachmentdir = "a"
+    @attachmentmax = 8*1024**2
     @mailmode = nil
     @mailadmdelimiter = "/"
     @mailadmsuffix = @mailadmdelimiter + "adm"
@@ -1355,6 +1358,7 @@
                             "Send this message to all of group."],
         'sendall_done'	=> ['送信完了', "sending message done"],
         'body'		=> ['本文', 'Body'],
+	'rcptto'	=> ['宛先', 'Recipients'],
 	'member'	=> ['メンバー', 'Member'],
 	'personalmode'	=> ['自分のだけ表示モード', 'Display Personal Only'],
 	'normalmode'	=> ['全員分表示モード', "Display Everyone's"],
@@ -1681,7 +1685,7 @@
                thru=nil, spoolto=false)
     # rcptto should be an Array
     body = NKF.nkf("-j", body) unless thru
-    subject = NKF.nkf("-jM", subject.strip)
+    subject = NKF.nkf("-jM", (subject||"No subject").strip)
     to = safecopy(to)		# cleanup tainted address
     subject.gsub!(/\n/, '')
     begin
@@ -1699,7 +1703,7 @@
           # m.puts "Date: #{Time.now.strftime("%a, %d %b %Y %T %z")}"
           m.print "\n"
         end
-	m.print body, "\n"
+	m.write body
 	m.close
       else
 	# exec(@attr['mail'], "-s", subject, to)
@@ -1723,10 +1727,25 @@
             open("/tmp/body", "w") {|w| w.print STDIN.readlines.join
               w.puts "---"
               w.puts recipient.join(",\n")
+              w.puts header.inspect
+              w.puts "ENV: #{ENV.inspect}"
             }
             exit 0
+          elsif header['Return-path'] && /-@/ =~ header['Return-path']
+            # if VERP is requested
+            mailcmd = ENV['MAILCMD'] || @opt['sendmail']
+            contents = STDIN.readlines
+            recipient.uniq.each {|r|
+              local, domain = header['Return-path'].split("@")
+              newlocal = local+r.sub("@", "=")
+              verp = newlocal+"@"+domain
+              verp = safecopy(verp)
+              open("| #{mailcmd} -f#{verp} -- #{r}", "w") {|m|
+                m.write contents.join
+              }
+            }
           else
-            #recipient.unshift "-f"+header['return-path'] if header['return-path']
+            recipient.unshift "-f"+header['Return-path'] if header['Return-path']
             exec(ENV['MAILCMD'] || @opt['sendmail'], *recipient)
           end
         end
@@ -2722,11 +2741,12 @@
     skip = false
     while line = body.shift
       case line.toeuc
-      when /^$/
-        hold << "\n"
-        break
-      ## when /^(subject|from): /i
-      when /^(\S+): /i             # if new header comes
+  # #Below does not work correctly when (from|subject): is final line
+  #    when /^$/
+  #      hold << "\n"
+  #      break
+  #    ## when /^(subject|from): /i
+      when /^(\S+): /i, /^$/            # if new header comes or header ends
         if /^subject:/i =~ hold[0] # check previous header in hold space
           sj = hold.join.toeuc.sub("Subject: ", "").gsub(tag, "").strip
           removeregexp && sj && sj.gsub!(removeregexp, "")
@@ -2737,10 +2757,14 @@
           email, comment = parseaddress(from)
           if (!comment || comment=="") && comment = @sc.ismembersemail(email)
             # Reverse conversion of uname<->email
-            comment = @sc.nickname(comment) || ""
+            comment = @sc.nickname(comment) || "whoareyou"
           end
           hold = ["From: "+rewritefrom(email, comment, fromhack)+"\n"]
         end
+        if /^$/ =~ line.toeuc
+          hold << line
+          break
+        end
         ret += hold
         hold = [line]
       when /^\s/                # continued line
@@ -2749,6 +2773,51 @@
     end
     ret + hold + body
   end
+  def extract_attachment(attachment)
+    # Must return [text, href] strings to attached files
+    href = ""; text = ""
+    dir = @attachmentdir
+    if %r,(https?://[^/]+), =~ @opt['url']
+      server = $1
+    else
+      msg = "`url' not set in after5.cf"
+      return [msg, msg]
+    end
+    
+    urlbase = sprintf("%s%s/%s",
+                      server, File.dirname(ENV['SCRIPT_NAME']), dir)
+    count=0
+    attachment.each {|a|
+      basename = safecopy(File.basename(a['filename']))
+      filename = safecopy(dir+"/"+basename)
+      umask = File.umask(022)
+      sz = a['value'].bytesize
+      next if sz == 0
+      count += 1
+      if sz > @attachmentmax
+        msg = sprintf("%d: %s is too large(%dMB), skipped\n",
+                        count, basename, sz/1024**2)
+        text += msg; href += msg
+        next
+      end
+      begin
+        (test(?d, dir) && test(?w, dir)) or Dir.mkdir(dir)
+        open(filename, "w") {|x| x.write a['value']}
+        File.chmod(0664, filename)
+        text += sprintf("%d: %s/%s\n", count, urlbase, basename)
+        href += sprintf("%d: <a href=\"%s\">%s/%s</a>\n",
+                        count, filename, urlbase, basename)
+      rescue
+      ensure
+        File.umask(umask)
+      end
+    }
+    if count>0
+      text="\n\n[[Attached files below]]\n"+text
+      href="[[Attached files]]\n"+href
+    end
+    [text.chomp, href.chomp]
+  end
   def defaultmladdress(name)
     prefix = (@opt['mailprefix'] || "")
     dash = prefix > '' ? "-" : ""
@@ -2759,7 +2828,7 @@
     #  LOCAL=1 DEFAULT=name ./after5.rb -list
     # $DEFAULT is ML name
     viamail = ENV['LOCAL'] && ENV['DEFAULT'] # called via mail
-    from = toadmin = groupmode = fromhack = nil
+    from = toadmin = groupmode = fromhack = extract_report = nil
     unless @opt['mailprefix'] && @opt['maildomain']
       if viamail
         STDERR.print msg('sendall_err') % [@opt['conf']]
@@ -2772,6 +2841,7 @@
     if viamail then
       prohibitviahttp()
       name = unquoted(ENV['DEFAULT'])
+      user = @sc.ismembersemail(ENV['SENDER'])
       if Regexp.new("(.*)("+Regexp.quote(@mailadmsuffix)+")") =~ name
         # To: GROUP/adm*@domain
         #   -> Forward to group administrator(s)
@@ -2798,7 +2868,11 @@
       end
     else                        # via http
       return nil unless checkauth
-      name = unquoted(@params['name'].untaint)
+      # HERE CGI Params name, subject, attachment and body
+      # comes with enctype=multipart/form-data,
+      # So we get them via Array
+      name = unquoted(@params['name'][0]['value'].untaint)
+      user = @params['user']
       if @sc.isuser(name)
         # groupmode = nil
       elsif grepgroup(name)
@@ -2807,39 +2881,58 @@
         @O.print @H.p("No such group: #{name}")
         return true
       end
-      nick = @sc.nickname(@params['user'])
-      from = sprintf("%s <%s>", nick, @params['user'])
-      body = @params['body'].gsub("\r", "").untaint
+      nick = @sc.nickname(user)
+      from = sprintf("%s <%s>", nick, user)
+      subj = @params['subject'][0]['value'] || "Message from "+@myname
+      body = @params['body'][0]['value'].gsub("\r", "").untaint
+      # Extract attachment file
+      if @params['attachment'].is_a?(Array)
+        body += (extract_report = extract_attachment(@params['attachment']))[0]
+      end
     end
-
+    # Variables here
+    #  name:		Destination group/user name
+    #  user:		Member account or nil(not member)
+    
     # Set values for header rewriting
+    # We have to setup these variables:
+    #  to:		Header To:
+    #  subj:		Header Subject:
+    #  from:		Header From:
+    #  rcpts:		Array of recipients addresses
+    #  header:		Hash of additional header values
+    #  body:		String of mail body
+    #  spooling:	Flag if spool ML files nor not
+    #  mldir:		spooling directory name
     if groupmode                # Run as ML
+      # To: should be ML address
+      # Return-path: should be verp of PREFIX-RCPT@DOMAIN
       bracket = @sc.getgroupattr(name, 'subjtag') || @opt['mailbracket']
       xmlname = @sc.getgroupattr(name, 'xmlname') || name
-      mldir = "ml/"+name
+      mldir = @mlbasedir+"/"+name
       to = @sc.getgroupattr(name, 'mladdress') || defaultmladdress(name)
       if @sc.getgroupattr(name, 'fromhack')
         fromhack = to
       end
       spooling = @opt['mlspooling']
+      returnpath = to.sub("@", @mailadmsuffix+"-@")
     else                        # Run as p2p mail
-      bracket = "NONE"          # Throught Subject
-      user = @params['user'] || ENV['SENDER']
-      if @sc.ismembersemail(user)
-        sender = defaultmladdress(quoted(user))
+      # To: should be Destination user name
+      # Return-path: should be PREFIX-USER@DOMAIN or $SENDER(not member)
+      bracket = "NONE"          # Through Subject
+      if user
+        returnpath = defaultmladdress(quoted(user))
       else
-        sender = user
+        returnpath = ENV['SENDER']
       end
       ###fromhack = sprintf("%s <%s>", nick, sender)
-      fromhack = sender
+      fromhack = returnpath
       
       xmlname = name
       to = name
       spooling = mldir = nil
     end
-    returnpath = to.sub("@", @mailadmsuffix+"-@")
     adminaddr  = to.sub("@", @mailadmsuffix+"@")
-    subj = @params['subject'] || "Message from "+@myname
     if bracket == "NONE"
       sjtag = ""
       tagre = nil
@@ -2852,36 +2945,39 @@
         gsub("%i", Regexp.quote(name)).
         gsub(/%(\d*)c/, '\d+')
       tagre = Regexp.new(tagpt)
-      subj = sjtag.strip+" "+subj.gsub(Regexp.new(tagpt), "")
+      if !viamail
+        subj = sjtag.strip+" "+subj.gsub(Regexp.new(tagpt), "")
+      end
     end
     if viamail then
       body = tagify_subj(STDIN.readlines, sjtag, tagre, fromhack).join
     elsif fromhack              # via http
-      from = rewritefrom(@params['user'], nick, groupmode ? to : sender)
+      from = rewritefrom(user, nick, groupmode ? to : returnpath)
     end
     header = {
       "X-ML-Driver" => ($hgid || @myname),
       "X-ML-Driver-URI" => $myurl,
+      "Return-path" => returnpath
     }
     if groupmode
       header["Reply-to"] = to
       header["X-ML-Name"] = xmlname
       header["X-ML-URI"] = sprintf("%s?-groupman+%s", @opt['url'], name)
-      header["Return-path"] = returnpath
     end
     Dir.chdir @mydir
-    if groupmode
-      rcpts = if grepgroup(name)
+    if groupmode                # (#includeself)
+      rcpts = if viamail
                 @sc.members(name)
               else
-                [name]
-              end.collect {|u| mailaddress(u, name).split(/,\s*|\s+/)}.flatten
+                @sc.members(name) + [user]
+              end.collect {|u|
+        mailaddress(u, name).split(/,\s*|\s+/)}.flatten.uniq
     else
       rcpts = @sc.mailaddress(name).split(/,\s*|\s+/).flatten
       rcpts += @sc.mailaddress(user).split(/,\s*|\s+/).flatten # +sender
     end
-    ENV["QMAILINJECT"] = "r"    # for ML mode, use verp
-    #
+    # ENV["QMAILINJECT"] = "r"    # for ML mode, use verp
+    # For vodafone, QMAILINJECT=r doesn't work correctly
     # On mail mode, check if sender can send message to list.
     if viamail && @sc.getgroupattr(name, 'limitsender')
       s = ENV['SENDER']
@@ -2907,6 +3003,7 @@
       @O.print @H.elementln("h1"){msg('sendall_done')}
       @O.print @H.p(sprintf(msg('sendall_head'),
                             nickname(name))+" "+msg('done'))
+      @O.print @H.elementln("pre"){extract_report[1]}
       link2home()
       @O.print footer()
       return true
@@ -2930,8 +3027,12 @@
     @O.print @H.elementln("h2") {
       sprintf(msg('sendall_head'), nickname(name))
     }
-    list = groupmode ? @sc.members(name) : [name, user]
-    @O.print @H.p(sprintf("%s: %s", msg('member'),
+    if groupmode                # (#includeself)
+      list = @sc.members(name)	# +user
+    else
+      list = [name, user]
+    end
+    @O.print @H.p(sprintf("%s: %s", msg('rcptto'),
                           list.collect {|u|
                             @H.element("abbr", "title"=>u){
                               @sc.nickname(u)
@@ -2939,15 +3040,31 @@
                           }.join(",\n")))
     @O.print @H.p(sprintf("(total %d)", list.length))+"\n"
     @O.print \
-    @H.elementln("form", {'action' => @myname+'?-list', 'method'=>"POST"}) {
+    @H.elementln("form", {
+                   'action' => @myname+'?-list', 'method'=>"POST",
+                   'enctype' => "multipart/form-data"}) {
       @H.elementln("table"){
         @H.elementln("tr"){
+          @H.element("td"){"To"} + \
+          @H.element("td"){
+            @H.element("code"){
+              groupmode ? defaultmladdress(quoted(name)) : @sc.nickname(name)
+            }
+          }
+        } + \
+        @H.elementln("tr"){
           @H.element("td"){"Subject"} + \
           @H.element("td"){
             @H.text("subject", "", 40, 128)
           }
         } + \
         @H.elementln("tr"){
+          @H.element("td"){"Attachment"} + \
+          @H.element("td"){
+            @H.element("input", "name"=>"attachment", "type"=>"file", 'multiple'=>true){}
+          }
+        } + \
+        @H.elementln("tr"){
           @H.element("td"){
             msg('body')
           } + \
@@ -4099,15 +4216,45 @@
       query = ARGV.join("&")
     end
 
-    for unit in query.split(/\&/)
-      if /^([a-z][-_0-9@%a-z.]*)=(.*)/i =~ unit
-	key, value = $1, $2
-	#value.gsub!(/%(..)/){[$1.hex].pack("c")} # これでURLデコードが一発
-	decode!(value)
-	decode!(key)
-	value = Kconv::toeuc(value) # EUCに変換
-	printf "[%s] = %s\n", key, value if $DEBUG
-	argument[key] = value
+    if ENV['CONTENT_TYPE'] &&
+        %r,multipart/form-data.*boundary=(.*),i =~ ENV['CONTENT_TYPE']
+      boundary = $1
+      query.split("\r\n--"+boundary).each {|unit|
+        if /Content-Disposition.*\bname=([\'\"])?(\S*)\1/i =~ unit
+          argument.has_key?(key=$2) or argument[key]=[]
+          newvalue = Hash.new
+          if /^Content.*filename=([\'\"])?(\S*)\1/i =~ unit
+            newvalue['filename'] = $2
+          end
+          newvalue['value'] = unit.sub(/.*\r\n\r\n/m, "")
+          if /^Content-type:\s*(\S*)/i =~ unit
+            newvalue['content-type'] = $1
+          else
+            newvalue['value'].gsub!("\r\n", "\n")
+          end
+          argument[key] << newvalue
+        end
+      }
+      if $DEBUG
+        open("/tmp/body", "w"){|x|
+          x.write query.split("\r\n--"+boundary+"\r\n")[-1]
+          argument.each{|k,v|
+            x.printf(" %s => %s\n", k.inspect, v.inspect)}
+          x.printf("boundary=\n%s\n",boundary)
+          x.printf("ENV=%s\n",ENV.inspect)
+          x.write query}
+      end
+    else
+      for unit in query.split(/\&/)
+        if /^([a-z][-_0-9@%a-z.]*)=(.*)/i =~ unit
+          key, value = $1, $2
+          #value.gsub!(/%(..)/){[$1.hex].pack("c")} # これでURLデコードが一発
+          decode!(value)
+          decode!(key)
+          value = Kconv::toeuc(value) # EUCに変換
+          printf "[%s] = %s\n", key, value if $DEBUG
+          argument[key] = value
+        end
       end
     end
     argument

yatex.org