diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | Database.go | 36 | ||||
-rw-r--r-- | README.md | 30 | ||||
-rw-r--r-- | activityPubStruct.go | 7 | ||||
-rw-r--r-- | client.go | 10 | ||||
-rw-r--r-- | databaseschema.psql | 9 | ||||
-rw-r--r-- | main.go | 253 | ||||
-rw-r--r-- | static/bottom.html | 2 | ||||
-rw-r--r-- | static/manage.html | 2 | ||||
-rw-r--r-- | static/npost.html | 2 | ||||
-rw-r--r-- | static/posts.html | 8 | ||||
-rw-r--r-- | verification.go | 68 | ||||
-rw-r--r-- | webfinger.go | 12 |
13 files changed, 373 insertions, 69 deletions
@@ -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 +} @@ -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"` @@ -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 ''; @@ -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"` +} |