//printPageNavi renders page_navi.txt, part for paging.
func (t *threadCGI) printPageNavi(path string, page int, ca *thread.Cache, id string) {
	len := ca.Len()
	first := len / cfg.ThreadPageSize
	if len%cfg.ThreadPageSize == 0 {
		first++
	}
	pages := make([]int, first+1)
	for i := 0; i <= first; i++ {
		pages[i] = i
	}
	s := struct {
		Page           int
		CacheLen       int
		Path           string
		ID             string
		First          int
		ThreadCGI      string
		Message        cgi.Message
		ThreadPageSize int
		Pages          []int
	}{
		page,
		len,
		path,
		id,
		first,
		cfg.ThreadURL,
		t.M,
		cfg.ThreadPageSize,
		pages,
	}
	cgi.RenderTemplate("page_navi", s, t.WR)
}
Beispiel #2
0
//MakeDat makes dat lines of 2ch from cache.
func MakeDat(ca *thread.Cache, board, host string) []string {
	recs := ca.LoadRecords(record.Alive)
	dat := make([]string, len(recs))
	table := mch.NewResTable(ca)

	i := 0
	for _, datfile := range recs.Keys() {
		rec := recs[datfile]
		err := rec.Load()
		if err != nil {
			log.Println(err)
			continue
		}
		name := rec.GetBodyValue("name", "")
		if name == "" {
			name = "名無しさん"
		}
		if rec.GetBodyValue("pubkey", "") != "" {
			name += "◆" + rec.GetBodyValue("pubkey", "")[:10]
		}
		comment := fmt.Sprintf("%s<>%s<>%s<>%s<>",
			name, rec.GetBodyValue("main", ""), util.Datestr2ch(rec.Stamp), MakeBody(rec, host, board, table))
		if i == 0 {
			comment += util.FileDecode(ca.Datfile)
		}
		dat[i] = comment
		i++
	}

	return dat
}
//printThreadBody renders body(records list) part of thread page with paging.
func (t *threadCGI) printThreadBody(id string, nPage int, ca *thread.Cache) {
	recs := ca.LoadRecords(record.Alive)
	ids := recs.Keys()
	fmt.Fprintln(t.WR, "</p>\n<dl id=\"records\">")
	from := len(ids) - cfg.ThreadPageSize*(nPage+1)
	to := len(ids) - cfg.ThreadPageSize*(nPage)
	if from < 0 {
		from = 0
	}
	if to < 0 {
		to = 0
	}
	var inrange []string
	switch {
	case id != "":
		inrange = ids
	case nPage > 0:
		inrange = ids[from:to]
	default:
		inrange = ids[from:]
	}

	for _, k := range inrange {
		rec := recs.Get(k, nil)
		if (id == "" || rec.ID[:8] == id) && rec.Load() == nil {
			t.printRecord(ca, rec)
		}
	}

	fmt.Fprintln(t.WR, "</dl>")
}
//appendRSS appends cache ca to rss with contents,url to records,stamp,attached file.
func (g *gatewayCGI) appendRSS(rsss *cgi.RSS, ca *thread.Cache) {
	now := time.Now().Unix()
	if ca.Stamp()+cfg.RSSRange < now {
		return
	}
	title := util.Escape(util.FileDecode(ca.Datfile))
	path := cfg.ThreadURL + "/" + util.StrEncode(title)
	recs := ca.LoadRecords(record.Alive)
	for _, r := range recs {
		if r.Stamp+cfg.RSSRange < now {
			continue
		}
		if err := r.Load(); err != nil {
			log.Println(err)
			continue
		}
		desc := cgi.RSSTextFormat(r.GetBodyValue("body", ""))
		content := g.rssHTMLFormat(r.GetBodyValue("body", ""), cfg.ThreadURL, title)
		if attach := r.GetBodyValue("attach", ""); attach != "" {
			suffix := r.GetBodyValue("suffix", "")
			if reg := regexp.MustCompile("^[0-9A-Za-z]+$"); !reg.MatchString(suffix) {
				suffix = cfg.SuffixTXT
			}
			content += fmt.Sprintf("\n    <p><a href=\"http://%s%s%s%s/%s/%d.%s\">%d.%s</a></p>",
				g.Host(), cfg.ThreadURL, "/", ca.Datfile, r.ID, r.Stamp, suffix, r.Stamp, suffix)
		}
		permpath := fmt.Sprintf("%s/%s", path[1:], r.ID[:8])
		rsss.Append(permpath, title, cgi.RSSTextFormat(r.GetBodyValue("name", "")), desc, content, user.GetStrings(ca.Datfile), r.Stamp, false)
	}
}
//getWithRange gets records with range using node n and adds to cache after checking them.
//if no records exist in cache, uses head
//return true if gotten records>0
func getWithRange(n *node.Node, c *thread.Cache, dm *Manager) bool {
	got := false
	for {
		from, to := dm.Get(n)
		if from <= 0 {
			return got
		}

		var okcount int
		ress, err := n.Talk(fmt.Sprintf("/get/%s/%d-%d", c.Datfile, from, to), nil)
		if err != nil {
			dm.Finished(n, false)
			return false
		}
		err = db.DB.Update(func(tx *bolt.Tx) error {
			for _, res := range ress {
				errf := c.CheckData(tx, res, -1, "", from, to)
				if errf == nil {
					okcount++
				}
			}
			return nil
		})
		if err != nil {
			log.Println(err)
		}
		dm.Finished(n, true)
		log.Println(c.Datfile, okcount, "records were saved from", n.Nodestr)
		got = okcount > 0
	}
}
//NewResTable creates ane returns a resTable instance.
func NewResTable(ca *thread.Cache) *ResTable {
	r := &ResTable{
		make(map[string]int),
		make([]string, ca.Len()+1),
	}
	recs := ca.LoadRecords(record.Alive)
	for i, k := range recs.Keys() {
		rec := recs.Get(k, nil)
		r.Num2id[i+1] = rec.ID[:8]
		r.ID2num[rec.ID[:8]] = i + 1
	}
	return r
}
Beispiel #7
0
//RemoveFileForm render remove_form_form page.
func (c *CGI) RemoveFileForm(ca *thread.Cache, title string) {
	s := struct {
		Cache     *thread.Cache
		CacheSize int64
		Title     string
		Defaults
	}{
		ca,
		ca.Size(),
		title,
		*c.Defaults(),
	}
	RenderTemplate("remove_file_form", s, c.WR)
}
//makeOneRow makes one row of CSV depending on c.
func (g *gatewayCGI) makeOneRow(c string, ca *thread.Cache, p, title string) string {
	switch c {
	case "file":
		return ca.Datfile
	case "stamp":
		return strconv.FormatInt(ca.Stamp(), 10)
	case "date":
		return time.Unix(ca.Stamp(), 0).String()
	case "path":
		return p
	case "uri":
		if g.Host() != "" && p != "" {
			return "http://" + g.Host() + p
		}
	case "type":
		return "thread"
	case "title":
		return title
	case "records":
		return strconv.Itoa(ca.Len())
	case "size":
		return strconv.FormatInt(ca.Size(), 10)
	case "tag":
		return user.String(ca.Datfile)
	case "sugtag":
		return suggest.String(ca.Datfile)
	}
	return ""
}
//NewManger sets recs as finished recs and returns DownloadManager obj.
func NewManger(ca *thread.Cache) *Manager {
	if d, exist := managers[ca.Datfile]; exist {
		log.Println(ca.Datfile, "is downloading")
		return d
	}
	recs := ca.LoadRecords(record.All)
	dm := &Manager{
		datfile: ca.Datfile,
		recs:    make(map[string]*targetRec),
	}
	for k := range recs {
		dm.recs[k] = &targetRec{
			finished: true,
		}
	}
	return dm
}
//printThreadHead renders head part of thread page with cookie.
func (t *threadCGI) printThreadHead(path, id string, page int, ca *thread.Cache, rss string) error {
	switch {
	case ca.HasRecord():
		if !t.IsBot() {
			download.GetCache(true, ca)
		} else {
			log.Println("bot detected, not get cache")
		}
	case t.CheckGetCache():
		ca.Subscribe()
		if t.Req.FormValue("search_new_file") == "" {
			download.GetCache(true, ca)
		}
	default:
		t.Print404(nil, id)
		return errors.New("no records")
	}
	var access string
	var newcookie []*http.Cookie
	if ca.HasRecord() && id == "" && page == 0 {
		cookie, err := t.Req.Cookie("access")
		if err == nil {
			access = cookie.Value
		} else {
			log.Println(err)
		}
		newcookie = t.setCookie(ca, access)
	}
	t.Header(path, rss, newcookie, false)
	return nil
}
Beispiel #11
0
//bg waits for at least one record in the cache.
func bg(c *thread.Cache, wg *sync.WaitGroup) {
	w := 2 * time.Second
	newest, err := recentlist.Newest(c.Datfile)
	var done chan struct{}
	go func() {
		wg.Wait()
		done <- struct{}{}
	}()
	if err == nil && (newest.Stamp == c.Stamp()) {
		return
	}
	for {
		select {
		case <-done:
			return
		case <-time.After(w):
			w += time.Second
			if c.HasRecord() || w >= 5*time.Second {
				return
			}
		}
	}
}
//printThreadTop renders toppart of thread page.
func (t *threadCGI) printThreadTop(path, id string, nPage int, ca *thread.Cache) {
	var lastrec *record.Record
	var resAnchor string
	recs := ca.LoadRecords(record.Alive)
	ids := recs.Keys()
	if ca.HasRecord() && nPage == 0 && id == "" && len(ids) > 0 {
		lastrec = recs[ids[len(ids)-1]]
		resAnchor = t.ResAnchor(lastrec.ID[:8], cfg.ThreadURL, t.Path(), false)
	}
	s := struct {
		Path      string
		Cache     *thread.Cache
		Lastrec   *record.Record
		ResAnchor template.HTML
		cgi.Defaults
	}{
		path,
		ca,
		lastrec,
		template.HTML(resAnchor),
		*t.Defaults(),
	}
	cgi.RenderTemplate("thread_top", s, t.WR)
}
Beispiel #13
0
//setFromCache adds cache.datfile/timestamp pair if not exists.
func setFromCache(tx *bolt.Tx, ca *thread.Cache) {
	_, err := getTime(tx, ca.Datfile)
	if err == nil {
		return
	}
	var firstStamp int64
	rec := ca.LoadRecords(record.Alive)
	switch {
	case !ca.HasRecord():
		firstStamp = ca.RecentStamp()
	case len(rec) > 0:
		firstStamp = rec[rec.Keys()[0]].Stamp
	default:
		firstStamp = time.Now().Add(-24 * time.Hour).Unix()
	}
	for {
		_, err = getThread(tx, firstStamp)
		if err != nil {
			break
		}
		firstStamp++
	}
	setEntry(tx, firstStamp, ca.Datfile)
}