2018-11-20 04:07:36 +00:00
package routes
import (
"database/sql"
"net/http"
"path/filepath"
"strconv"
"strings"
2019-04-19 06:36:26 +00:00
c "github.com/Azareal/Gosora/common"
2020-01-23 06:17:50 +00:00
qgen "github.com/Azareal/Gosora/query_gen"
2018-11-20 04:07:36 +00:00
)
type AttachmentStmts struct {
get * sql . Stmt
}
var attachmentStmts AttachmentStmts
// TODO: Abstract this with an attachment store
func init ( ) {
2019-04-19 06:36:26 +00:00
c . DbInits . Add ( func ( acc * qgen . Accumulator ) error {
2018-11-20 04:07:36 +00:00
attachmentStmts = AttachmentStmts {
2020-01-23 06:17:50 +00:00
get : acc . Select ( "attachments" ) . Columns ( "sectionID, sectionTable, originID, originTable, uploadedBy, path" ) . Where ( "path=? AND sectionID=? AND sectionTable=?" ) . Prepare ( ) ,
2018-11-20 04:07:36 +00:00
}
return acc . FirstError ( )
} )
}
2020-01-23 06:17:50 +00:00
var maxAgeYear = "max-age=" + strconv . Itoa ( int ( c . Year ) )
2019-04-19 06:36:26 +00:00
func ShowAttachment ( w http . ResponseWriter , r * http . Request , user c . User , filename string ) c . RouteError {
filename = c . Stripslashes ( filename )
2019-07-26 23:29:42 +00:00
ext := filepath . Ext ( "./attachs/" + filename )
2019-04-19 06:36:26 +00:00
if ! c . AllowedFileExts . Contains ( strings . TrimPrefix ( ext , "." ) ) {
return c . LocalError ( "Bad extension" , w , r , user )
2018-11-20 04:07:36 +00:00
}
2019-10-01 21:06:22 +00:00
sid , err := strconv . Atoi ( r . FormValue ( "sid" ) )
2018-11-20 04:07:36 +00:00
if err != nil {
2019-10-01 21:06:22 +00:00
return c . LocalError ( "The sid is not an integer" , w , r , user )
2018-11-20 04:07:36 +00:00
}
2019-07-25 07:00:49 +00:00
sectionTable := r . FormValue ( "stype" )
2018-11-20 04:07:36 +00:00
var originTable string
var originID , uploadedBy int
2019-10-01 21:06:22 +00:00
err = attachmentStmts . get . QueryRow ( filename , sid , sectionTable ) . Scan ( & sid , & sectionTable , & originID , & originTable , & uploadedBy , & filename )
2018-11-20 04:07:36 +00:00
if err == sql . ErrNoRows {
2019-04-19 06:36:26 +00:00
return c . NotFound ( w , r , nil )
2018-11-20 04:07:36 +00:00
} else if err != nil {
2019-04-19 06:36:26 +00:00
return c . InternalError ( err , w , r )
2018-11-20 04:07:36 +00:00
}
if sectionTable == "forums" {
2019-10-01 21:06:22 +00:00
_ , ferr := c . SimpleForumUserCheck ( w , r , & user , sid )
2018-11-20 04:07:36 +00:00
if ferr != nil {
return ferr
}
if ! user . Perms . ViewTopic {
2019-04-19 06:36:26 +00:00
return c . NoPermissions ( w , r , user )
2018-11-20 04:07:36 +00:00
}
} else {
2019-04-19 06:36:26 +00:00
return c . LocalError ( "Unknown section" , w , r , user )
2018-11-20 04:07:36 +00:00
}
2020-01-23 06:17:50 +00:00
2018-11-20 04:07:36 +00:00
if originTable != "topics" && originTable != "replies" {
2019-04-19 06:36:26 +00:00
return c . LocalError ( "Unknown origin" , w , r , user )
2018-11-20 04:07:36 +00:00
}
if ! user . Loggedin {
2020-01-23 06:17:50 +00:00
w . Header ( ) . Set ( "Cache-Control" , maxAgeYear )
2018-11-20 04:07:36 +00:00
} else {
2019-04-19 06:36:26 +00:00
guest := c . GuestUser
2019-10-01 21:06:22 +00:00
_ , ferr := c . SimpleForumUserCheck ( w , r , & guest , sid )
2018-11-20 04:07:36 +00:00
if ferr != nil {
return ferr
}
2019-11-04 11:55:52 +00:00
h := w . Header ( )
2018-11-20 04:07:36 +00:00
if guest . Perms . ViewTopic {
2020-01-23 06:17:50 +00:00
h . Set ( "Cache-Control" , maxAgeYear )
2018-11-20 04:07:36 +00:00
} else {
2019-11-04 11:55:52 +00:00
h . Set ( "Cache-Control" , "private" )
2018-11-20 04:07:36 +00:00
}
}
// TODO: Fix the problem where non-existent files aren't greeted with custom 404s on ServeFile()'s side
http . ServeFile ( w , r , "./attachs/" + filename )
return nil
}
2018-12-28 02:08:35 +00:00
2019-04-19 06:36:26 +00:00
func deleteAttachment ( w http . ResponseWriter , r * http . Request , user c . User , aid int , js bool ) c . RouteError {
2020-01-23 06:17:50 +00:00
err := c . DeleteAttachment ( aid )
2018-12-28 02:08:35 +00:00
if err == sql . ErrNoRows {
2019-04-19 06:36:26 +00:00
return c . NotFoundJSQ ( w , r , nil , js )
2020-02-18 22:43:55 +00:00
} else if err != nil {
return c . InternalErrorJSQ ( err , w , r , js )
2018-12-28 02:08:35 +00:00
}
2020-02-18 22:43:55 +00:00
return nil
2018-12-28 02:08:35 +00:00
}
// TODO: Stop duplicating this code
// TODO: Use a transaction here
// TODO: Move this function to neutral ground
2020-02-09 10:00:08 +00:00
func uploadAttachment ( w http . ResponseWriter , r * http . Request , user c . User , sid int , sectionTable string , oid int , originTable , extra string ) ( pathMap map [ string ] string , rerr c . RouteError ) {
2018-12-28 02:08:35 +00:00
pathMap = make ( map [ string ] string )
files , rerr := uploadFilesWithHash ( w , r , user , "./attachs/" )
if rerr != nil {
return nil , rerr
}
for _ , filename := range files {
2019-04-19 06:36:26 +00:00
aid , err := c . Attachments . Add ( sid , sectionTable , oid , originTable , user . ID , filename , extra )
2018-12-28 02:08:35 +00:00
if err != nil {
2019-04-19 06:36:26 +00:00
return nil , c . InternalError ( err , w , r )
2018-12-28 02:08:35 +00:00
}
_ , ok := pathMap [ filename ]
if ok {
pathMap [ filename ] += "," + strconv . Itoa ( aid )
} else {
pathMap [ filename ] = strconv . Itoa ( aid )
}
switch originTable {
case "topics" :
2019-04-19 06:36:26 +00:00
_ , err = topicStmts . updateAttachs . Exec ( c . Attachments . CountIn ( originTable , oid ) , oid )
2018-12-28 02:08:35 +00:00
if err != nil {
2019-04-19 06:36:26 +00:00
return nil , c . InternalError ( err , w , r )
2018-12-28 02:08:35 +00:00
}
2019-04-19 06:36:26 +00:00
err = c . Topics . Reload ( oid )
2018-12-28 02:08:35 +00:00
if err != nil {
2019-04-19 06:36:26 +00:00
return nil , c . InternalError ( err , w , r )
2018-12-28 02:08:35 +00:00
}
case "replies" :
2019-04-19 06:36:26 +00:00
_ , err = replyStmts . updateAttachs . Exec ( c . Attachments . CountIn ( originTable , oid ) , oid )
2018-12-28 02:08:35 +00:00
if err != nil {
2019-04-19 06:36:26 +00:00
return nil , c . InternalError ( err , w , r )
2018-12-28 02:08:35 +00:00
}
}
}
return pathMap , nil
}