func (m *mchCGI) postComment(threadKey, name, mail, body, passwd, tag string) error {
	stamp := time.Now().Unix()
	recbody := make(map[string]string)
	recbody["body"] = html.EscapeString(body)
	recbody["name"] = html.EscapeString(name)
	recbody["mail"] = html.EscapeString(mail)
	c := thread.NewCache(threadKey)
	rec := record.New(c.Datfile, "", 0)
	rec.Build(stamp, recbody, passwd)
	if rec.IsSpam() {
		return errSpamM
	}
	rec.Sync()
	if tag != "" {
		user.Set(c.Datfile, []string{tag})
	}
	go updateque.UpdateNodes(rec, nil)
	return nil
}
postComment creates a record from args and adds it to thread.Cache.
also adds tag if not tag!=""
package mch
import (
	"bytes"
	"errors"
	"fmt"
	"html"
	"log"
	"net/http"
	"regexp"
	"strconv"
	"strings"
	"time"
	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
	"github.com/shingetsu-gou/shingetsu-gou/cfg"
	"github.com/shingetsu-gou/shingetsu-gou/cgi"
	"github.com/shingetsu-gou/shingetsu-gou/mch"
	"github.com/shingetsu-gou/shingetsu-gou/mch/keylib"
	"github.com/shingetsu-gou/shingetsu-gou/record"
	"github.com/shingetsu-gou/shingetsu-gou/tag/user"
	"github.com/shingetsu-gou/shingetsu-gou/thread"
	"github.com/shingetsu-gou/shingetsu-gou/thread/download"
	"github.com/shingetsu-gou/shingetsu-gou/updateque"
	"github.com/shingetsu-gou/shingetsu-gou/util"
)
func Setup(s *cgi.LoggingServeMux) {
	log.Println("start 2ch interface")
	rtr := mux.NewRouter()
	cgi.RegistToRouter(rtr, "/2ch/", boardApp)
	cgi.RegistToRouter(rtr, "/2ch/dat/{datkey:[^\\.]+}.dat", threadApp)
	cgi.RegistToRouter(rtr, "/2ch/{board:[^/]+}/subject.txt", subjectApp)
	cgi.RegistToRouter(rtr, "/2ch/subject.txt", subjectApp)
	cgi.RegistToRouter(rtr, "/2ch/{board:[^/]+}/head.txt", headApp)
	cgi.RegistToRouter(rtr, "/2ch/head.txt", headApp)
	s.Handle("/2ch/", handlers.CompressHandler(rtr))
	s.RegistCompressHandler("/test/bbs.cgi", postCommentApp)
}
func boardApp(w http.ResponseWriter, r *http.Request) {
	a, err := new(w, r)
	if err != nil {
		log.Println(err)
		return
	}
	a.boardApp()
}
func threadApp(w http.ResponseWriter, r *http.Request) {
	a, err := new(w, r)
	if err != nil {
		log.Println(err)
		return
	}
	m := mux.Vars(r)
	board := m["board"]
	if board == "" {
		board = "2ch"
	}
	a.threadApp(board, m["datkey"])
}
func subjectApp(w http.ResponseWriter, r *http.Request) {
	a, err := new(w, r)
	if err != nil {
		log.Println(err)
		return
	}
	m := mux.Vars(r)
	a.subjectApp(m["board"])
}
func postCommentApp(w http.ResponseWriter, r *http.Request) {
	a, err := new(w, r)
	if err != nil {
		log.Println(err)
		return
	}
	a.postCommentApp()
}
func headApp(w http.ResponseWriter, r *http.Request) {
	a, err := new(w, r)
	if err != nil {
		log.Println(err)
		return
	}
	a.headApp()
}
type mchCGI struct {
	*cgi.CGI
}
func new(w http.ResponseWriter, r *http.Request) (*mchCGI, error) {
	c, err := cgi.NewCGI(w, r)
	if err != nil {
		w.WriteHeader(403)
		fmt.Fprintf(w, "403 Forbidden")
		return nil, err
	}
	a := mchCGI{
		CGI: c,
	}
	if !c.CheckVisitor() {
		http.Error(w, "403 Forbidden", http.StatusForbidden)
		return nil, errors.New("403 forbidden")
	}
	return &a, nil
}
func (m *mchCGI) serveContent(name string, t time.Time, str string) {
	br := bytes.NewReader([]byte(util.ToSJIS(str)))
	http.ServeContent(m.WR, m.Req, name, t, br)
}
func (m *mchCGI) boardApp() {
	l := m.Req.FormValue("Accept-Language")
	if l == "" {
		l = "ja"
	}
	msg := cgi.SearchMessage(l, cfg.FileDir)
	m.WR.Header().Set("Content-Type", "text/html; charset=Shift_JIS")
	board := util.Escape(util.GetBoard(m.Path()))
	var text string
	if board != "" {
		text = fmt.Sprintf("%s - %s - %s", msg["logo"], msg["description"], board)
	} else {
		text = fmt.Sprintf("%s - %s", msg["logo"], msg["description"])
	}
	htmlStr := fmt.Sprintf(
		`<!DOCTYPE html>
        <html><head>
        <meta http-equiv="content-type" content="text/html; charset=Shift_JIS">
        <title>%s</title>
        <meta name="description" content="%s">
        </head><body>
        <h1>%s</h1>
        </body></html>`,
		text, text, text)
	m.serveContent("a.html", time.Time{}, htmlStr)
}
func (m *mchCGI) threadApp(board, datkey string) {
	m.WR.Header().Set("Content-Type", "text/plain; charset=Shift_JIS")
	n, err := strconv.ParseInt(datkey, 10, 64)
	if err != nil {
		log.Println(err)
		return
	}
	key := keylib.GetFilekey(n)
	if err != nil {
		m.WR.WriteHeader(404)
		fmt.Fprintf(m.WR, "404 Not Found")
		return
	}
	data := thread.NewCache(key)
	if !data.Exists() {
		m.WR.WriteHeader(404)
		fmt.Fprintf(m.WR, "404 Not Found")
		return
	}
	if m.CheckGetCache() {
		download.GetCache(true, data)
	}
	thread := keylib.MakeDat(data, board, m.Req.Host)
	str := strings.Join(thread, "\n") + "\n"
	m.serveContent("a.txt", time.Unix(data.Stamp(), 0), str)
}
func (m *mchCGI) makeSubjectCachelist(board string) []*thread.Cache {
	result := thread.MakeRecentCachelist()
	if board == "" {
		return result
	}
	var result2 []*thread.Cache
	for _, c := range result {
		if user.Has(c.Datfile, board) {
			result2 = append(result2, c)
		}
	}
	return result2
}
func (m *mchCGI) subjectApp(board string) {
	var boardEncoded, boardName string
	if board != "" {
		boardEncoded = util.StrDecode(board)
	}
	if boardEncoded != "" {
		boardName = util.FileDecode("dummy_" + boardEncoded)
	}
	subject, lastStamp := m.makeSubject(boardName)
	m.WR.Header().Set("Content-Type", "text/plain; charset=Shift_JIS")
	m.serveContent("a.txt", time.Unix(lastStamp, 0), strings.Join(subject, "\n")+"\n")
}
func (m *mchCGI) makeSubject(board string) ([]string, int64) {
	loadFromNet := m.CheckGetCache()
	var subjects []string
	cl := m.makeSubjectCachelist(board)
	var lastStamp int64
	for _, c := range cl {
		if !loadFromNet && c.Len(record.Alive) == 0 {
			continue
		}
		if lastStamp < c.Stamp() {
			lastStamp = c.Stamp()
		}
		key, err := keylib.GetDatkey(c.Datfile)
		if err != nil {
			log.Println(err)
			continue
		}
		titleStr := util.FileDecode(c.Datfile)
		if titleStr == "" {
			continue
		}
		titleStr = strings.Trim(titleStr, "\r\n")
		subjects = append(subjects, fmt.Sprintf("%d.dat<>%s (%d)",
			key, titleStr, c.Len(record.Alive)))
	}
	return subjects, lastStamp
}
func (m *mchCGI) headApp() {
	m.WR.Header().Set("Content-Type", "text/plain; charset=Shift_JIS")
	var body string
	err := util.EachLine(cfg.Motd(), func(line string, i int) error {
		line = strings.TrimSpace(line)
		body += line + "<br>\n"
		return nil
	})
	if err != nil {
		log.Println(err)
	}
	m.serveContent("a.txt", time.Time{}, body)
}
var (
	errSpamM = errors.New("this is spam")
)