parent
6d3e8028cb
commit
26be9b5104
11
handlers.go
11
handlers.go
@ -98,7 +98,7 @@ func (rl *rushlink) viewPasteHandlerFlags(w http.ResponseWriter, r *http.Request
|
|||||||
key := vars["key"]
|
key := vars["key"]
|
||||||
var p *db.Paste
|
var p *db.Paste
|
||||||
var fu *db.FileUpload
|
var fu *db.FileUpload
|
||||||
if err := rl.db.Bolt.View(func(tx *bolt.Tx) error {
|
err := rl.db.Bolt.View(func(tx *bolt.Tx) error {
|
||||||
var err error
|
var err error
|
||||||
p, err = db.GetPaste(tx, key)
|
p, err = db.GetPaste(tx, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -113,12 +113,13 @@ func (rl *rushlink) viewPasteHandlerFlags(w http.ResponseWriter, r *http.Request
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}); err != nil {
|
})
|
||||||
|
if err != nil {
|
||||||
|
status := db.ErrHTTPStatusCode(err)
|
||||||
|
if status == http.StatusInternalServerError {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
rl.renderError(w, r, status, err.Error())
|
||||||
if p == nil {
|
|
||||||
rl.renderError(w, r, http.StatusNotFound, "url key not found in the database")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -53,6 +54,35 @@ const (
|
|||||||
PasteStateDeleted
|
PasteStateDeleted
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// minKeyLen specifies the mimimum length of a paste key.
|
||||||
|
const minKeyLen = 4
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrKeyInvalidChar occurs when a key contains an invalid character.
|
||||||
|
ErrKeyInvalidChar = errors.New("invalid character in key")
|
||||||
|
// ErrKeyInvalidLength occurs when a key embeds a length that is incorrect.
|
||||||
|
ErrKeyInvalidLength = errors.New("key length encoding is incorrect")
|
||||||
|
// ErrPasteDoesNotExist occurs when a key does not exist in the database.
|
||||||
|
ErrPasteDoesNotExist = errors.New("url key not found in the database")
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrHTTPStatusCode returns the HTTP status code that should correspond to
|
||||||
|
// the provided error.
|
||||||
|
// server error, or false if it is not.
|
||||||
|
func ErrHTTPStatusCode(err error) int {
|
||||||
|
switch err {
|
||||||
|
case nil:
|
||||||
|
return 0
|
||||||
|
case ErrKeyInvalidChar:
|
||||||
|
return http.StatusNotFound
|
||||||
|
case ErrKeyInvalidLength:
|
||||||
|
return http.StatusNotFound
|
||||||
|
case ErrPasteDoesNotExist:
|
||||||
|
return http.StatusNotFound
|
||||||
|
}
|
||||||
|
return http.StatusInternalServerError
|
||||||
|
}
|
||||||
|
|
||||||
// Base64 encoding and decoding
|
// Base64 encoding and decoding
|
||||||
var base64Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
|
var base64Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
|
||||||
var base64Encoder = base64.RawURLEncoding.WithPadding(base64.NoPadding)
|
var base64Encoder = base64.RawURLEncoding.WithPadding(base64.NoPadding)
|
||||||
@ -87,13 +117,45 @@ func (t PasteState) String() string {
|
|||||||
|
|
||||||
// GetPaste retrieves a paste from the database.
|
// GetPaste retrieves a paste from the database.
|
||||||
func GetPaste(tx *bolt.Tx, key string) (*Paste, error) {
|
func GetPaste(tx *bolt.Tx, key string) (*Paste, error) {
|
||||||
|
if err := ValidatePasteKey(key); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return GetPasteNoValidate(tx, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidatePasteKey validates the format of the key that has
|
||||||
|
func ValidatePasteKey(key string) error {
|
||||||
|
internalLen := minKeyLen
|
||||||
|
countingOnes := true
|
||||||
|
for _, ch := range key {
|
||||||
|
limb := strings.IndexRune(base64Alphabet, ch)
|
||||||
|
if limb == -1 {
|
||||||
|
return ErrKeyInvalidChar
|
||||||
|
}
|
||||||
|
for i := 5; i >= 0 && countingOnes; i-- {
|
||||||
|
if (limb>>uint(i))&0x1 == 0 {
|
||||||
|
countingOnes = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
internalLen++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if internalLen != len(key) {
|
||||||
|
return ErrKeyInvalidLength
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPasteNoValidate retrieves a paste from the database without validating
|
||||||
|
// the key format first.
|
||||||
|
func GetPasteNoValidate(tx *bolt.Tx, key string) (*Paste, error) {
|
||||||
pastesBucket := tx.Bucket([]byte(BucketPastes))
|
pastesBucket := tx.Bucket([]byte(BucketPastes))
|
||||||
if pastesBucket == nil {
|
if pastesBucket == nil {
|
||||||
return nil, errors.Errorf("bucket %v does not exist", BucketPastes)
|
return nil, errors.Errorf("bucket %v does not exist", BucketPastes)
|
||||||
}
|
}
|
||||||
storedBytes := pastesBucket.Get([]byte(key))
|
storedBytes := pastesBucket.Get([]byte(key))
|
||||||
if storedBytes == nil {
|
if storedBytes == nil {
|
||||||
return nil, nil
|
return nil, ErrPasteDoesNotExist
|
||||||
}
|
}
|
||||||
return decodePaste(storedBytes)
|
return decodePaste(storedBytes)
|
||||||
}
|
}
|
||||||
@ -214,7 +276,6 @@ func GeneratePasteKey(tx *bolt.Tx, minimumEntropy int) (string, error) {
|
|||||||
// is used to ensure that a new key has at least some amount of guessing
|
// is used to ensure that a new key has at least some amount of guessing
|
||||||
// entropy.
|
// entropy.
|
||||||
func generatePasteKeyInner(epoch, entropy int) (string, error) {
|
func generatePasteKeyInner(epoch, entropy int) (string, error) {
|
||||||
minKeyLen := 4
|
|
||||||
entropyEpoch := entropy
|
entropyEpoch := entropy
|
||||||
entropyEpoch -= minKeyLen * 6 // First 4 characters provide 24 bits.
|
entropyEpoch -= minKeyLen * 6 // First 4 characters provide 24 bits.
|
||||||
entropyEpoch++ // One bit less because of '0' bit.
|
entropyEpoch++ // One bit less because of '0' bit.
|
||||||
|
Loading…
Reference in New Issue
Block a user