Ejemplo n.º 1
0
//printAttach renders the content of attach file and makes thumnail if needed and possible.
func (t *threadCGI) printAttach(datfile, id string, stamp int64, thumbnailSize, suffix string) {
	ca := thread.NewCache(datfile)
	switch {
	case ca.HasRecord():
	case t.CheckGetCache():
		download.GetCache(true, ca)
	default:
		t.Print404(ca, "")
		return
	}
	rec := record.New(ca.Datfile, id, stamp)
	if !rec.Exists() {
		t.Print404(ca, "")
		return
	}
	if err := rec.Load(); err != nil {
		t.Print404(ca, "")
		return
	}
	if rec.GetBodyValue("suffix", "") != suffix {
		t.Print404(ca, "")
		return
	}
	t.renderAttach(rec, suffix, stamp, thumbnailSize)
}
Ejemplo n.º 2
0
//doRecent renders records whose timestamp is in range of one specified in url.
func doRecent(w http.ResponseWriter, r *http.Request) {
	s, err := new(w, r)
	if err != nil {
		log.Println(err)
		return
	}
	reg := regexp.MustCompile("^recent/?([-0-9A-Za-z/]*)$")
	m := reg.FindStringSubmatch(s.Path())
	if m == nil {
		log.Println("illegal url")
		return
	}
	stamp := m[1]
	last := time.Now().Unix() + cfg.RecentRange
	begin, end, _ := s.parseStamp(stamp, last)
	for _, i := range recentlist.GetRecords() {
		if begin > i.Stamp || i.Stamp > end {
			continue
		}
		ca := thread.NewCache(i.Datfile)
		cont := fmt.Sprintf("%d<>%s<>%s", i.Stamp, i.ID, i.Datfile)
		if user.Len(ca.Datfile) > 0 {
			cont += "<>tag:" + user.String(ca.Datfile)
		}
		_, err := fmt.Fprintf(w, "%s\n", cont)
		if err != nil {
			log.Println(err)
		}
	}
}
Ejemplo n.º 3
0
//checkInfo checks posted info and returs thread name.
//if ok.
func (m *mchCGI) checkInfo(info map[string]string) string {
	var key string
	if info["subject"] != "" {
		key = util.FileEncode("thread", info["subject"])
	} else {
		n, err := strconv.ParseInt(info["key"], 10, 64)
		if err != nil {
			m.errorResp(err.Error(), info)
			return ""
		}
		key = keylib.GetFilekey(n)
	}

	switch {
	case info["body"] == "":
		m.errorResp("本文がありません.", info)
		return ""
	case thread.NewCache(key).Exists(), m.HasAuth():
	case info["subject"] != "":
		m.errorResp("掲示版を作る権限がありません", info)
		return ""
	default:
		m.errorResp("掲示版がありません", info)
		return ""
	}

	if info["subject"] == "" && key == "" {
		m.errorResp("フォームが変です.", info)
		return ""
	}
	return key
}
Ejemplo n.º 4
0
//threadApp load thread.Cache specified in the url and returns dat file
//listing records. if thread.Cache len=0 or for each refering the thread.Cache 4 times
//reloads thread.Cache fron network.
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)
}
Ejemplo n.º 5
0
//UpdateNodes do doUpdateNode for each records using related nodes.
//if success to doUpdateNode, add node to updatelist and recentlist and
//removes the record from queue.
func UpdateNodes(rec *record.Record, n *node.Node) {
	if !doUpdateNode(rec, n) {
		return
	}
	recentlist.Append(rec.Head)
	if !cfg.HeavyMoon {
		return
	}
	if ca := thread.NewCache(rec.Datfile); !ca.Exists() {
		ca.Subscribe()
	}
}
Ejemplo n.º 6
0
//postCommentApp checks posted data and replaces >> links to html links,
//and  saves it as record.
func (m *mchCGI) postCommentApp() {
	if m.Req.Method != http.MethodPost {
		m.WR.Header().Set("Content-Type", "text/plain")
		m.WR.WriteHeader(404)
		fmt.Fprintf(m.WR, "404 Not Found")
		return
	}
	info := m.getCommentData()
	info["host"] = m.Req.Host
	key := m.checkInfo(info)
	if key == "" {
		return
	}

	referer := m.getCP932("Referer")
	reg := regexp.MustCompile("/2ch_([^/]+)/")
	var tag string
	if ma := reg.FindStringSubmatch(referer); ma != nil && m.HasAuth() {
		tag = util.FileDecode("dummy_" + ma[1])
	}
	table := mch.NewResTable(thread.NewCache(key))
	reg = regexp.MustCompile(">>([1-9][0-9]*)")
	body := reg.ReplaceAllStringFunc(info["body"], func(str string) string {
		noStr := reg.FindStringSubmatch(str)[1]
		no, err := strconv.Atoi(noStr)
		if err != nil {
			log.Fatal(err)
		}
		return ">>" + table.Num2id[no]
	})

	name := info["name"]
	var passwd string
	if strings.ContainsRune(name, '#') {
		ary := strings.Split(name, "#")
		name = ary[0]
		passwd = ary[1]
	}
	if passwd != "" && !m.IsAdmin() {
		m.errorResp("自ノード以外で署名機能は使えません", info)
	}
	err := m.postComment(key, name, info["mail"], body, passwd, tag)
	if err == errSpamM {
		m.errorResp("スパムとみなされました", info)
	}
	m.WR.Header().Set("Content-Type", "text/html; charset=Shift_JIS")
	fmt.Fprintln(m.WR,
		util.ToSJIS(`<html lang="ja"><head><meta http-equiv="Content-Type" content="text/html"><title>書きこみました。</title></head><body>書きこみが終わりました。<br><br></body></html>`))
}
Ejemplo n.º 7
0
//GetDatkey returns stamp from filekey.
//if not found, tries to read from cache.
func GetDatkey(filekey string) (int64, error) {
	var v int64
	err := db.DB.Update(func(tx *bolt.Tx) error {
		var errr error
		v, errr = getTime(tx, filekey)
		if errr == nil {
			return nil
		}
		c := thread.NewCache(filekey)
		setFromCache(tx, c)
		v, errr = getTime(tx, filekey)
		return errr
	})
	return v, err
}
Ejemplo n.º 8
0
//Load loads from the file, adds stamps/datfile pairs from cachelist and recentlist.
//and saves to file.
func Load() {
	allCaches := thread.AllCaches()
	allRecs := recentlist.GetRecords()
	err := db.DB.Update(func(tx *bolt.Tx) error {
		for _, c := range allCaches {
			setFromCache(tx, c)
		}
		for _, rec := range allRecs {
			c := thread.NewCache(rec.Datfile)
			setFromCache(tx, c)
		}
		return nil
	})
	if err != nil {
		log.Println(err)
	}
}
Ejemplo n.º 9
0
//printThreadAjax renders records in cache id for ajax.
func (t *threadCGI) printThreadAjax(id string) {
	th := strings.Split(t.Path(), "/")[0]
	filePath := util.FileEncode("thread", th)
	ca := thread.NewCache(filePath)
	if !ca.HasRecord() {
		log.Println(filePath, "not found")
		return
	}
	fmt.Fprintln(t.WR, "<dl>")
	recs := ca.LoadRecords(record.Alive)
	for _, rec := range recs {
		if id == "" || rec.ID[:8] == id && rec.Load() == nil {
			t.printRecord(ca, rec)
		}
	}
	fmt.Fprintln(t.WR, "</dl>")
}
Ejemplo n.º 10
0
//doUpdateNode broadcast and get data for each new records.
//if can get data (even if spam) return true, if fails to get, return false.
//if no fail, broadcast updates to node in cache and added n to nodelist and searchlist.
func doUpdateNode(rec *record.Record, n *node.Node) bool {
	mutex.Lock()
	deleteOldUpdated()
	if _, exist := updated[rec.Hash()]; exist {
		log.Println("already broadcasted", rec.ID)
		mutex.Unlock()
		return true
	}
	updated[rec.Hash()] = time.Now()
	mutex.Unlock()

	ca := thread.NewCache(rec.Datfile)
	var err error
	if !ca.Exists() || n == nil {
		log.Println("no cache or updates by myself, broadcast updates.")
		UpdatedRecord.register(rec.Head)
		manager.TellUpdate(ca.Datfile, rec.Stamp, rec.ID, n)
		if UpdatedRecord.wait() || n != nil {
			log.Println(rec.ID, "was gotten or don't have the record")
		} else {
			log.Println(rec.ID, "was NOT gotten, will call updates later")
			go func() {
				time.Sleep(10 * time.Minute)
				UpdateNodes(rec, n)
			}()
		}
		return true
	}
	log.Println("cache exists. get record from node n.")
	err = rec.GetData(n)
	switch err {
	case cfg.ErrGet:
		log.Println("could not get")
		return false
	case cfg.ErrSpam:
		log.Println("marked spam")
		return true
	default:
		log.Println("telling update")
		manager.TellUpdate(ca.Datfile, rec.Stamp, rec.ID, nil)
		manager.Join(n)
		return true
	}
}
Ejemplo n.º 11
0
//MakeBracketLink changes str in brackets to the html links format.
func MakeBracketLink(body, datHost, board string, table *mch.ResTable) string {
	regs := []*regexp.Regexp{
		regexp.MustCompile("^(?P<title>[^/]+)$"),
		regexp.MustCompile("^/(?P<type>[a-z]+)/(?P<title>[^/]+)$"),
		regexp.MustCompile("^(?P<title>[^/]+)/(?P<id>[0-9a-f]{8})$"),
		regexp.MustCompile("^/(?P<type>[a-z]+)/(?P<title>[^/]+)/(?P<id>[0-9a-f]{8})$"),
	}
	reg := regexp.MustCompile(`\[\[([^<>]+?)\]\]`)
	return reg.ReplaceAllStringFunc(body, func(str string) string {
		link := reg.FindStringSubmatch(str)[1]
		result := make(map[string]string)
		for _, r := range regs {
			if match := r.FindStringSubmatch(link); match != nil {
				for i, name := range r.SubexpNames() {
					result[name] = match[i]
				}
				break
			}
		}
		if result["title"] == "" {
			return result["body"]
		}
		if result["type"] == "" {
			result["type"] = "thread"
		}
		file := util.FileEncode(result["type"], result["title"])
		datkey, err := GetDatkey(file)
		if err != nil {
			log.Println(err)
			return body
		}
		if result["id"] == "" {
			url := fmt.Sprintf("http://%s/test/read.cgi/%s/%d/", datHost, board, datkey)
			return fmt.Sprintf("[[%s(%s)]]", result["title"], url)
		}
		ca := thread.NewCache(file)
		table = mch.NewResTable(ca)
		no := table.ID2num[result["id"]]
		url := fmt.Sprintf("http://%s/test/read.cgi/%s/%d/%d", datHost, board, datkey, no)
		return fmt.Sprintf("[[%s(&gt;&gt;%d %s)]]", result["title"], no, url)
	})
}
Ejemplo n.º 12
0
//doHave checks existance of cache whose name is specified in url.
func doHave(w http.ResponseWriter, r *http.Request) {
	s, err := new(w, r)
	if err != nil {
		log.Println(err)
		return
	}
	reg := regexp.MustCompile("^have/([0-9A-Za-z_]+)$")
	m := reg.FindStringSubmatch(s.Path())
	if m == nil {
		fmt.Fprintln(w, "NO")
		log.Println("illegal url")
		return
	}
	ca := thread.NewCache(m[1])
	if ca.HasRecord() {
		fmt.Fprintln(w, "YES")
	} else {
		fmt.Fprintln(w, "NO")
	}
}
Ejemplo n.º 13
0
//postComment creates a record from args and adds it to thread.Cache.
//also adds tag if not tag!=""
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
}
Ejemplo n.º 14
0
//doPost parses multipart form ,makes record of it and adds to cache.
//if form dopost=yes broadcasts it.
func (t *threadCGI) doPost() string {
	attached, attachedErr := t.parseAttached()
	if attachedErr != nil {
		log.Println(attachedErr)
	}
	suffix := t.guessSuffix(attached)
	ca := thread.NewCache(t.Req.FormValue("file"))
	rec, err := t.makeRecord(attached, suffix, ca)
	if err != nil {
		return ""
	}
	proxyClient := t.Req.Header.Get("X_FORWARDED_FOR")
	log.Printf("post %s/%d_%s from %s/%s\n", ca.Datfile, ca.Stamp(), rec.ID, t.Req.RemoteAddr, proxyClient)

	if len(rec.Recstr()) > cfg.RecordLimit<<10 {
		t.Header(t.M["big_file"], "", nil, true)
		t.Footer(nil)
		return ""
	}
	if rec.IsSpam() {
		t.Header(t.M["spam"], "", nil, true)
		t.Footer(nil)
		return ""
	}

	if ca.Exists() {
		rec.Sync()
	} else {
		t.Print404(nil, "")
		return ""
	}

	if t.Req.FormValue("dopost") != "" {
		log.Println(rec.Datfile, rec.ID, "is queued")
		go updateque.UpdateNodes(rec, nil)
	}

	return rec.ID[:8]

}
Ejemplo n.º 15
0
//printThread renders whole thread list page.
func (t *threadCGI) printThread(path, id string, nPage int) {
	if id != "" && t.Req.FormValue("ajax") != "" {
		t.printThreadAjax(id)
		return
	}
	filePath := util.FileEncode("thread", path)
	ca := thread.NewCache(filePath)
	rss := cfg.GatewayURL + "/rss"
	if t.printThreadHead(path, id, nPage, ca, rss) != nil {
		return
	}
	tags := strings.Fields(strings.TrimSpace(t.Req.FormValue("tag")))
	if t.IsAdmin() && len(tags) > 0 {
		user.Add(ca.Datfile, tags)
	}
	t.printTag(ca)
	t.printThreadTop(path, id, nPage, ca)
	t.printPageNavi(path, nPage, ca, id)
	t.printThreadBody(id, nPage, ca)

	escapedPath := html.EscapeString(path)
	escapedPath = strings.Replace(escapedPath, "  ", "&nbsp;&nbsp;", -1)
	ss := struct {
		Cache   *thread.Cache
		Message cgi.Message
	}{
		ca,
		t.M,
	}
	cgi.RenderTemplate("thread_bottom", ss, t.WR)

	if ca.HasRecord() {
		t.printPageNavi(path, nPage, ca, id)
		fmt.Fprintf(t.WR, "</p>")
	}
	t.printPostForm(ca)
	t.printTag(ca)
	t.RemoveFileForm(ca, escapedPath)
	t.Footer(t.MakeMenubar("bottom", rss))
}
Ejemplo n.º 16
0
//doGetHead renders records contents(get) or id+timestamp(head) who has id and
// whose stamp is in range of one specified by url.
func doGetHead(w http.ResponseWriter, r *http.Request) {
	s, err := new(w, r)
	if err != nil {
		log.Println(err)
		return
	}
	reg := regexp.MustCompile("^(get|head|removed)/([0-9A-Za-z_]+)/?([-0-9A-Za-z/]*)$")
	m := reg.FindStringSubmatch(s.Path())
	if m == nil {
		log.Println("illegal url", s.Path())
		return
	}
	method, datfile, stamp := m[1], m[2], m[3]
	ca := thread.NewCache(datfile)
	begin, end, id := s.parseStamp(stamp, math.MaxInt32)
	var recs record.Map
	if method == "removed" {
		recs = ca.LoadRecords(record.Removed)
	} else {
		recs = ca.LoadRecords(record.Alive)
	}
	for _, r := range recs {
		if r.InRange(begin, end, id) {
			if method == "get" {
				if err := r.Load(); err != nil {
					log.Println(err)
					continue
				}
				fmt.Fprintln(s.WR, r.Recstr())
				continue
			}
			fmt.Fprintln(s.WR, strings.Replace(r.Idstr(), "_", "<>", -1))
		}
	}
	if method == "get" {
		updateque.UpdatedRecord.Inform(datfile, id, begin, end)
	}
}