aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--Database.go36
-rw-r--r--README.md30
-rw-r--r--activityPubStruct.go7
-rw-r--r--client.go10
-rw-r--r--databaseschema.psql9
-rw-r--r--main.go253
-rw-r--r--static/bottom.html2
-rw-r--r--static/manage.html2
-rw-r--r--static/npost.html2
-rw-r--r--static/posts.html8
-rw-r--r--verification.go68
-rw-r--r--webfinger.go12
13 files changed, 373 insertions, 69 deletions
diff --git a/.gitignore b/.gitignore
index 1f7a89d..9789d20 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,5 @@
#*
public/
config
-clientkey \ No newline at end of file
+clientkey
+pem/
diff --git a/Database.go b/Database.go
index a83c37d..3e890dd 100644
--- a/Database.go
+++ b/Database.go
@@ -12,7 +12,7 @@ import "sort"
func GetActorFromDB(db *sql.DB, id string) Actor {
var nActor Actor
- query :=`select type, id, name, preferedusername, inbox, outbox, following, followers, restricted, summary from actor where id=$1`
+ query :=`select type, id, name, preferedusername, inbox, outbox, following, followers, restricted, summary, publickeypem from actor where id=$1`
rows, err := db.Query(query, id)
@@ -20,19 +20,22 @@ func GetActorFromDB(db *sql.DB, id string) Actor {
return nActor
}
- defer rows.Close()
+ var publicKeyPem string
+ defer rows.Close()
for rows.Next() {
- err = rows.Scan(&nActor.Type, &nActor.Id, &nActor.Name, &nActor.PreferredUsername, &nActor.Inbox, &nActor.Outbox, &nActor.Following, &nActor.Followers, &nActor.Restricted, &nActor.Summary)
+ err = rows.Scan(&nActor.Type, &nActor.Id, &nActor.Name, &nActor.PreferredUsername, &nActor.Inbox, &nActor.Outbox, &nActor.Following, &nActor.Followers, &nActor.Restricted, &nActor.Summary, &publicKeyPem)
CheckError(err, "error with actor from db scan ")
}
+ nActor.PublicKey = GetActorPemFromDB(db, publicKeyPem)
+
return nActor
}
func GetActorByNameFromDB(db *sql.DB, name string) Actor {
var nActor Actor
- query :=`select type, id, name, preferedusername, inbox, outbox, following, followers, restricted, summary from actor where name=$1`
+ query :=`select type, id, name, preferedusername, inbox, outbox, following, followers, restricted, summary, publickeypem from actor where name=$1`
rows, err := db.Query(query, name)
@@ -40,12 +43,15 @@ func GetActorByNameFromDB(db *sql.DB, name string) Actor {
return nActor
}
+ var publicKeyPem string
defer rows.Close()
for rows.Next() {
- err = rows.Scan(&nActor.Type, &nActor.Id, &nActor.Name, &nActor.PreferredUsername, &nActor.Inbox, &nActor.Outbox, &nActor.Following, &nActor.Followers, &nActor.Restricted, &nActor.Summary)
+ err = rows.Scan(&nActor.Type, &nActor.Id, &nActor.Name, &nActor.PreferredUsername, &nActor.Inbox, &nActor.Outbox, &nActor.Following, &nActor.Followers, &nActor.Restricted, &nActor.Summary, &publicKeyPem)
CheckError(err, "error with actor from db scan ")
}
+ nActor.PublicKey = GetActorPemFromDB(db, publicKeyPem)
+
return nActor
}
@@ -120,6 +126,8 @@ func CreateNewBoardDB(db *sql.DB, actor Actor) Actor{
SetActorFollowingDB(db, response)
MakeActivityRequest(db, nActivity)
}
+
+ CreatePem(db, actor)
}
return actor
@@ -1206,3 +1214,21 @@ func GetActorReportedDB(db *sql.DB, id string) []ObjectBase {
return nObj
}
+
+func GetActorPemFromDB(db *sql.DB, pemID string) PublicKeyPem {
+ query := `select id, owner, file from publickeypem where id=$1`
+ rows, err := db.Query(query, pemID)
+
+ CheckError(err, "could not get public key pem from database")
+
+ var pem PublicKeyPem
+
+ defer rows.Close()
+ rows.Next()
+ rows.Scan(&pem.Id, &pem.Owner, &pem.PublicKeyPem)
+ f, _ := os.ReadFile(pem.PublicKeyPem)
+
+ pem.PublicKeyPem = strings.ReplaceAll(string(f), "\r\n", `\n`)
+
+ return pem
+}
diff --git a/README.md b/README.md
index 05ff96b..8da0dd6 100644
--- a/README.md
+++ b/README.md
@@ -74,36 +74,18 @@ Any contributions or suggestions are appreciated. Best way to give immediate fee
`emailpass:password`
+#### local testing
+ When testing on a local env when setting the `instance` value in the config file you have to append the port number to the local address eg. `instance:localhost:3000` with `instanceport` also being set to the same port.
+
+ If you want to test federation between servers locally you have to use your local ip as the `instance` eg. `instance:192.168.0.2:3000` and `instance:192:168:0:3:3000` adding the port to localhost will not route correctly.
+
### Managing the server
To access the managment page to create new boards or subscribe to other boards, when you start the server the console will output the `Mod key` and `Admin Login`
Use the `Mod key` by appending it to your servers url, `https://fchan.xyz/[Mod key]` once there you will be prompted for the `Admin Login` credentials.
- You can manage each board by appending the `Mod key` to the desired board url: `https://fchan.xyz/g/[Mod Key]`
+ You can manage each board by appending the `Mod key` to the desired board url: `https://fchan.xyz/[Mod Key]/g`
The `Mod key` is not static and is reset on server restart.
-### Creating a new board
-
- `CreateNewBoardDB(db *sql.DB, actor Actor)`
-
- returns Actor.
-
-### Creating a new actor
-
- `CreateNewActor(board string, prefName string, summary string, authReq []string, restricted bool)`
-
- returns Actor
-
- - `board` is the abbreviated name such as `g`
-
- - `prefName` is the fully readable name such as `Technology`
-
- - `summary` is a summary of the board
-
- - `authReq` is an array string of required privileges to post on the board, default is: `[]string{"captcha","email","passphrase"}`
-
- - `restricted` is bool. `true` is blue board, `false` is red board
-
-
## Server Update
Check the git repo for the latest commits. If there are commits you want to update to, pull and restart instance.
diff --git a/activityPubStruct.go b/activityPubStruct.go
index 0955b0c..d74ae82 100644
--- a/activityPubStruct.go
+++ b/activityPubStruct.go
@@ -74,11 +74,18 @@ type Actor struct {
Followers string `json:"followers,omitempty"`
Name string `json:"name,omitempty"`
PreferredUsername string `json:"preferredUsername,omitempty"`
+ PublicKey PublicKeyPem `json:"publicKey,omitempty"`
Summary string `json:"summary,omitempty"`
AuthRequirement []string `json:"authrequirement,omitempty"`
Restricted bool `json:"restricted"`
}
+type PublicKeyPem struct {
+ Id string `json:"id,omitempty"`
+ Owner string `json:"owner,omitempty"`
+ PublicKeyPem string `json:"publicKeyPem,omitempty"`
+}
+
type Activity struct {
AtContext
Type string `json:"type,omitempty"`
diff --git a/client.go b/client.go
index 1ef6250..6ca3ee0 100644
--- a/client.go
+++ b/client.go
@@ -215,15 +215,17 @@ func PostGet(w http.ResponseWriter, r *http.Request, db *sql.DB){
name := GetActorFollowNameFromPath(path)
followActors := GetActorsFollowFromName(actor, name)
followCollection := GetActorsFollowPostFromId(db, followActors, postId)
-
- returnData.Board.Post.Actor = followCollection.Actor
-
+
DeleteRemovedPosts(db, &followCollection)
DeleteTombstoneReplies(&followCollection)
- if len(followCollection.OrderedItems) > 0 {
+ if len(followCollection.OrderedItems) > 0 {
returnData.Board.InReplyTo = followCollection.OrderedItems[0].Id
returnData.Posts = append(returnData.Posts, followCollection.OrderedItems[0])
+
+ var actor Actor
+ actor = FingerActor(returnData.Board.InReplyTo)
+ returnData.Board.Post.Actor = &actor
}
} else {
collection := GetObjectByIDFromDB(db, inReplyTo)
diff --git a/databaseschema.psql b/databaseschema.psql
index eac2c34..a9ede23 100644
--- a/databaseschema.psql
+++ b/databaseschema.psql
@@ -207,6 +207,13 @@ id varchar(100),
type varchar(25)
);
-
ALTER TABLE activitystream ADD COLUMN IF NOT EXISTS tripcode varchar(50) default '';
ALTER TABLE cacheactivitystream ADD COLUMN IF NOT EXISTS tripcode varchar(50) default '';
+
+CREATE TABLE IF NOT EXISTS publicKeyPem(
+id varchar(100) UNIQUE,
+owner varchar(100),
+file varchar(100)
+);
+
+ALTER TABLE actor ADD COLUMN IF NOT EXISTS publicKeyPem varchar(100) default '';
diff --git a/main.go b/main.go
index 35b9fe3..b37c41d 100644
--- a/main.go
+++ b/main.go
@@ -37,9 +37,7 @@ var activitystreams = "application/ld+json; profile=\"https://www.w3.org/ns/acti
func main() {
- if _, err := os.Stat("./public"); os.IsNotExist(err) {
- os.Mkdir("./public", 0755)
- }
+ CreatedNeededDirectories()
InitCache()
@@ -48,7 +46,7 @@ func main() {
defer db.Close()
RunDatabaseSchema(db)
-
+
go MakeCaptchas(db, 100)
*Key = CreateClientKey()
@@ -188,6 +186,7 @@ func main() {
page, _ := strconv.Atoi(postNum)
collection, valid := WantToServePage(db, actor.Name, page)
+
if valid {
OutboxGet(w, r, db, collection)
}
@@ -471,9 +470,14 @@ func main() {
FollowingBoards = GetActorFollowingDB(db, Domain)
- Boards = GetBoardCollection(db)
+ Boards = GetBoardCollection(db)
+
+ var redirect string
+ if(actor.Name != "main") {
+ redirect = "/" + actor.Name
+ }
- http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
+ http.Redirect(w, r, "/" + *Key + "/" + redirect, http.StatusSeeOther)
} else if manage && actor.Name != "" {
t := template.Must(template.ParseFiles("./static/main.html", "./static/manage.html"))
@@ -645,7 +649,7 @@ func main() {
Boards = GetBoardCollection(db)
}
- http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
+ http.Redirect(w, r, "/" + *Key, http.StatusSeeOther)
})
http.HandleFunc("/verify", func(w http.ResponseWriter, r *http.Request){
@@ -706,8 +710,10 @@ func main() {
http.HandleFunc("/delete", func(w http.ResponseWriter, r *http.Request){
id := r.URL.Query().Get("id")
- board := r.URL.Query().Get("board")
- actor := GetActorFromPath(db, id, "/")
+ manage := r.URL.Query().Get("manage")
+ board := r.URL.Query().Get("board")
+ col := GetCollectionFromID(id)
+ actor := col.OrderedItems[0].Actor
_, auth := GetPasswordFromSession(r)
if id == "" || auth == "" {
@@ -722,29 +728,58 @@ func main() {
return
}
+ var obj ObjectBase
+ obj.Id = id
+ obj.Actor = actor
+
+ isOP := CheckIfObjectOP(db, obj.Id)
+
+ var OP string
+ if len(col.OrderedItems[0].InReplyTo) > 0 {
+ OP = col.OrderedItems[0].InReplyTo[0].Id
+ }
+
if !IsIDLocal(db, id) {
- CreateLocalDeleteDB(db, id, "post")
- CloseLocalReportDB(db, id, board)
- http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
+ if(!isOP) {
+ CloseLocalReportDB(db, id, board)
+ CreateLocalDeleteDB(db, id, "post")
+ } else {
+
+ }
+ if(manage == "t") {
+ http.Redirect(w, r, "/" + *Key + "/" + board, http.StatusSeeOther)
+ } else if(OP != ""){
+ http.Redirect(w, r, "/" + board + "/" + remoteShort(OP), http.StatusSeeOther)
+ } else {
+ http.Redirect(w, r, "/" + board, http.StatusSeeOther)
+ }
+
return
}
- var obj ObjectBase
- obj.Id = id
- obj.Actor = &actor
-
- isOP := CheckIfObjectOP(db, obj.Id)
+
if !isOP {
+ DeleteReportActivity(db, id)
DeleteObjectRequest(db, id)
DeleteObject(db, obj.Id)
- http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
+ if(manage == "t"){
+ http.Redirect(w, r, "/" + *Key + "/" + board , http.StatusSeeOther)
+ }else{
+ http.Redirect(w, r, OP, http.StatusSeeOther)
+ }
return
+
} else {
+ DeleteReportActivity(db, id)
DeleteObjectAndRepliesRequest(db, id)
DeleteObjectAndReplies(db, obj.Id)
- http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
+ if(manage == "t"){
+ http.Redirect(w, r, "/" + *Key + "/" + board , http.StatusSeeOther)
+ }else{
+ http.Redirect(w, r, "/" + board, http.StatusSeeOther)
+ }
return
}
@@ -755,7 +790,18 @@ func main() {
http.HandleFunc("/deleteattach", func(w http.ResponseWriter, r *http.Request){
id := r.URL.Query().Get("id")
+ manage := r.URL.Query().Get("manage")
+ board := r.URL.Query().Get("board")
+ col := GetCollectionFromID(id)
+ actor := col.OrderedItems[0].Actor
+ var OP string
+ if (len(col.OrderedItems[0].InReplyTo) > 0 && col.OrderedItems[0].InReplyTo[0].Id != "") {
+ OP = col.OrderedItems[0].InReplyTo[0].Id
+ } else {
+ OP = id
+ }
+
_, auth := GetPasswordFromSession(r)
if id == "" || auth == "" {
@@ -764,23 +810,32 @@ func main() {
return
}
- actor := GetActorFromPath(db, id, "/")
-
if !HasAuth(db, auth, actor.Id) {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(""))
return
- }
+ }
+
+
if !IsIDLocal(db, id) {
CreateLocalDeleteDB(db, id, "attachment")
- http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
+ if(manage == "t") {
+ http.Redirect(w, r, "/" + *Key + "/" + board, http.StatusSeeOther)
+ } else {
+ http.Redirect(w, r, "/" + board + "/" + remoteShort(OP), http.StatusSeeOther)
+ }
return
}
DeleteAttachmentFromFile(db, id)
DeletePreviewFromFile(db, id)
- http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
+
+ if(manage == "t") {
+ http.Redirect(w, r, "/" + *Key + "/" + board, http.StatusSeeOther)
+ } else {
+ http.Redirect(w, r, OP, http.StatusSeeOther)
+ }
})
http.HandleFunc("/report", func(w http.ResponseWriter, r *http.Request){
@@ -817,13 +872,13 @@ func main() {
if !IsIDLocal(db, id) {
CloseLocalReportDB(db, id, board)
- http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
+ http.Redirect(w, r, "/" + *Key + "/" + board, http.StatusSeeOther)
return
}
reported := DeleteReportActivity(db, id)
if reported {
- http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
+ http.Redirect(w, r, "/" + *Key + "/" + board, http.StatusSeeOther)
return
}
@@ -834,13 +889,13 @@ func main() {
if !IsIDLocal(db, id) {
CreateLocalReportDB(db, id, board, reason)
- http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
+ http.Redirect(w, r, "/" + board + "/" + remoteShort(id), http.StatusSeeOther)
return
}
reported := ReportActivity(db, id, reason)
if reported {
- http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
+ http.Redirect(w, r, id, http.StatusSeeOther)
return
}
@@ -869,6 +924,48 @@ func main() {
w.Write([]byte(""))
})
+ http.HandleFunc("/.well-known/webfinger", func(w http.ResponseWriter, r *http.Request) {
+ acct := r.URL.Query()["resource"]
+
+ if(len(acct) < 1) {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte("resource needs a value"))
+ return
+ }
+
+ acct[0] = strings.Replace(acct[0], "acct:", "", -1)
+
+ actorDomain := strings.Split(acct[0], "@")
+
+ if(len(actorDomain) < 2) {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte("accpets only subject form of acct:board@instance"))
+ return
+ }
+
+ if !IsActorLocal(db, TP + "" + actorDomain[1] + "/" + actorDomain[0]) {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte("actor not local"))
+ return
+ }
+
+ var finger Webfinger
+ var link WebfingerLink
+
+ finger.Subject = "acct:" + actorDomain[0] + "@" + actorDomain[1]
+ link.Rel = "self"
+ link.Type = "application/activity+json"
+ link.Href = TP + "" + actorDomain[1] + "/" + actorDomain[0]
+
+ finger.Links = append(finger.Links, link)
+
+ enc, _ := json.Marshal(finger)
+
+ w.Header().Set("Content-Type", activitystreams)
+ w.Write(enc)
+
+ })
+
fmt.Println("Server for " + Domain + " running on port " + Port)
fmt.Println("Mod key: " + *Key)
@@ -2028,3 +2125,103 @@ func RunDatabaseSchema(db *sql.DB) {
CheckError(err, "could not exec databaseschema.psql")
}
}
+
+func CreatedNeededDirectories() {
+ if _, err := os.Stat("./public"); os.IsNotExist(err) {
+ os.Mkdir("./public", 0755)
+ }
+
+ if _, err := os.Stat("./pem/board"); os.IsNotExist(err) {
+ os.MkdirAll("./pem/board", 0700)
+ }
+}
+
+//looks for actor with pattern of board@instance
+func FingerActor(path string) Actor{
+
+ actor, instance := GetActorInstance(path)
+
+ r := FingerRequest(actor, instance)
+
+ var nActor Actor
+
+ if r.StatusCode == 200 {
+ defer r.Body.Close()
+
+ body, _ := ioutil.ReadAll(r.Body)
+
+ err := json.Unmarshal(body, &nActor)
+
+ CheckError(err, "error getting fingerrequet resp from json body")
+ }
+
+ return nActor
+}
+
+func FingerRequest(actor string, instance string) (*http.Response){
+ acct := "acct:" + actor + "@" + instance
+ req, err := http.NewRequest("GET", "http://" + instance + "/.well-known/webfinger?resource=" + acct, nil)
+
+ CheckError(err, "could not get finger request from id req")
+
+ req.Header.Set("Accept", activitystreams)
+
+ resp, err := http.DefaultClient.Do(req)
+
+ var finger Webfinger
+
+ if err != nil {
+ CheckError(err, "could not get actor from finger resp with id " + acct)
+ }
+
+ if resp.StatusCode == 200 {
+ defer resp.Body.Close()
+
+ body, _ := ioutil.ReadAll(resp.Body)
+
+ err := json.Unmarshal(body, &finger)
+
+ CheckError(err, "error getting fingerrequet resp from json body")
+ }
+
+ if(len(finger.Links) > 0) {
+ for _, e := range finger.Links {
+ if(e.Type == "application/activity+json"){
+ req, err := http.NewRequest("GET", e.Href, nil)
+
+ CheckError(err, "could not get finger request from id req")
+
+ req.Header.Set("Accept", activitystreams)
+
+ resp, err := http.DefaultClient.Do(req)
+ return resp
+ }
+ }
+ }
+
+ return resp
+}
+
+func GetActorInstance(path string) (string, string) {
+ re := regexp.MustCompile(`([@]?([\w\d.-_]+)[@](.+))`)
+ atFormat := re.MatchString(path)
+
+ if(atFormat) {
+ match := re.FindStringSubmatch(path)
+ if(len(match) > 1) {
+ return match[1], match[2]
+ }
+ }
+
+ re = regexp.MustCompile(`(http:\\|https:\\)?(www)?([\w\d-_.:]+)\/([\w\d-_.]+)`)
+ httpFormat := re.MatchString(path)
+
+ if(httpFormat) {
+ match := re.FindStringSubmatch(path)
+ if(len(match) > 3) {
+ return match[4], match[3]
+ }
+ }
+
+ return "", ""
+}
diff --git a/static/bottom.html b/static/bottom.html
index c058b14..8774419 100644
--- a/static/bottom.html
+++ b/static/bottom.html
@@ -29,7 +29,7 @@
<input id="report-submit" type="submit" value="Report" style="float: right;">
<input type="hidden" id="report-inReplyTo-box" name="id" value="{{ .Board.InReplyTo }}">
<input type="hidden" id="sendTo" name="sendTo" value="{{ .Board.To }}">
- <input type="hidden" id="boardName" name="boardName" value="{{ .Board.Name }}">
+ <input type="hidden" id="boardName" name="board" value="{{ .Board.Name }}">
<input type="hidden" name="close" value="0">
<input type="hidden" id="captchaCode" name="captchaCode" value="{{ .Board.CaptchaCode }}">
<div style="width: 202px; margin: 0 auto; padding-top: 12px;">
diff --git a/static/manage.html b/static/manage.html
index 9578f8a..4fb417f 100644
--- a/static/manage.html
+++ b/static/manage.html
@@ -50,7 +50,7 @@
<ul style="display: inline-block; padding: 0; margin: 0; list-style-type: none;">
{{ $domain := .Domain }}
{{ range .Reported }}
- <li><a id="rpost" post="{{ .ID }}" href=""></a> - <b>{{ .Count }}</b> <a href="/delete?id={{ .ID }}&board={{ $board.Name }}">[Remove Post]</a> <a href="/deleteattach?id={{ .ID }}">[Remove Attachment]</a> <a href="/report?id={{ .ID }}&close=1&board={{ $board.Name }}">[Close]</a></li>
+ <li><a id="rpost" post="{{ .ID }}" href=""></a> - <b>{{ .Count }}</b> <a href="/delete?id={{ .ID }}&board={{ $board.Name }}&manage=t">[Remove Post]</a> <a href="/deleteattach?id={{ .ID }}&board={{ $board.Name }}&manage=t">[Remove Attachment]</a> <a href="/report?id={{ .ID }}&close=1&board={{ $board.Name }}">[Close]</a></li>
{{ end }}
</ul>
</div>
diff --git a/static/npost.html b/static/npost.html
index b91b795..e3c4cec 100644
--- a/static/npost.html
+++ b/static/npost.html
@@ -14,6 +14,8 @@
</ul>
<hr>
+{{ $board.Restricted }}
+
{{ template "posts" . }}
<hr>
diff --git a/static/posts.html b/static/posts.html
index 7806204..aaf10c9 100644
--- a/static/posts.html
+++ b/static/posts.html
@@ -9,11 +9,11 @@
<div style="overflow: auto;">
<div id="{{ .Id }}" style="overflow: visible; margin-bottom: 12px;">
{{ if eq $board.ModCred $board.Domain $board.Actor.Id }}
- <a href="/delete?id={{ .Id }}">[Delete Post]</a>
+ <a href="/delete?id={{ .Id }}&board={{ $board.Actor.Name }}">[Delete Post]</a>
{{ end }}
{{ if .Attachment }}
{{ if eq $board.ModCred $board.Domain $board.Actor.Id }}
- <a href="/deleteattach?id={{ .Id }}">[Delete Attachment]</a>
+ <a href="/deleteattach?id={{ .Id }}&board={{ $board.Actor.Name }}">[Delete Attachment]</a>
{{ end }}
<span style="display: block;">File: <a id="{{ .Id }}-img" href="{{ (index .Attachment 0).Href}}">{{ (index .Attachment 0).Name }}</a><span id="{{ .Id }}-size">({{ (index .Attachment 0).Size }})</span></span>
<div id="media-{{ .Id }}"></div>
@@ -75,11 +75,11 @@
<div style="float: left; display: block; margin-right: 5px;">>></div>
<div class="post" style="overflow: auto; padding: 5px; margin-bottom: 2px;">
{{ if eq $board.ModCred $board.Domain $board.Actor.Id }}
- <a href="/delete?id={{ .Id }}">[Delete Post]</a>
+ <a href="/delete?id={{ .Id }}&board={{ $board.Actor.Name }}">[Delete Post]</a>
{{ end }}
{{ if .Attachment }}
{{ if eq $board.ModCred $board.Domain $board.Actor.Id }}
- <a href="/deleteattach?id={{ .Id }}">[Delete Attachment]</a>
+ <a href="/deleteattach?id={{ .Id }}&board={{ $board.Actor.Id }}">[Delete Attachment]</a>
{{ end }}
<span style="display: block;">File <a id="{{ .Id }}-img" href="{{ (index .Attachment 0).Href}}">{{ (index .Attachment 0).Name }}</a> <span id="{{ .Id }}-size">({{ (index .Attachment 0).Size }})</span></span>
<div id="media-{{ .Id }}"></div>
diff --git a/verification.go b/verification.go
index 8dc5a6b..99f5fb7 100644
--- a/verification.go
+++ b/verification.go
@@ -8,6 +8,10 @@ import "time"
import "os/exec"
import "os"
import "math/rand"
+import "crypto/rsa"
+import "crypto/x509"
+import "encoding/pem"
+import crand "crypto/rand"
type Verify struct {
Type string
@@ -471,4 +475,68 @@ func Captcha() string {
return newID
}
+func CreatePem(db *sql.DB, actor Actor) {
+ privatekey, err := rsa.GenerateKey(crand.Reader, 2048)
+ CheckError(err, "error creating private pem key")
+ privateKeyBytes := x509.MarshalPKCS1PrivateKey(privatekey)
+
+ privateKeyBlock := &pem.Block{
+ Type: "RSA PRIVATE KEY",
+ Bytes: privateKeyBytes,
+ }
+
+ privatePem, err := os.Create("./pem/board/" + actor.Name + "-private.pem")
+ CheckError(err, "error creating private pem file for " + actor.Name)
+
+ err = pem.Encode(privatePem, privateKeyBlock)
+ CheckError(err, "error encoding private pem")
+
+ publickey := &privatekey.PublicKey
+ publicKeyBytes, err := x509.MarshalPKIXPublicKey(publickey)
+ CheckError(err, "error Marshaling public key to X509")
+
+ publicKeyBlock := &pem.Block{
+ Type: "PUBLIC KEY",
+ Bytes: publicKeyBytes,
+ }
+
+ publicPem, err := os.Create("./pem/board/" + actor.Name + "-public.pem")
+ CheckError(err, "error creating public pem file for " + actor.Name)
+
+ err = pem.Encode(publicPem, publicKeyBlock)
+ CheckError(err, "error encoding public pem")
+
+ _, err = os.Stat("./pem/board/" + actor.Name + "-public.pem")
+ if os.IsNotExist(err) {
+ CheckError(err, "public pem file for actor does not exist")
+ } else {
+ StorePemToDB(db, actor)
+ }
+}
+
+func StorePemToDB(db *sql.DB, actor Actor) {
+ query := "select publicKeyPem from actor where id=$1"
+ rows, err := db.Query(query, actor.Id)
+
+ CheckError(err, "error selecting publicKeyPem id from actor")
+
+ var result string
+ defer rows.Close()
+ rows.Next()
+ rows.Scan(&result)
+
+ if(result != "") {
+ return
+ }
+
+ publicKeyPem := actor.Id + "#main-key"
+ query = "update actor set publicKeyPem=$1 where id=$2"
+ _, err = db.Exec(query, publicKeyPem, actor.Id)
+ CheckError(err, "error updating publicKeyPem id to actor")
+
+ file := "./pem/board/" + actor.Name + "-public.pem"
+ query = "insert into publicKeyPem (id, owner, file) values($1, $2, $3)"
+ _, err = db.Exec(query, publicKeyPem, actor.Id, file)
+ CheckError(err, "error creating publicKeyPem for actor ")
+}
diff --git a/webfinger.go b/webfinger.go
new file mode 100644
index 0000000..004bdca
--- /dev/null
+++ b/webfinger.go
@@ -0,0 +1,12 @@
+package main
+
+type Webfinger struct {
+ Subject string `json:"subject,omitempty"`
+ Links []WebfingerLink `json:"links,omitempty"`
+}
+
+type WebfingerLink struct {
+ Rel string `json:"rel,omitempty"`
+ Type string `json:"type,omitempty"`
+ Href string `json:"href,omitempty"`
+}