Merge branch 'master' of ssh://github.com/majestrate/nntpchan
This commit is contained in:
		@@ -323,10 +323,10 @@ type Database interface {
 | 
				
			|||||||
	GetPostingStats(granularity, begin, end int64) (PostingStats, error)
 | 
						GetPostingStats(granularity, begin, end int64) (PostingStats, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// peform search query
 | 
						// peform search query
 | 
				
			||||||
	SearchQuery(prefix, group, text string, chnl chan PostModel) error
 | 
						SearchQuery(prefix, group, text string, chnl chan PostModel, limit int) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// find posts with similar hash
 | 
						// find posts with similar hash
 | 
				
			||||||
	SearchByHash(prefix, group, posthash string, chnl chan PostModel) error
 | 
						SearchByHash(prefix, group, posthash string, chnl chan PostModel, limit int) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// get full thread model
 | 
						// get full thread model
 | 
				
			||||||
	GetThreadModel(prefix, root_msgid string) (ThreadModel, error)
 | 
						GetThreadModel(prefix, root_msgid string) (ThreadModel, error)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1111,14 +1111,14 @@ func (self *httpFrontend) handle_api_find(wr http.ResponseWriter, r *http.Reques
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		donechnl <- 0
 | 
							donechnl <- 0
 | 
				
			||||||
	}(wr)
 | 
						}(wr)
 | 
				
			||||||
 | 
						limit := 50
 | 
				
			||||||
	if len(h) > 0 {
 | 
						if len(h) > 0 {
 | 
				
			||||||
		self.daemon.database.SearchByHash(self.prefix, g, h, chnl)
 | 
							go self.daemon.database.SearchByHash(self.prefix, g, h, chnl, limit)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		self.daemon.database.SearchQuery(self.prefix, g, s, chnl)
 | 
							go self.daemon.database.SearchQuery(self.prefix, g, s, chnl, limit)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	chnl <- nil
 | 
						chnl <- nil
 | 
				
			||||||
	<-donechnl
 | 
						<-donechnl
 | 
				
			||||||
	close(donechnl)
 | 
					 | 
				
			||||||
	io.WriteString(wr, " null ]")
 | 
						io.WriteString(wr, " null ]")
 | 
				
			||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -718,7 +718,7 @@ func (self *nntpConnection) handleLine(daemon *NNTPDaemon, code int, line string
 | 
				
			|||||||
				if len(reason) > 0 {
 | 
									if len(reason) > 0 {
 | 
				
			||||||
					// discard, we do not want
 | 
										// discard, we do not want
 | 
				
			||||||
					log.Println(self.name, "rejected", msgid, reason)
 | 
										log.Println(self.name, "rejected", msgid, reason)
 | 
				
			||||||
					conn.PrintfLine("439 %s %s", code, msgid, reason)
 | 
										conn.PrintfLine("439 %s %s", msgid, reason)
 | 
				
			||||||
					_, err = io.Copy(ioutil.Discard, msg.Body)
 | 
										_, err = io.Copy(ioutil.Discard, msg.Body)
 | 
				
			||||||
					if ban {
 | 
										if ban {
 | 
				
			||||||
						err = daemon.database.BanArticle(msgid, reason)
 | 
											err = daemon.database.BanArticle(msgid, reason)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -219,10 +219,10 @@ func (self *PostgresDatabase) prepareStatements() {
 | 
				
			|||||||
		GetMessageIDByCIDR:              "SELECT message_id FROM ArticlePosts WHERE addr IN ( SELECT encaddr FROM EncryptedAddrs WHERE addr_cidr <<= cidr($1) )",
 | 
							GetMessageIDByCIDR:              "SELECT message_id FROM ArticlePosts WHERE addr IN ( SELECT encaddr FROM EncryptedAddrs WHERE addr_cidr <<= cidr($1) )",
 | 
				
			||||||
		GetMessageIDByEncryptedIP:       "SELECT message_id FROM ArticlePosts WHERE addr = $1",
 | 
							GetMessageIDByEncryptedIP:       "SELECT message_id FROM ArticlePosts WHERE addr = $1",
 | 
				
			||||||
		GetPostsBefore:                  "SELECT message_id FROM ArticlePosts WHERE time_posted < $1",
 | 
							GetPostsBefore:                  "SELECT message_id FROM ArticlePosts WHERE time_posted < $1",
 | 
				
			||||||
		SearchQuery_1:                   "SELECT newsgroup, message_id, ref_id FROM ArticlePosts WHERE message LIKE $1 ORDER BY time_posted DESC",
 | 
							SearchQuery_1:                   "SELECT newsgroup, message_id, ref_id FROM ArticlePosts WHERE message LIKE $1 ORDER BY time_posted DESC LIMIT $2",
 | 
				
			||||||
		SearchQuery_2:                   "SELECT newsgroup, message_id, ref_id FROM ArticlePosts WHERE newsgroup = $1 AND message LIKE $2 ORDER BY time_posted DESC",
 | 
							SearchQuery_2:                   "SELECT newsgroup, message_id, ref_id FROM ArticlePosts WHERE newsgroup = $1 AND message LIKE $2 ORDER BY time_posted DESC LIMIT $3",
 | 
				
			||||||
		SearchByHash_1:                  "SELECT message_newsgroup, message_id, message_ref_id FROM Articles WHERE message_id_hash LIKE $1 ORDER BY time_obtained DESC",
 | 
							SearchByHash_1:                  "SELECT message_newsgroup, message_id, message_ref_id FROM Articles WHERE message_id_hash LIKE $1 ORDER BY time_obtained DESC LIMIT $2",
 | 
				
			||||||
		SearchByHash_2:                  "SELECT message_newsgroup, message_id, message_ref_id FROM Articles WHERE message_newsgroup = $2 AND message_id_hash LIKE $1 ORDER BY time_obtained DESC",
 | 
							SearchByHash_2:                  "SELECT message_newsgroup, message_id, message_ref_id FROM Articles WHERE message_newsgroup = $2 AND message_id_hash LIKE $1 ORDER BY time_obtained DESC LIMIT $3",
 | 
				
			||||||
		GetNNTPPostsInGroup:             "SELECT message_no, ArticlePosts.message_id, subject, time_posted, ref_id, name, path FROM ArticleNumbers INNER JOIN ArticlePosts ON ArticleNumbers.message_id = ArticlePosts.message_id WHERE ArticlePosts.newsgroup = $1 ORDER BY message_no",
 | 
							GetNNTPPostsInGroup:             "SELECT message_no, ArticlePosts.message_id, subject, time_posted, ref_id, name, path FROM ArticleNumbers INNER JOIN ArticlePosts ON ArticleNumbers.message_id = ArticlePosts.message_id WHERE ArticlePosts.newsgroup = $1 ORDER BY message_no",
 | 
				
			||||||
		GetCitesByPostHashLike:          "SELECT message_id, message_ref_id FROM Articles WHERE message_id_hash LIKE $1",
 | 
							GetCitesByPostHashLike:          "SELECT message_id, message_ref_id FROM Articles WHERE message_id_hash LIKE $1",
 | 
				
			||||||
		GetYearlyPostHistory:            "WITH times(endtime, begintime) AS ( SELECT CAST(EXTRACT(epoch from i) AS BIGINT) AS endtime, CAST(EXTRACT(epoch from i - interval '1 month') AS BIGINT) AS begintime FROM generate_series(now() - interval '10 year', now(), '1 month'::interval) i ) SELECT begintime, endtime, ( SELECT count(*) FROM ArticlePosts WHERE time_posted > begintime AND time_posted < endtime) FROM times",
 | 
							GetYearlyPostHistory:            "WITH times(endtime, begintime) AS ( SELECT CAST(EXTRACT(epoch from i) AS BIGINT) AS endtime, CAST(EXTRACT(epoch from i - interval '1 month') AS BIGINT) AS begintime FROM generate_series(now() - interval '10 year', now(), '1 month'::interval) i ) SELECT begintime, endtime, ( SELECT count(*) FROM ArticlePosts WHERE time_posted > begintime AND time_posted < endtime) FROM times",
 | 
				
			||||||
@@ -1970,14 +1970,14 @@ func (self *PostgresDatabase) GetPostingStats(gran, begin, end int64) (st Postin
 | 
				
			|||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (self *PostgresDatabase) SearchQuery(prefix, group string, text string, chnl chan PostModel) (err error) {
 | 
					func (self *PostgresDatabase) SearchQuery(prefix, group string, text string, chnl chan PostModel, limit int) (err error) {
 | 
				
			||||||
	if text != "" && strings.Count(text, "%") == 0 {
 | 
						if text != "" && strings.Count(text, "%") == 0 {
 | 
				
			||||||
		text = "%" + text + "%"
 | 
							text = "%" + text + "%"
 | 
				
			||||||
		var rows *sql.Rows
 | 
							var rows *sql.Rows
 | 
				
			||||||
		if group == "" {
 | 
							if group == "" {
 | 
				
			||||||
			rows, err = self.conn.Query(self.stmt[SearchQuery_1], text)
 | 
								rows, err = self.conn.Query(self.stmt[SearchQuery_1], text, limit)
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			rows, err = self.conn.Query(self.stmt[SearchQuery_2], group, text)
 | 
								rows, err = self.conn.Query(self.stmt[SearchQuery_2], group, text, limit)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if err == nil {
 | 
							if err == nil {
 | 
				
			||||||
			for rows.Next() {
 | 
								for rows.Next() {
 | 
				
			||||||
@@ -1991,15 +1991,15 @@ func (self *PostgresDatabase) SearchQuery(prefix, group string, text string, chn
 | 
				
			|||||||
	close(chnl)
 | 
						close(chnl)
 | 
				
			||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
func (self *PostgresDatabase) SearchByHash(prefix, group, text string, chnl chan PostModel) (err error) {
 | 
					func (self *PostgresDatabase) SearchByHash(prefix, group, text string, chnl chan PostModel, limit int) (err error) {
 | 
				
			||||||
	if text != "" && strings.Count(text, "%") == 0 {
 | 
						if text != "" && strings.Count(text, "%") == 0 {
 | 
				
			||||||
		text = "%" + text + "%"
 | 
							text = "%" + text + "%"
 | 
				
			||||||
		var rows *sql.Rows
 | 
							var rows *sql.Rows
 | 
				
			||||||
		if group == "" {
 | 
							if group == "" {
 | 
				
			||||||
			rows, err = self.conn.Query(self.stmt[SearchByHash_1], text)
 | 
								rows, err = self.conn.Query(self.stmt[SearchByHash_1], text, limit)
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			rows, err = self.conn.Query(self.stmt[SearchByHash_2], text, group)
 | 
								rows, err = self.conn.Query(self.stmt[SearchByHash_2], text, group, limit)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if err == nil {
 | 
							if err == nil {
 | 
				
			||||||
			for rows.Next() {
 | 
								for rows.Next() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,8 +26,17 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
var ErrOversizedMessage = errors.New("oversized message")
 | 
					var ErrOversizedMessage = errors.New("oversized message")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ~ 10 MB unbased64'd
 | 
					// (cathugger)
 | 
				
			||||||
const DefaultMaxMessageSize = 1024 * 1024 * 10
 | 
					// my test showed that 8MiB of attachments split in 5 parts
 | 
				
			||||||
 | 
					// plus some text produce something close to typhical big message
 | 
				
			||||||
 | 
					// resulted in 11483923 bytes.
 | 
				
			||||||
 | 
					// that's consistent with rough size calculation mentioned in
 | 
				
			||||||
 | 
					// <https://en.wikipedia.org/wiki/Base64#MIME>
 | 
				
			||||||
 | 
					// ((origlen * 1.37) + 814)
 | 
				
			||||||
 | 
					// which resulted in 11493206 bytes for 8MiB of data.
 | 
				
			||||||
 | 
					// previous default of 10MiB (10485760) was too low in practice.
 | 
				
			||||||
 | 
					// use 11MiB (11534336) to leave some space for longer than usual texts.
 | 
				
			||||||
 | 
					const DefaultMaxMessageSize = 11 * 1024 * 1024
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// HARD max message size
 | 
					// HARD max message size
 | 
				
			||||||
const MaxMessageSize = 1024 * 1024 * 1024
 | 
					const MaxMessageSize = 1024 * 1024 * 1024
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -477,28 +477,47 @@ func newNaclSignKeypair() (string, string) {
 | 
				
			|||||||
	return hex.EncodeToString(pk), hex.EncodeToString(sk)
 | 
						return hex.EncodeToString(pk), hex.EncodeToString(sk)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func makeTripcodeLen(pubkey string, length int) string {
 | 
				
			||||||
 | 
						var b strings.Builder
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data, err := hex.DecodeString(pubkey)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "[invalid]"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if length <= 0 || length > len(data) {
 | 
				
			||||||
 | 
							length = len(data)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// originally srnd (and srndv2) used 9600==0x2580
 | 
				
			||||||
 | 
						// however, range shifted by 0x10 looks better to me (cathugger)
 | 
				
			||||||
 | 
						// (instead of `▀▁▂▃▄▅▆▇█▉▊▋▌▍▎▏` it'll use `⚀⚁⚂⚃⚄⚅⚆⚇⚈⚉⚊⚋⚌⚍⚎⚏`)
 | 
				
			||||||
 | 
						// and display equaly good both in torbrowser+DejaVuSans and phone
 | 
				
			||||||
 | 
						// since jeff ack'd it (he doesn't care probably), I'll just use it
 | 
				
			||||||
 | 
						const rstart = 0x2590
 | 
				
			||||||
 | 
						// 0x2500 can display with TBB font whitelist, but looks too cryptic.
 | 
				
			||||||
 | 
						// startin from 0x2600 needs more than DejaVuSans so I'll avoid it
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// logic (same as in srnd):
 | 
				
			||||||
 | 
						// it first writes length/2 chars of begining
 | 
				
			||||||
 | 
						// and then length/2 chars of ending
 | 
				
			||||||
 | 
						// if length==len(data), that essentially means just using whole
 | 
				
			||||||
 | 
						i := 0
 | 
				
			||||||
 | 
						for ; i < length/2; i++ {
 | 
				
			||||||
 | 
							b.WriteRune(rstart + rune(data[i]))
 | 
				
			||||||
 | 
							b.WriteRune(0xFE0E) // text style variant
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for ; i < length; i++ {
 | 
				
			||||||
 | 
							b.WriteRune(rstart + rune(data[len(data)-length+i]))
 | 
				
			||||||
 | 
							b.WriteRune(0xFE0E) // text style variant
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return b.String()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// make a utf-8 tripcode
 | 
					// make a utf-8 tripcode
 | 
				
			||||||
func makeTripcode(pk string) string {
 | 
					func makeTripcode(pk string) string {
 | 
				
			||||||
	data, err := hex.DecodeString(pk)
 | 
						return makeTripcodeLen(pk, 0)
 | 
				
			||||||
	if err == nil {
 | 
					 | 
				
			||||||
		tripcode := ""
 | 
					 | 
				
			||||||
		//  here is the python code this is based off of
 | 
					 | 
				
			||||||
		//  i do something slightly different but this is the base
 | 
					 | 
				
			||||||
		//
 | 
					 | 
				
			||||||
		//  for x in range(0, length / 2):
 | 
					 | 
				
			||||||
		//    pub_short += '&#%i;' % (9600 + int(full_pubkey_hex[x*2:x*2+2], 16))
 | 
					 | 
				
			||||||
		//  length -= length / 2
 | 
					 | 
				
			||||||
		//  for x in range(0, length):
 | 
					 | 
				
			||||||
		//    pub_short += '&#%i;' % (9600 + int(full_pubkey_hex[-(length*2):][x*2:x*2+2], 16))
 | 
					 | 
				
			||||||
		//
 | 
					 | 
				
			||||||
		for _, c := range data {
 | 
					 | 
				
			||||||
			ch := 9600
 | 
					 | 
				
			||||||
			ch += int(c)
 | 
					 | 
				
			||||||
			tripcode += fmt.Sprintf("&#%04d;", ch)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return tripcode
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return "[invalid]"
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// generate a new message id with base name
 | 
					// generate a new message id with base name
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,7 +15,7 @@ Dependancies:
 | 
				
			|||||||
* imagemagick
 | 
					* imagemagick
 | 
				
			||||||
* ffmpeg
 | 
					* ffmpeg
 | 
				
			||||||
* sox
 | 
					* sox
 | 
				
			||||||
* go 1.9
 | 
					* go 
 | 
				
			||||||
* GNU make
 | 
					* GNU make
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Debian instructions
 | 
					## Debian instructions
 | 
				
			||||||
@@ -24,7 +24,7 @@ These are installation instructions for Debian.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
### Install Go
 | 
					### Install Go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Install the Go programming language version _1.9_ from the [Go website](https://golang.org/dl/).
 | 
					Install the latest version of the Go programming language from the [Go website](https://golang.org/dl/).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Install the dependancies
 | 
					### Install the dependancies
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user