@ -12,6 +12,8 @@ import (
@@ -12,6 +12,8 @@ import (
"os"
"os/exec"
"path"
"path/filepath"
"sort"
"strconv"
"strings"
"time"
@ -48,7 +50,66 @@ func writeStatus(c net.Conn, code int) {
@@ -48,7 +50,66 @@ func writeStatus(c net.Conn, code int) {
writeHeader ( c , code , meta )
}
func serveFile ( c net . Conn , requestData , filePath string ) {
func serveDirectory ( c net . Conn , request * url . URL , dirPath string ) {
var files [ ] os . FileInfo
err := filepath . Walk ( dirPath , func ( path string , info os . FileInfo , err error ) error {
if err != nil {
return err
} else if path == dirPath {
return nil
}
files = append ( files , info )
if info . IsDir ( ) {
return filepath . SkipDir
}
return nil
} )
if err != nil {
writeStatus ( c , gemini . StatusTemporaryFailure )
return
}
// List directories first
sort . Slice ( files , func ( i , j int ) bool {
iDir := files [ i ] . IsDir ( ) || files [ i ] . Mode ( ) & os . ModeSymlink != 0
jDir := files [ j ] . IsDir ( ) || files [ j ] . Mode ( ) & os . ModeSymlink != 0
if iDir != jDir {
return iDir
}
return i < j
} )
writeHeader ( c , gemini . StatusSuccess , "text/gemini; charset=utf-8" )
c . Write ( [ ] byte ( "# " + request . Path + "\r\n\r\n" ) )
if request . Path != "/" {
c . Write ( [ ] byte ( "=> ../ ../\r\n\r\n" ) )
}
for _ , info := range files {
fileName := info . Name ( )
filePath := url . PathEscape ( info . Name ( ) )
if info . IsDir ( ) || info . Mode ( ) & os . ModeSymlink != 0 {
fileName += "/"
filePath += "/"
}
c . Write ( [ ] byte ( "=> " + fileName + " " + filePath + "\r\n" ) )
if info . IsDir ( ) || info . Mode ( ) & os . ModeSymlink != 0 {
c . Write ( [ ] byte ( "\r\n" ) )
continue
}
modified := "Never"
if ! info . ModTime ( ) . IsZero ( ) {
modified = info . ModTime ( ) . Format ( "2006-01-02 3:04 PM" )
}
c . Write ( [ ] byte ( modified + " - " + formatFileSize ( info . Size ( ) ) + "\r\n\r\n" ) )
}
}
func serveFile ( c net . Conn , request * url . URL , requestData , filePath string , listDir bool ) {
fi , err := os . Stat ( filePath )
if os . IsNotExist ( err ) {
writeStatus ( c , gemini . StatusNotFound )
@ -58,6 +119,9 @@ func serveFile(c net.Conn, requestData, filePath string) {
@@ -58,6 +119,9 @@ func serveFile(c net.Conn, requestData, filePath string) {
return
}
originalPath := filePath
var fetchIndex bool
if mode := fi . Mode ( ) ; mode . IsDir ( ) {
if requestData [ len ( requestData ) - 1 ] != '/' {
// Add trailing slash
@ -66,6 +130,8 @@ func serveFile(c net.Conn, requestData, filePath string) {
@@ -66,6 +130,8 @@ func serveFile(c net.Conn, requestData, filePath string) {
return
}
fetchIndex = true
_ , err := os . Stat ( path . Join ( filePath , "index.gemini" ) )
if err == nil {
filePath = path . Join ( filePath , "index.gemini" )
@ -76,6 +142,10 @@ func serveFile(c net.Conn, requestData, filePath string) {
@@ -76,6 +142,10 @@ func serveFile(c net.Conn, requestData, filePath string) {
fi , err = os . Stat ( filePath )
if os . IsNotExist ( err ) {
if fetchIndex && listDir {
serveDirectory ( c , request , originalPath )
return
}
writeStatus ( c , gemini . StatusNotFound )
return
} else if err != nil {
@ -268,29 +338,29 @@ func handleConn(c net.Conn) {
@@ -268,29 +338,29 @@ func handleConn(c net.Conn) {
matchedHost = true
for _ , serve := range config . Hosts [ hostname ] {
if serve . Proxy != "" {
if serve . r != nil && serve . r . Match ( pathBytes ) {
if serve . r != nil && serve . r . Match ( pathBytes ) {
if serve . Proxy != "" {
serveProxy ( c , requestData , serve . Proxy )
return
} else if serve . r == nil && strings . HasPrefix ( request . Path , serve . Path ) {
serveProxy ( c , requestData , serve . Proxy )
} else if serve . cmd != nil {
serveCommand ( c , serve . cmd )
return
}
} else if serve . cmd != nil {
if serve . r != nil && serve . r . Match ( pathBytes ) {
serveCommand ( c , serve . cmd )
serveFile ( c , request , requestData , path . Join ( serve . Root , strippedPath ) , serve . ListDirectory )
return
} else if serve . r == nil && strings . HasPrefix ( request . Path , serve . Path ) {
if serve . Proxy != "" {
serveProxy ( c , requestData , serve . Proxy )
return
} else if serve . r == nil && strings . HasPrefix ( request . Path , serve . Path ) {
} else if serve . cmd != nil {
serveCommand ( c , serve . cmd )
return
}
}
if serve . r != nil && serve . r . Match ( pathBytes ) {
serveFile ( c , requestData , path . Join ( serve . Root , strippedPath ) )
return
} else if serve . r == nil && strings . HasPrefix ( request . Path , serve . Path ) {
serveFile ( c , requestData , path . Join ( serve . Root , strippedPath [ len ( serve . Path ) - 1 : ] ) )
filePath := request . Path [ len ( serve . Path ) : ]
if len ( filePath ) > 0 && filePath [ 0 ] == '/' {
filePath = filePath [ 1 : ]
}
serveFile ( c , request , requestData , path . Join ( serve . Root , filePath ) , serve . ListDirectory )
return
}
}