예제 #1
0
파일: util.go 프로젝트: ZiRo-/srndv2
// given a tripcode after the #
// make a seed byteslice
func parseTripcodeSecret(str string) []byte {
	// try decoding hex
	raw := unhex(str)
	keylen := nacl.CryptoSignSeedLen()
	if raw == nil || len(raw) != keylen {
		// treat this as a "regular" chan tripcode
		// decode as bytes then pad the rest with 0s if it doesn't fit
		raw = make([]byte, keylen)
		str_bytes := []byte(str)
		if len(str_bytes) > keylen {
			copy(raw, str_bytes[:keylen])
		} else {
			copy(raw, str_bytes)
		}
	}
	return raw
}
예제 #2
0
// turn a post request into an nntp article write it to temp dir and tell daemon
func (self *httpFrontend) handle_postRequest(pr *postRequest, b bannedFunc, e errorFunc, s successFunc, createGroup bool) {
	var err error
	if len(pr.Attachments) > self.attachmentLimit {
		err = errors.New("too many attachments")
		e(err)
		return
	}
	nntp := new(nntpArticle)
	defer nntp.Reset()
	var banned bool
	nntp.headers = make(ArticleHeaders)
	address := pr.IpAddress
	// check for banned
	if len(address) > 0 {
		banned, err = self.daemon.database.CheckIPBanned(address)
		if err == nil {
			if banned {
				b()
				return
			}
		} else {
			e(err)
			return
		}
	}
	if len(address) == 0 {
		address = "Tor"
	}

	if !strings.HasPrefix(address, "127.") {
		// set the ip address of the poster to be put into article headers
		// if we cannot determine it, i.e. we are on Tor/i2p, this value is not set
		if address == "Tor" {
			nntp.headers.Set("X-Tor-Poster", "1")
		} else {
			address, err = self.daemon.database.GetEncAddress(address)
			if err == nil {
				nntp.headers.Set("X-Encrypted-IP", address)
			} else {
				e(err)
				return
			}
			// TODO: add x-tor-poster header for tor exits
		}
	}

	// always lower case newsgroups
	board := strings.ToLower(pr.Group)

	// post fail message
	banned, err = self.daemon.database.NewsgroupBanned(board)
	if banned {
		e(errors.New("newsgroup banned "))
		return
	}
	if err != nil {
		e(err)
	}

	if !createGroup && !self.daemon.database.HasNewsgroup(board) {
		e(errors.New("we don't have this newsgroup " + board))
		return
	}

	// if we don't have an address for the poster try checking for i2p httpd headers
	if len(pr.Destination) == i2pDestHashLen() {
		nntp.headers.Set("X-I2P-DestHash", pr.Destination)
	}

	ref := pr.Reference
	if len(ref) > 0 {
		if ValidMessageID(ref) {
			if self.daemon.database.HasArticleLocal(ref) {
				nntp.headers.Set("References", ref)
			} else {
				e(errors.New("article referenced not locally available"))
				return
			}
		} else {
			e(errors.New("invalid reference"))
			return
		}
	}

	// set newsgroup
	nntp.headers.Set("Newsgroups", pr.Group)

	// check message size
	if len(pr.Attachments) == 0 && len(pr.Message) == 0 {
		e(errors.New("no message"))
		return
	}
	// TODO: make configurable
	if len(pr.Message) > 1024*1024 {
		e(errors.New("your message is too big"))
		return
	}

	if len(pr.Frontend) == 0 {
		// :-DDD
		pr.Frontend = "mongo.db.is.web.scale"
	} else if len(pr.Frontend) > 128 {
		e(errors.New("frontend name is too long"))
		return
	}

	subject := pr.Subject

	// set subject
	if len(subject) == 0 {
		subject = "None"
	} else if len(subject) > 256 {
		// subject too big
		e(errors.New("Subject is too long"))
		return
	}

	nntp.headers.Set("Subject", subject)
	if isSage(subject) {
		nntp.headers.Set("X-Sage", "1")
	}

	name := pr.Name

	var tripcode_privkey []byte

	// set name
	if len(name) == 0 {
		name = "Anonymous"
	} else {
		idx := strings.Index(name, "#")
		// tripcode
		if idx >= 0 {
			tripcode_privkey = parseTripcodeSecret(name[idx+1:])
			name = strings.Trim(name[:idx], "\t ")
			if name == "" {
				name = "Anonymous"
			}
		}
	}
	if len(name) > 128 {
		// name too long
		e(errors.New("name too long"))
		return
	}
	msgid := genMessageID(pr.Frontend)
	// roll until dubs if desired
	for pr.Dubs && !MessageIDWillDoDubs(msgid) {
		msgid = genMessageID(pr.Frontend)
	}

	nntp.headers.Set("From", nntpSanitize(fmt.Sprintf("%s <poster@%s>", name, pr.Frontend)))
	nntp.headers.Set("Message-ID", msgid)

	// set message
	nntp.message = createPlaintextAttachment([]byte(pr.Message))
	// set date
	nntp.headers.Set("Date", timeNowStr())
	// append path from frontend
	nntp.AppendPath(pr.Frontend)

	// add extra headers if needed
	if pr.ExtraHeaders != nil {
		for name, val := range pr.ExtraHeaders {
			// don't overwrite existing headers
			if nntp.headers.Get(name, "") == "" {
				nntp.headers.Set(name, val)
			}
		}
	}
	if self.attachments {
		var delfiles []string
		for _, att := range pr.Attachments {
			// add attachment
			if len(att.Filedata) > 0 {
				a := createAttachment(att.Filetype, att.Filename, strings.NewReader(att.Filedata))
				nntp.Attach(a)
				err = a.Save(self.daemon.store.AttachmentDir())
				if err == nil {
					delfiles = append(delfiles, a.Filepath())
					// check if we need to thumbnail it
					if !CheckFile(self.daemon.store.ThumbnailFilepath(a.Filepath())) {
						err = self.daemon.store.GenerateThumbnail(a.Filepath())
					}
					if err == nil {
						delfiles = append(delfiles, self.daemon.store.ThumbnailFilepath(a.Filepath()))
					}
				}
				if err != nil {
					break
				}
			}
		}
		if err != nil {
			// nuke files
			for _, fname := range delfiles {
				DelFile(fname)
			}
			e(err)
			return
		}
	}
	// pack it before sending so that the article is well formed
	nntp.Pack()
	// sign if needed
	if len(tripcode_privkey) == nacl.CryptoSignSeedLen() {
		err = self.daemon.store.RegisterPost(nntp)
		if err != nil {
			e(err)
			return
		}
		nntp, err = signArticle(nntp, tripcode_privkey)
		if err != nil {
			// error signing
			e(err)
			return
		}
		if err == nil {
			err = self.daemon.store.RegisterSigned(nntp.MessageID(), nntp.Pubkey())
		}
	} else {
		err = self.daemon.store.RegisterPost(nntp)
	}
	if err != nil {
		e(err)
		return
	}
	// have daemon sign message
	// DON'T Wrap sign yet
	// wrapped := self.daemon.WrapSign(nntp)
	// save it
	f := self.daemon.store.CreateFile(nntp.MessageID())
	if f == nil {
		e(errors.New("failed to store article"))
		return
	} else {
		err = nntp.WriteTo(f)
		f.Close()
		if err == nil {
			self.daemon.loadFromInfeed(nntp.MessageID())
			s(nntp)
			return
		}
		// clean up
		DelFile(self.daemon.store.GetFilename(nntp.MessageID()))
		e(err)
	}
}